forge3d.animation — Skeletal Animation¶
forge3d's animation system provides skeletal animation with keyframe blending, blend trees, and FABRIK inverse kinematics.
Core classes¶
Bone
dataclass
¶
골격 트리의 단일 링크.
local_matrix: 부모 좌표계에서의 (4,4) 변환 행렬 (바인드 포즈). parent_idx: 부모 본 인덱스. 루트는 None.
Skeleton
dataclass
¶
본 계층 집합 — 순방향 기구학(FK) 계산.
world_matrices ¶
(N, 4, 4) 월드 행렬 배열을 반환한다.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
local_overrides
|
dict[str, ndarray] | None
|
{bone_name: (4,4) 로컬 행렬} — AnimationClip sample() 결과. None이면 바인드 포즈 사용. |
None
|
joint_positions ¶
(N, 3) 각 본의 월드 공간 위치.
chain
classmethod
¶
단일 체인(링크 1개씩 연결) 골격 생성 헬퍼.
positions: [(3,)] 각 관절의 월드 위치 리스트
AnimationClip
dataclass
¶
키프레임 기반 애니메이션 클립.
{bone_name: (T, 10) float64}
각 행: [time, pos_x, pos_y, pos_z, quat_w, quat_x, quat_y, quat_z, scale_x, scale_y, scale_z]
AnimationPlayer
dataclass
¶
BlendTree
dataclass
¶
1D 파라미터 기반 두 클립 가중 블렌딩.
parameter in [0.0, 1.0]: 0→clip_a, 1→clip_b
IKTarget
dataclass
¶
Bases: Component
FABRIK IK의 목표 위치를 나타내는 ECS 컴포넌트.
FABRIKSolver ¶
N링크 체인 FABRIK IK.
체인은 [(3,)] 링크 위치 리스트로 표현된다. 링크 길이는 초기 체인에서 자동으로 계산된다.
solve ¶
solve(chain: list[ndarray], target: ndarray, max_iterations: int = 20, tolerance: float = 0.0001) -> list[np.ndarray]
수렴된 링크 위치 리스트를 반환한다.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chain
|
list[ndarray]
|
[(3,)] 관절 위치. chain[0]이 루트(고정), chain[-1]이 끝단. |
required |
target
|
ndarray
|
(3,) 목표 위치. |
required |
max_iterations
|
int
|
최대 반복 횟수. |
20
|
tolerance
|
float
|
끝단과 목표 간 허용 오차 (m). |
0.0001
|
Returns:
| Type | Description |
|---|---|
list[ndarray]
|
수렴된 관절 위치 리스트 (chain과 같은 길이). |
AnimationSystem ¶
Bases: System
AnimationPlayer 컴포넌트를 매 프레임 전진시키고 Transform을 동기화한다.
Usage examples¶
Define a skeleton¶
import numpy as np
from forge3d import Bone, Skeleton
# Each Bone needs a name, a local bind-pose matrix (4×4), and an optional parent index.
def _translate(x, y, z):
m = np.eye(4)
m[:3, 3] = [x, y, z]
return m
bones = [
Bone(name="root", local_matrix=np.eye(4), parent_idx=None),
Bone(name="hip", local_matrix=_translate(0,0,0.9), parent_idx=0),
Bone(name="spine", local_matrix=_translate(0,0,0.3), parent_idx=1),
Bone(name="l_shoulder", local_matrix=_translate(-0.3,0,0.2), parent_idx=2),
Bone(name="l_elbow", local_matrix=_translate(-0.28,0,0), parent_idx=3),
Bone(name="l_wrist", local_matrix=_translate(-0.25,0,0), parent_idx=4),
]
skeleton = Skeleton(bones=bones)
Load and play an animation clip¶
import numpy as np
from forge3d import AnimationClip, AnimationPlayer
# Create a clip with keyframes
clip = AnimationClip(
name="wave",
duration=1.0,
keyframes={
"l_shoulder": [(0.0, np.zeros(3), np.array([1,0,0,0])), # (time, pos, quat)
(0.5, np.zeros(3), np.array([0.707,0,0,0.707])),
(1.0, np.zeros(3), np.array([1,0,0,0]))],
}
)
player = AnimationPlayer(skeleton=skeleton)
player.play(clip, loop=True)
# Advance by one frame
player.update(dt=1/60)
pose = player.current_pose() # dict[bone_name, (pos, quat, scale)]
Blend tree (locomotion)¶
from forge3d import BlendTree
# Blend between idle and walk clips based on speed
blend = BlendTree(clip_a=idle_clip, clip_b=walk_clip)
blend.parameter = 0.6 # 0=idle, 1=walk
player = AnimationPlayer(skeleton=skeleton, blend_tree=blend)
player.update(dt=1/60)
FABRIK inverse kinematics¶
from forge3d import FABRIKSolver, IKTarget
solver = FABRIKSolver(
skeleton=skeleton,
chain=["l_shoulder", "l_elbow", "l_wrist"], # bone chain
iterations=10,
tolerance=0.001, # 1 mm
)
target = IKTarget(
position=np.array([0.4, 0.1, 1.2]), # world-space target
weight=1.0,
)
pose = player.current_pose()
solved_pose = solver.solve(pose, target)