mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
Allow explicit world axes for widescreen visibility cones
(cherry picked from commit 2c6e0a3a20)
This commit is contained in:
+12
-5
@@ -330,12 +330,13 @@ A model-list or per-child cosine rejection can use a horizontal-only envelope:
|
||||
|
||||
```toml
|
||||
[widescreen.cull.aspect_cone]
|
||||
forward_addr = "0x1F8000E8" # signed Q12 X/Z/Y halfwords
|
||||
forward_addr = "0x1F8000E8" # three signed Q12 halfwords
|
||||
forward_offsets = [0, 4, 2] # byte offsets for X/Z/Y; this title stores X/Y/Z
|
||||
object_type_offset = 12
|
||||
object_reg = 19
|
||||
x_reg = 16
|
||||
z_reg = 17
|
||||
y_reg = 18
|
||||
z_reg = 18
|
||||
y_reg = 17
|
||||
hysteresis_pixels = 24
|
||||
queue_reserve = 4
|
||||
queue_count_addrs = ["0x1F800144", "0x1F800150", "0x1F80015C"]
|
||||
@@ -352,8 +353,8 @@ expected = "0x0082202A" # signed SLT reject predicate
|
||||
cosine_threshold = 856 # required for SLT; Q10
|
||||
object_reg = 20 # optional per-site register overrides
|
||||
x_reg = 19
|
||||
z_reg = 18
|
||||
y_reg = 17
|
||||
z_reg = 17
|
||||
y_reg = 18
|
||||
queue_guard = false # this lower-level predicate appends to no fixed queue
|
||||
```
|
||||
|
||||
@@ -362,6 +363,12 @@ queue_guard = false # this lower-level predicate appends to no fixed queue
|
||||
- An `SLTI` site derives its Q10 cosine threshold from the immediate unless
|
||||
`cosine_threshold` is given. An `SLT` site requires it explicitly.
|
||||
- A vanilla keep is always preserved. Only a vanilla rejection is retested.
|
||||
- `x_reg`/`z_reg` describe the world ground plane; `y_reg` is world vertical.
|
||||
`forward_offsets` maps the corresponding X/Z/Y forward components from the
|
||||
three halfwords at `forward_addr`. It must be a permutation of `[0, 2, 4]`.
|
||||
Omitting it preserves the legacy `[0, 2, 4]` order. Match both register and
|
||||
forward mappings to the actual camera basis; the title's coordinate names
|
||||
alone are not sufficient evidence.
|
||||
- Horizontal reach follows the current client aspect. Vertical reach,
|
||||
near/far checks, type dispatch, and the game’s queue-capacity branches are
|
||||
unchanged.
|
||||
|
||||
@@ -246,6 +246,7 @@ uint32_t overlay_codegen_config_hash(const GameConfig& c) {
|
||||
h.u32(site.queue_guard ? 1u : 0u);
|
||||
}
|
||||
h.u32(c.ws_aspect_cone.forward_addr);
|
||||
for (uint32_t value : c.ws_aspect_cone.forward_offsets) h.u32(value);
|
||||
h.u32(c.ws_aspect_cone.object_type_offset);
|
||||
h.u32(c.ws_aspect_cone.object_reg);
|
||||
h.u32(c.ws_aspect_cone.x_reg);
|
||||
@@ -2785,6 +2786,18 @@ GameConfig load_game_config(const fs::path& config_path_in) {
|
||||
out[i] = (uint32_t)values[i];
|
||||
}
|
||||
};
|
||||
if (cone.contains("forward_offsets")) {
|
||||
load_cone_int_array("forward_offsets",
|
||||
ws_aspect_cone.forward_offsets);
|
||||
auto offsets = ws_aspect_cone.forward_offsets;
|
||||
std::sort(offsets.begin(), offsets.end());
|
||||
if (offsets != std::array<uint32_t, 3>{0u, 2u, 4u})
|
||||
throw std::runtime_error(fmt::format(
|
||||
"{}: [widescreen.cull.aspect_cone] "
|
||||
"forward_offsets must be a permutation of [0, 2, 4] "
|
||||
"for X, Z, Y (Y is world vertical)",
|
||||
config_path.string()));
|
||||
}
|
||||
load_cone_hex_array("queue_count_addrs",
|
||||
ws_aspect_cone.queue_count_addrs);
|
||||
load_cone_int_array("queue_capacities",
|
||||
|
||||
@@ -197,7 +197,10 @@ struct WidescreenAspectConeSite {
|
||||
|
||||
struct WidescreenAspectConeConfig {
|
||||
std::vector<WidescreenAspectConeSite> sites;
|
||||
uint32_t forward_addr = 0; // three signed Q12 halfwords: X,Z,Y
|
||||
uint32_t forward_addr = 0; // three signed Q12 halfwords
|
||||
// Byte offsets for semantic X,Z,Y. Y is world vertical; legacy configs
|
||||
// stored the forward vector in X,Z,Y order.
|
||||
std::array<uint32_t, 3> forward_offsets{0u, 2u, 4u};
|
||||
uint32_t object_type_offset = 0;
|
||||
uint32_t object_reg = 0;
|
||||
uint32_t x_reg = 0;
|
||||
|
||||
@@ -436,7 +436,7 @@ expected = "0x24420155"
|
||||
"expected must be ADDI/ADDIU rt,zero,imm",
|
||||
"parser rejects non-constant terrain-angle instructions");
|
||||
|
||||
const auto aspect_cone = write_config(root, "aspect-cone", R"toml(
|
||||
const std::string aspect_tables = R"toml(
|
||||
[widescreen.cull.aspect_cone]
|
||||
forward_addr = "0x1F8000E8"
|
||||
object_type_offset = 12
|
||||
@@ -463,7 +463,8 @@ x_reg = 19
|
||||
z_reg = 18
|
||||
y_reg = 17
|
||||
queue_guard = false
|
||||
)toml");
|
||||
)toml";
|
||||
const auto aspect_cone = write_config(root, "aspect-cone", aspect_tables);
|
||||
const auto aspect_config =
|
||||
PSXRecompV4::load_game_config(aspect_cone);
|
||||
check(aspect_config.ws_aspect_cone.sites.size() == 2 &&
|
||||
@@ -484,6 +485,8 @@ queue_guard = false
|
||||
!aspect_config.ws_aspect_cone.sites[1].queue_guard &&
|
||||
aspect_config.ws_aspect_cone.forward_addr ==
|
||||
0x1F8000E8u &&
|
||||
aspect_config.ws_aspect_cone.forward_offsets ==
|
||||
std::array<uint32_t, 3>{0u, 2u, 4u} &&
|
||||
aspect_config.ws_aspect_cone.object_reg == 19u &&
|
||||
aspect_config.ws_aspect_cone.x_reg == 16u &&
|
||||
aspect_config.ws_aspect_cone.z_reg == 17u &&
|
||||
@@ -496,6 +499,22 @@ queue_guard = false
|
||||
|
||||
const auto aspect_hash = PSXRecompV4::overlay_codegen_config_hash(
|
||||
aspect_config);
|
||||
auto axis_tables = aspect_tables;
|
||||
axis_tables.insert(axis_tables.find("forward_addr"),
|
||||
"forward_offsets = [0, 4, 2]\n");
|
||||
const auto axis_config = PSXRecompV4::load_game_config(
|
||||
write_config(root, "aspect-cone-axis-order", axis_tables));
|
||||
check(axis_config.ws_aspect_cone.forward_offsets ==
|
||||
std::array<uint32_t, 3>{0u, 4u, 2u},
|
||||
"parser maps a middle-vertical forward vector explicitly");
|
||||
check(aspect_hash != PSXRecompV4::overlay_codegen_config_hash(axis_config),
|
||||
"forward component order changes overlay cache identity");
|
||||
axis_tables.replace(axis_tables.find("[0, 4, 2]"), 9, "[0, 2, 2]");
|
||||
const auto bad_axis_config =
|
||||
write_config(root, "aspect-cone-repeated-axis", axis_tables);
|
||||
check_throws([&] { (void)PSXRecompV4::load_game_config(bad_axis_config); },
|
||||
"forward_offsets must be a permutation",
|
||||
"parser rejects a duplicated forward component");
|
||||
auto changed_aspect_config = aspect_config;
|
||||
changed_aspect_config.ws_aspect_cone.hysteresis_pixels++;
|
||||
check(aspect_hash != PSXRecompV4::overlay_codegen_config_hash(
|
||||
|
||||
@@ -375,6 +375,7 @@ void gpu_ws_set_aspect_cone(const uint32_t *addresses,
|
||||
const uint32_t *queue_guards,
|
||||
int nsites,
|
||||
uint32_t forward_addr,
|
||||
const uint32_t forward_offsets[3],
|
||||
uint32_t object_type_offset,
|
||||
uint32_t hysteresis_pixels,
|
||||
uint32_t queue_reserve,
|
||||
|
||||
@@ -8,7 +8,9 @@ extern "C" {
|
||||
|
||||
/* Test whether a camera-relative vector is inside a rectangular view cone.
|
||||
*
|
||||
* Coordinates use Tomba 2's X,Z,Y ordering. The camera forward vector is Q12.
|
||||
* Arguments use semantic X,Z,Y ordering: X/Z span the ground plane and Y is
|
||||
* world vertical. Map the title's stored components to those axes before
|
||||
* calling. The camera forward vector is Q12.
|
||||
* `threshold` is the game's Q10 cosine threshold (the SLTI immediate after its
|
||||
* normalized dot product). `extent_pixels` expands only the horizontal
|
||||
* half-angle from the vanilla 160-pixel half-width; the vertical half-angle is
|
||||
|
||||
+11
-3
@@ -1306,6 +1306,7 @@ typedef struct {
|
||||
static WsAspectConeSite ws_aspect_cone_sites[WS_EXPLICIT_CULL_SITES_MAX];
|
||||
static int ws_aspect_cone_n = 0;
|
||||
static uint32_t ws_aspect_cone_forward_addr = 0;
|
||||
static uint32_t ws_aspect_cone_forward_offsets[3] = {0u, 2u, 4u};
|
||||
static uint32_t ws_aspect_cone_object_type_offset = 0;
|
||||
static uint32_t ws_aspect_cone_hysteresis_pixels = 0;
|
||||
static uint32_t ws_aspect_cone_queue_reserve = 0;
|
||||
@@ -1334,6 +1335,7 @@ void gpu_ws_set_aspect_cone(const uint32_t *addresses,
|
||||
const uint32_t *queue_guards,
|
||||
int nsites,
|
||||
uint32_t forward_addr,
|
||||
const uint32_t forward_offsets[3],
|
||||
uint32_t object_type_offset,
|
||||
uint32_t hysteresis_pixels,
|
||||
uint32_t queue_reserve,
|
||||
@@ -1362,6 +1364,9 @@ void gpu_ws_set_aspect_cone(const uint32_t *addresses,
|
||||
(uint8_t)(!queue_guards || queue_guards[i] != 0);
|
||||
}
|
||||
ws_aspect_cone_forward_addr = forward_addr;
|
||||
for (int i = 0; i < 3; i++)
|
||||
ws_aspect_cone_forward_offsets[i] =
|
||||
forward_offsets ? forward_offsets[i] : (uint32_t)i * 2u;
|
||||
ws_aspect_cone_object_type_offset = object_type_offset;
|
||||
ws_aspect_cone_hysteresis_pixels = hysteresis_pixels;
|
||||
ws_aspect_cone_queue_reserve = queue_reserve;
|
||||
@@ -1500,11 +1505,14 @@ uint32_t psx_ws_aspect_cone_result(uint32_t site_address, uint32_t vanilla,
|
||||
(total_margin > ws_cull_guard_pixels)
|
||||
? total_margin - ws_cull_guard_pixels : 0;
|
||||
const int32_t fx =
|
||||
(int16_t)psx_read_half(ws_aspect_cone_forward_addr + 0u);
|
||||
(int16_t)psx_read_half(ws_aspect_cone_forward_addr +
|
||||
ws_aspect_cone_forward_offsets[0]);
|
||||
const int32_t fz =
|
||||
(int16_t)psx_read_half(ws_aspect_cone_forward_addr + 2u);
|
||||
(int16_t)psx_read_half(ws_aspect_cone_forward_addr +
|
||||
ws_aspect_cone_forward_offsets[1]);
|
||||
const int32_t fy =
|
||||
(int16_t)psx_read_half(ws_aspect_cone_forward_addr + 4u);
|
||||
(int16_t)psx_read_half(ws_aspect_cone_forward_addr +
|
||||
ws_aspect_cone_forward_offsets[2]);
|
||||
const uint32_t threshold = site->cosine_threshold;
|
||||
|
||||
const int in_visible =
|
||||
|
||||
@@ -18490,6 +18490,7 @@ int main(int argc, char** argv) {
|
||||
object_regs.data(), x_regs.data(), z_regs.data(),
|
||||
y_regs.data(), queue_guards.data(), (int)addresses.size(),
|
||||
gc.ws_aspect_cone.forward_addr,
|
||||
gc.ws_aspect_cone.forward_offsets.data(),
|
||||
gc.ws_aspect_cone.object_type_offset,
|
||||
gc.ws_aspect_cone.hysteresis_pixels,
|
||||
gc.ws_aspect_cone.queue_reserve,
|
||||
|
||||
@@ -43,6 +43,26 @@ int main(void) {
|
||||
1000, 0, -1000, 2896, 0, 2896, cos_q10, 160),
|
||||
"horizontal expansion does not bypass pitched vertical rejection");
|
||||
|
||||
/* Recorded Tomba 2 village camera in stored X,Y,Z order:
|
||||
* right=(3568,0,-2009), up=(-342,4037,-607), forward=(1980,696,3516).
|
||||
* Model EF788 delta=(4967,502,1916) lies at the wide right edge. Both
|
||||
* vectors must be reordered to semantic X,Z,Y before using this helper. */
|
||||
check(!psx_ws_aspect_cone_contains(
|
||||
4967, 502, 1916, 1980, 696, 3516, 856, 120),
|
||||
"legacy storage interpretation reproduces the village rejection");
|
||||
check(psx_ws_aspect_cone_contains(
|
||||
4967, 1916, 502, 1980, 3516, 696, 856, 120),
|
||||
"mapped village camera accepts its horizontal edge actor");
|
||||
check(!psx_ws_aspect_cone_contains(
|
||||
4967, 1916, 502, 1980, 3516, 696, 856, 0),
|
||||
"same actor remains outside the unexpanded cone");
|
||||
check(psx_ws_aspect_cone_contains(
|
||||
1980, 3516, 696, 1980, 3516, 696, 856, 0),
|
||||
"mapped pitched village camera accepts its forward axis");
|
||||
check(!psx_ws_aspect_cone_contains(
|
||||
1296, 2302, 8770, 1980, 3516, 696, 856, 120),
|
||||
"mapped village camera still rejects a point above its vertical cone");
|
||||
|
||||
check(!psx_ws_aspect_cone_contains(
|
||||
1000, 0, 0, q12, 0, 0, 0, 0),
|
||||
"invalid zero threshold is inert");
|
||||
|
||||
Reference in New Issue
Block a user