Skip to content

v1 → v2 Migration Guide

Explains how to upgrade v1.x code to v2.0. v1 API is fully backward-compatible in v2 — existing code runs without modification. Follow this guide only when you want to adopt v2 features.


1. Installation

# v1
pip install pyforge3d==1.1.0

# v2 (includes Rust extension)
pip install pyforge3d==2.0.0
# Or from source: maturin build --release && pip install dist/pyforge3d-2.0.0-*.whl

Rust (rustup) is not required for installation — if the Rust build fails, the Python fallback is selected automatically.


2. Version check

import forge3d
print(forge3d.__version__)  # "2.0.0"

3. Existing code — no changes needed

All v1 code runs unchanged in v2.

# v1 code — runs in v2 without modification
import forge3d as f3d

world = f3d.World(gravity=(0, 0, -9.81))
world.add_ground()
box = world.add_box(size=(1, 1, 1), position=(0, 0, 5), mass=1.0)

viewer = f3d.Viewer(world, max_frames=90)
while viewer.is_open:
    world.step(dt=1 / 60)
    viewer.draw()

4. Adopting v2 features — ECS

v1 style (still supported)

world = f3d.World()
box = world.add_box(size=(1,1,1), position=(0,0,5), mass=1.0)

v2 ECS style (opt-in)

ew = f3d.EntityWorld()
box = ew.create_entity(
    f3d.Transform(position=[0, 0, 5]),
    f3d.MeshRenderer(mesh_id="box_1x1x1", material_id="red"),
    f3d.Rigidbody(mass=1.0),
)
ew.add_system(f3d.PhysicsSystem())
ew.add_system(f3d.RenderSystem())
ew.step(1/60)

Bridging v1 Body → ECS entity

# Wrap an existing v1 Body as an ECS entity
from forge3d import body_to_entity
entity = body_to_entity(world, v1_box, ew)

5. Modern rendering pipeline (P26)

v1 — OpenGL 3.3 Blinn-Phong

viewer = f3d.Viewer(world, mode="realtime")

v2 — Deferred PBR (automatic upgrade)

# Viewer uses DeferredRenderer internally (automatic)
viewer = f3d.Viewer(world, mode="realtime")

# Direct usage
from forge3d.render import DeferredRenderer
renderer = DeferredRenderer(width=1280, height=720)
frame = renderer.render(snapshot)

6. Audio system (P28)

# New in v2
clip = f3d.AudioClip.from_sine(freq=440.0, duration=0.5)
audio_sys = f3d.AudioSystem()
audio_sys.play(clip, volume=0.8)

# Collision event → sound trigger
handler = audio_sys.make_collision_handler(clip, max_volume=1.0)
world.on_collision_begin(handler)

7. Animation + IK (P29)

# Create skeleton
skel = f3d.Skeleton.chain([
    np.array([0., 0., 0.]),
    np.array([0., 1., 0.]),
    np.array([0., 2., 0.]),
])

# FABRIK IK
solver = f3d.FABRIKSolver()
chain = [np.array([0., 0., 0.]), np.array([0., 1., 0.]), np.array([0., 2., 0.])]
solved = solver.solve(chain, target=np.array([1.5, 1.0, 0.]))

8. Particle system (P31)

ew = f3d.EntityWorld()
ew.create_entity(
    f3d.Transform(position=np.array([0., 5., 0.])),
    f3d.ParticleEmitter.preset("sparks", rate=500),
)
ps = f3d.ParticleSystem()
ew.add_system(ps)
ew.step(1/60)
print(f"{ps.total_alive} sparks alive")

9. Scene save / load (P30)

# Save ECS scene
from forge3d import save_scene, load_scene
save_scene(ew, "my_scene.json")

# Using scene manager
mgr = f3d.SceneManager(ew)
mgr.on_scene_loaded(lambda: print("Scene loaded"))
mgr.load_scene("level1.json")

10. Rust core control

# Disable Rust core (Python fallback)
USE_RUST_CORE=0 python my_game.py

# Force Rust core (error if unavailable)
USE_RUST_CORE=1 python my_game.py
from forge3d.backend import USE_RUST_CORE
print("Rust core:", USE_RUST_CORE)

11. Breaking changes

None. The v1 API is frozen.


12. Deprecated

None. v1 API is guaranteed until v3.