Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/hy_worldplay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,9 @@ the WBench metrics pipeline.
- **Defaults** (`navigation_to_poses.py`): 24 FPS, temporal compression 4 (6
latents/sec), forward speed `0.08`, yaw/pitch `3°` per step. Adjust to match
your weights/config.
- **Third-person direction convention.** `yaw > 0` (`right`) moves the camera
from behind the character toward the character's `+X`/right side; `yaw < 0`
(`left`) moves it toward `-X`/left. The adapter uses side-relative wording
because clockwise/counterclockwise depends on how a top-down view is drawn.
- These scripts target HunyuanVideo-1.5 at 480p. See the official repo for
resolution/model-type (`ar`/`bi`) options.
9 changes: 7 additions & 2 deletions examples/hy_worldplay/navigation_to_poses.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,15 @@ def generate_orbit_trajectory(motions, radius=ORBIT_RADIUS, height=ORBIT_HEIGHT)
always looking at the character position.

Each motion dict may contain:
- "yaw": azimuth delta (radians, positive = orbit right)
- "yaw": side intent (radians, positive = orbit toward subject right)
- "pitch": elevation delta (radians, positive = orbit up)
- "forward": character moves forward; camera + character translate together
- "right": character strafes right; camera + character translate together

The initial camera is behind the character at -Z. Positive/right yaw moves
it toward the character's +X/right side, while negative/left yaw moves it
toward -X/left. Avoid describing these as clockwise/counterclockwise: that
label changes with the top-down viewing and plotting convention.
"""
poses = []
azimuth = np.pi # start behind the character (+Z forward, camera at -Z)
Expand Down Expand Up @@ -108,7 +113,7 @@ def _cam_pose():

for move in motions:
# Orbit rotation (camera moves, character stays)
# yaw<0 = "turn left" = character faces left = camera orbits clockwise (azimuth increases)
# Positive/right yaw moves the initially rear camera toward +X.
if "yaw" in move:
azimuth -= move["yaw"]
if "pitch" in move:
Expand Down
6 changes: 6 additions & 0 deletions src/models/camera/poses.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ def generate_orbit_trajectory(
character (and the camera with it). The character's heading is fixed, so
forward is along world +Z — turning only re-frames the character, it does
not steer the walk direction.

The initial camera is behind the character at -Z. A positive yaw (the
WBench "right" action) moves it toward the character's +X/right side; a
negative yaw moves it toward -X/left. This side-based definition avoids
the viewpoint-dependent ambiguity of clockwise/counterclockwise.
"""
poses = []
azimuth = np.pi # start behind the character (+Z forward, camera at -Z)
Expand Down Expand Up @@ -176,6 +181,7 @@ def _cam_pose():

for move in motions:
if "yaw" in move:
# Positive/right yaw moves the initially rear camera toward +X.
azimuth -= move["yaw"]
if "pitch" in move:
elevation = np.clip(elevation - move["pitch"], np.deg2rad(-60), np.deg2rad(60))
Expand Down
6 changes: 6 additions & 0 deletions src/models/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
move = [forward, right] forward > 0 = ahead, right > 0 = strafe right
yaw > 0 = turn right, < 0 = turn left
pitch > 0 = look up, < 0 = look down

For third-person orbit adapters, left/right name the side of the subject that
the camera moves toward. The reference trajectory starts behind a subject
facing world +Z, so yaw > 0 moves the camera toward the subject's +X/right
side. Clockwise/counterclockwise is intentionally avoided because it depends
on the top-down viewing and plotting convention.
"""
from typing import Dict, List, Optional, Any

Expand Down
8 changes: 4 additions & 4 deletions src/models/text/prompt_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@
"S": "The subject moves backward. The camera follows, maintaining distance and framing.",
"A": "The subject moves to the left. The camera tracks left, keeping the subject centered.",
"D": "The subject moves to the right. The camera tracks right, keeping the subject centered.",
"left": "The camera orbits counterclockwise around the subject. The subject stays still.",
"right": "The camera orbits clockwise around the subject. The subject stays still.",
"left": "The camera moves along an arc toward the subject's left side, keeping the subject centered. The subject stays still.",
"right": "The camera moves along an arc toward the subject's right side, keeping the subject centered. The subject stays still.",
"up": "The camera cranes upward while tilting down to keep the subject centered.",
"down": "The camera cranes downward while tilting up to keep the subject centered.",
"W+A": "The subject moves diagonally forward-left. The camera follows along the same diagonal.",
"W+D": "The subject moves diagonally forward-right. The camera follows along the same diagonal.",
"S+D": "The subject moves backward-right. The camera retreats along the same diagonal.",
"W+left": "The subject moves forward. The camera arcs counterclockwise while following.",
"W+right": "The subject moves forward. The camera arcs clockwise while following.",
"W+left": "The subject moves forward. The camera follows on an arc toward the subject's left side.",
"W+right": "The subject moves forward. The camera follows on an arc toward the subject's right side.",
"W+up": "The subject moves forward. The camera cranes upward while following.",
"W+down": "The subject moves forward. The camera cranes downward while following.",
}
Expand Down
56 changes: 56 additions & 0 deletions tests/test_navigation_conventions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import unittest

import numpy as np

from examples.hy_worldplay.navigation_to_poses import (
generate_orbit_trajectory as generate_hy_orbit_trajectory,
)
from src.models.camera.poses import generate_orbit_trajectory
from src.models.navigation import action_to_navigation
from src.models.text.prompt_builder import build_turn_prompt


class ThirdPersonDirectionConventionTest(unittest.TestCase):
def test_actions_map_left_and_right_to_opposite_yaw_signs(self):
self.assertEqual(action_to_navigation("right")["yaw"], 1)
self.assertEqual(action_to_navigation("left")["yaw"], -1)

def test_reference_orbit_moves_to_the_named_subject_side(self):
right_pose = generate_orbit_trajectory([{"yaw": 0.1}])[-1]
left_pose = generate_orbit_trajectory([{"yaw": -0.1}])[-1]

self.assertGreater(right_pose[0, 3], 0.0)
self.assertLess(left_pose[0, 3], 0.0)
np.testing.assert_allclose(
right_pose[[0, 2], 3],
[-left_pose[0, 3], left_pose[2, 3]],
)

def test_hy_worldplay_adapter_uses_the_same_orbit_convention(self):
right_pose = generate_hy_orbit_trajectory([{"yaw": 0.1}])[-1]
left_pose = generate_hy_orbit_trajectory([{"yaw": -0.1}])[-1]

self.assertGreater(right_pose[0, 3], 0.0)
self.assertLess(left_pose[0, 3], 0.0)

def test_third_person_prompts_use_side_relative_wording(self):
case = {}
right_prompt = build_turn_prompt(
case,
{"type": "navigation", "action": "right"},
perspective="third_person",
)
left_prompt = build_turn_prompt(
case,
{"type": "navigation", "action": "left"},
perspective="third_person",
)

self.assertIn("subject's right side", right_prompt)
self.assertIn("subject's left side", left_prompt)
self.assertNotIn("clockwise", right_prompt.lower())
self.assertNotIn("clockwise", left_prompt.lower())


if __name__ == "__main__":
unittest.main()