Skip to content

forge3d.editor — In-Engine Editor

forge3d ships an in-engine scene editor with play/pause/step controls, entity selection, transform gizmos, and a hierarchy/inspector panel.


EditorApp

EditorApp

씬 에디터 애플리케이션.

v1 World + ECS EntityWorld를 함께 관리한다. ImGui 없는 환경에서는 상태 머신과 레이캐스트 로직만 동작한다.

play_state property

play_state: PlayState

is_playing property

is_playing: bool

is_paused property

is_paused: bool

is_editing property

is_editing: bool

__init__

__init__(world: World, entity_world: EntityWorld, config: LayoutConfig | None = None, dt: float = 1 / 60) -> None

play

play() -> None

에디터 모드 → 플레이 모드 (물리 활성화).

pause

pause() -> None

플레이 → 일시정지.

stop

stop() -> None

플레이/일시정지 → 에디터 모드.

step_once

step_once() -> None

단일 물리 스텝 실행 (일시정지 또는 에디터 모드에서 사용).

update

update() -> None

한 프레임을 처리한다 (물리 스텝 + UI 업데이트).

pick_entity

pick_entity(screen_x: float, screen_y: float, fov_deg: float = 45.0, view_matrix: ndarray | None = None) -> int | None

화면 좌표 (screen_x, screen_y)에서 레이캐스트로 엔티티를 선택한다.

move_selected

move_selected(axis: int, delta: float) -> None

선택된 엔티티를 지정 축으로 delta 만큼 이동한다 (0=X, 1=Y, 2=Z).

save_scene

save_scene(path: str | None = None) -> None

현재 ECS 씬을 JSON으로 저장한다.

on_scene_saved

on_scene_saved(callback: Callable) -> None

set_scene_path

set_scene_path(path: str) -> None

run_headless

run_headless(n_frames: int = 1) -> None

GUI 없이 n_frames 동안 update()를 실행한다.

run

run(max_frames: int = 0) -> None

에디터를 실행한다. ImGui 가용 시 실제 창, 없으면 1프레임 headless.


Gizmos

TranslateGizmo

선택 엔티티의 위치를 축별로 이동시키는 기즈모.

pick

pick(ray_origin: ndarray, ray_dir: ndarray, ew: EntityWorld, max_dist: float = 100.0) -> Entity | None

화면 레이로 가장 가까운 엔티티를 선택한다.

각 엔티티의 AABB 구(半경 1m 기본)에 레이-구 교차 테스트.

start_drag

start_drag(axis: int) -> None

축 드래그 시작 (0=X, 1=Y, 2=Z).

drag

drag(delta: float, ew: EntityWorld) -> None

delta 거리만큼 선택 축을 따라 엔티티를 이동한다.

GizmoMode

Bases: Enum

screen_to_ray

screen_to_ray(screen_x: float, screen_y: float, width: int, height: int, fov_deg: float, view_matrix: ndarray) -> tuple[np.ndarray, np.ndarray]

화면 좌표 → 월드 공간 레이 (origin, direction).

view_matrix: (4,4) 카메라 뷰 행렬.


Usage examples

Launch the editor

import forge3d as f3d

world = f3d.World()
ew = f3d.EntityWorld()

editor = f3d.EditorApp(world, ew, title="My Scene")

# Populate the scene
ew.create_entity(
    f3d.Transform(position=[0, 0, 2]),
    f3d.MeshRenderer(shape="box", size=(1, 1, 1)),
)

editor.run()   # opens an OS window with the editor UI

Headless testing (CI-safe)

editor = f3d.EditorApp(world, ew)
editor.run_headless(n_frames=10)   # run 10 update cycles without a window

Play/pause/step controls

editor.play()            # start simulation
editor.pause()           # freeze time
editor.step_once()       # advance exactly one physics tick while paused
editor.stop()            # revert to pre-play snapshot

print(editor.play_state)   # PlayState.PLAYING | PAUSED | EDITING

Transform gizmo

from forge3d.editor import TranslateGizmo
import numpy as np

gizmo = TranslateGizmo()

# Ray from camera
origin    = np.array([0., 0., 10.])
direction = np.array([0., 0., -1.])

hit_entity = editor.pick_entity(
    ew,
    view_matrix=np.eye(4),
    ray_origin=origin,
    ray_dir=direction,
)
if hit_entity is not None:
    editor.move_selected(axis=0, delta=0.1)   # move +X by 0.1 m

Save scene

editor.set_scene_path("scenes/level1.json")
editor.save_scene()                  # save to configured path
editor.save_scene("scenes/bak.json") # override path