fix(world): match retail portal passage and exit warp
This commit is contained in:
parent
eab23cbdd1
commit
842bd89c16
11 changed files with 355 additions and 28 deletions
|
|
@ -16,6 +16,7 @@ Named Sept-2013 retail symbols:
|
|||
- `gmSmartBoxUI::UseTime` `0x004D6E30`
|
||||
- `CreatureMode::CreatureMode` `0x00454390`
|
||||
- `CreatureMode::SetCameraDirection_Degrees` `0x00453760`
|
||||
- `Frame::rotate` `0x004525B0`
|
||||
- `CreatureMode::AddObject` `0x004556D0`
|
||||
- `UIGlobals::Init` `0x004EE470`
|
||||
- `UIGlobals::GetAnimLevel` `0x004EE540`
|
||||
|
|
@ -24,6 +25,9 @@ Named Sept-2013 retail symbols:
|
|||
- `SmartBox::PlayerPositionUpdated` `0x00453870`
|
||||
- `CPhysicsObj::teleport_hook` `0x00514ED0`
|
||||
- `CommandInterpreter::PlayerTeleported` `0x006B32B0`
|
||||
- `SmartBox::SetOverrideFovDistance` `0x00451BC0`
|
||||
- `SmartBox::GetOverrideFovDistance` `0x00451BE0`
|
||||
- `Render::set_vdst` `0x0054B240`
|
||||
|
||||
Constants were checked against static x86 disassembly. The Binary Ninja
|
||||
pseudocode loses several x87 operands in `UseTime`; disassembly is authoritative
|
||||
|
|
@ -93,7 +97,12 @@ sequence animations, plays `Sound_UI_ExitPortal`, and enters `WorldFadeIn`.
|
|||
|
||||
## Camera motion
|
||||
|
||||
The camera rotates around AC's vertical `+Z` axis. Each segment is independent:
|
||||
The camera rolls around its own AC `+Y` forward axis. It does not yaw around
|
||||
world `+Z`. `SetCameraDirection_Degrees` resets the frame to identity, converts
|
||||
the vector to radians, then passes `(0, angle, 0)` to `Frame::rotate`, whose
|
||||
input is an axis-angle rotation vector. The forward direction therefore stays
|
||||
down the portal passage while local `+Z` up rolls around it. Each segment is
|
||||
independent:
|
||||
|
||||
```text
|
||||
if now >= rotationStart + rotationDuration:
|
||||
|
|
@ -184,6 +193,39 @@ World/tunnel fades use `GetAnimLevel(elapsed / FadeTime)`. The fade belongs
|
|||
between the 3-D viewport and the retained UI. It must never cover or disable
|
||||
the retained UI.
|
||||
|
||||
## View-plane warp
|
||||
|
||||
Retail couples every fade state to a projection transition. On begin it
|
||||
captures the current SmartBox view-plane distance; for a perspective
|
||||
projection this is `cot(verticalFov / 2)`. The transition endpoint is the
|
||||
named constant `TRANSITION_VIEW_PLANE_DISTANCE = 0.001` at `0x007BD260`.
|
||||
|
||||
```text
|
||||
fadeLevel = GetAnimLevel(elapsed / FadeTime) / 1024
|
||||
|
||||
WorldFadeOut or TunnelFadeOut:
|
||||
currentDistance = gameDistance
|
||||
+ (0.001 - gameDistance) * fadeLevel
|
||||
|
||||
TunnelFadeIn or WorldFadeIn:
|
||||
currentDistance = 0.001
|
||||
+ (gameDistance - 0.001) * fadeLevel
|
||||
|
||||
SmartBox.SetOverrideFovDistance(true, currentDistance)
|
||||
```
|
||||
|
||||
`Render::set_vdst` turns the distance back into projection values:
|
||||
|
||||
```text
|
||||
verticalFov = 2 * atan(1 / currentDistance)
|
||||
znear = max(0.1, currentDistance * 0.25)
|
||||
```
|
||||
|
||||
At `0.001`, the view is nearly 180 degrees wide. Black covers the singular
|
||||
endpoint; as `WorldFadeIn` clears, the FOV eases back to the captured game
|
||||
projection. This is retail's characteristic destination-world warp, separate
|
||||
from both the alpha fade and chase-camera movement.
|
||||
|
||||
## Player placement boundary
|
||||
|
||||
```text
|
||||
|
|
@ -195,7 +237,7 @@ PlayerPositionUpdated(isTeleport=true):
|
|||
clear position-update/teleport bookkeeping
|
||||
player.teleport_hook()
|
||||
commandInterpreter.PlayerTeleported()
|
||||
move world viewer to player
|
||||
SmartBox.set_viewer(player.position, reset_sought=true)
|
||||
update CellManager position
|
||||
|
||||
player.teleport_hook():
|
||||
|
|
@ -212,6 +254,11 @@ PlayerTeleported():
|
|||
SendMovementEvent()
|
||||
```
|
||||
|
||||
The viewer reset copies the player's complete position into both `viewer` and
|
||||
`viewer_sought_position`. Subsequent ordinary camera updates extend the chase
|
||||
boom outward through the same damped and swept path used after camera
|
||||
collision. Reusing the source-world viewer is not retail behavior.
|
||||
|
||||
The retail teleport hook does not clear the character's animation sequence.
|
||||
The arrival presentation is instead hidden by the portal scene and its final
|
||||
world fade while the normal motion/update stream settles. Therefore acdream
|
||||
|
|
@ -227,6 +274,11 @@ must not add an arrival-only animation reset.
|
|||
- During tunnel states the portal scene replaces world pixels before retained
|
||||
UI draws. The black fade also draws before retained UI. Input dispatch and
|
||||
`RetailUiRuntime.Tick` continue unchanged.
|
||||
- The projection owner captures the active camera's M22 view-plane distance at
|
||||
teleport begin and applies `Render::set_vdst` semantics to both the tunnel
|
||||
and world viewport during the four fade states.
|
||||
- Destination placement resets the retail chase camera's published and sought
|
||||
positions to the player before the normal update path resumes.
|
||||
- Destination residency remains acdream's asynchronous adaptation. It supplies
|
||||
the same `EndTeleportAnimation` edge retail receives when its blocking cell
|
||||
load completes; it does not alter presentation ordering.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue