Changelog¶
All notable changes to forge3d are documented here. Format follows Keep a Changelog, versioning follows Semantic Versioning.
Unreleased¶
2.2.1 — 2026-06-15 — Packaging fix¶
Fixed¶
- PyPI packaging: missing rendering dependencies —
pip install pyforge3dno longer silently omitsimageio,glfw,moderngland other rendering packages. The[project.dependencies]inpyproject.tomlare now correctly reflected in the published distribution metadata.
2.2.0 — 2026-06-13 — Game-dev ergonomics & API clarity¶
Added¶
Input.axis(neg_key, pos_key) → float— reads two keys as a[-1, 1]float axis; replaces thefloat(inp.key_held(D)) - float(inp.key_held(A))idiom. Also added toScriptedInput.OrbitCamera.handle_input(inp, dt, ...)— handles mouse drag, Q/E rotation, and scroll zoom insideOrbitCameraitself; no manual event parsing needed in game loops.CharacterController.move_camera_relative(inp, cam, speed, dt)— moves the character in camera-relative direction without manually computing yaw.Viewer.show_grid: boolproperty (also a constructor parameter) — replaces internalviewer._renderer._show_gridaccess.Viewer.draw_textanchor-based layout —x=None/y=Noneauto-position byanchor;marginparameter added. New anchors:"topcenter","bottomleft","bottomcenter","bottomright".App.__init__render parameters —substeps,shadow_resolution,sky_color,show_grid,max_dtcan now be set at construction.
Fixed¶
Sentryhover drift (apps/game/enemies.py) — hover raycast missinglayer_maskcaused sentries to hit their own body. Fixed withlayer_mask=CollisionLayer.DEFAULT | CollisionLayer.TERRAIN.
2.1.1 — 2026-06-11¶
Changed¶
- Package description and all in-code taglines updated to "own dynamics, own rules, no compromises."
2.1.0 — 2026-06-06 — API completeness and polish¶
Bug fixes, missing features, and consistency improvements discovered during development.
Fixed¶
- B-1
add_terrain(material=Material(...))custom material lost in snapshot —TerrainSnapshot.material/BodySnapshot.materialfields now include the resolvedMaterialobject directly - B-2
Body.namewas read-only —namesetter added - B-3
World.restore(path)instance method added for in-place restore; existing classmethodWorld.load()retained - Deferred renderer
Material.emissivescalar →(R,G,B)conversion TypeError fixed
Added¶
Body.shape_type,Body.shape_params,Body.rotation_matrixpropertiesBody.on_collision_begin(cb)/on_collision_end(cb)per-body collision callbacksMaterial.emissive: float— propagated through the full snapshot pipelineTransform.quaternion[w,x,y,z]/Transform.matrix4(4,4)propertiesTriggerZone.set_position(),.set_half_extents(),.enabledruntime manipulationworld.raycast_all(origin, dir, max_dist, layer_mask)— full hit listworld.overlap_sphere(center, radius)/world.overlap_box(center, half_extents)spatial queriesworld.add_terrain(..., friction=0.8, layer=CollisionLayer.TERRAIN)parametersworld.step(dt, substeps=1)substep supportworld.update(frame_dt)fixed-timestep accumulator (world.fixed_dt,world.max_substeps)world.add_character(position, height, radius)→CharacterController(move/jump/glide)world.profiler→PhysicsProfiler— context manager for step timingJointTypeStrEnum (HINGE,BALL,PRISMATIC,SPRING,FIXED,DISTANCE)CollisionLayer.mask_for(*layers)bitmask helperViewer(shadow_resolution=N)— configurable shadow resolution; default raised 512 → 1024
Changed¶
CollisionLayer.TERRAIN = 0x0008(swapped with formerBULLETvalue;BULLET = 0x0020)
2.0.0 — 2026-06-04 — v2.0: Python-first high-performance game/simulation engine¶
v2 advances v1 on three axes:
- Runtime performance — Rust native extension (PyO3 + maturin), ≥10× physics hot-loop speedup
- Graphics pipeline — Deferred PBR + CSM + SSAO + HDR + Bloom (OpenGL 4.3+)
- API ecosystem — ECS, audio, animation, scene management, particles, UI, editor
Full backward compatibility with v1 API: World, Body, Viewer, Recorder signatures frozen.
Added — P25: Rust native extension¶
forge3d._core— PyO3 + maturin mixed build (Rust 1.96+)forge3d._core.gjk_query(verts_a, verts_b)— GJK + EPA collision detectionforge3d._core.bvh_build/bvh_query_pairs— BVH broadphase (22× improvement at N=500)forge3d._core.pgs_solve— PGS contact solver Rust pathforge3d._core.se3_mul/quat_normalize/quat_mul— SIMD math (glam-based)USE_RUST_CORE=0/1env var to force Python fallback
Added — P26: Modern rendering pipeline¶
DeferredRenderer— OpenGL 4.3 deferred rendering- G-Buffer 4 channels (position/normal/albedo-roughness/emissive-metallic)
- CSM (Cascaded Shadow Maps) 4 cascades, PCF 9-tap
- SSAO 64 samples + blur pass
- HDR framebuffer + ACES tonemap + Kawase bloom
- Instanced rendering
RenderPassABC — per-pass pipeline abstractionforge3d.render.DeferredRendererpublicly exported
Added — P27: Entity Component System¶
EntityWorld— entity create/destroy, component CRUD,query()- Built-in components:
Transform,Rigidbody,Collider,MeshRenderer,Script,CameraComponent,LightComponent SystemABC +PhysicsSystem,RenderSystem,ScriptSystembody_to_entity()— v1 Body → ECS bridgesave_scene()/load_scene()— ECS scene JSON serializationEntityNotFoundError— clear error on destroyed-entity access
Added — P28: Audio system¶
AudioClip— WAV/OGG load +from_sine()factoryAudioSource,AudioListener— ECS componentsAudioSystem— OpenAL auto-detect,NullDriverfallback in headlessAudioSystem.make_collision_handler()— collision event → sound trigger factory
Added — P29: Animation system¶
Skeleton,Bone— skeleton hierarchy, FK world matricesAnimationClip— keyframe LERP/SLERP interpolationAnimationPlayer,BlendTree— ECS componentsFABRIKSolver— N-link FABRIK IK (convergence < 1e-4 m)AnimationSystem— ECS systemchain_from_ur5_joints()— UR5 FK chain helper
Added — P30: Scene management¶
SceneNode— dirty-flag cache + parent/child hierarchyPrefab— JSON save/load/instantiateSceneManager— load/unload/additive scene transitions + callbacks
Added — P31: Particle system¶
ParticleEmitter— ECS component (rate/lifetime/gravity/restitution)ParticleSystem— NumPy vectorized + JAX vmap dual path (100k particles < 33 ms)- VFX presets:
sparks,smoke,debris,rain
Added — P32: UI system¶
DebugPanel,InspectorPanel,HierarchyPanel— ImGui panels (null fallback)Canvas— 2D overlay (clipping, NumPy rasterizer)UISystem— ECS system
Added — P33: Scene editor¶
EditorApp— Play/Pause/Step state machineTranslateGizmo— ray-sphere intersection selection + axis dragEditorLayout— 3-panel layoutscreen_to_ray()— screen coords → world ray
Added — P34 (optional): wgpu backend¶
WgpuRenderer— wgpu-py off-screen renderer, WGSL PBR shaders- Automatic fallback to
DeferredRendererwhen wgpu is not available
1.1.0 — 2026-06-03 — Game-Ready Release¶
Added¶
Physics API extensions
World.add_box(static=True)— create static box via public APIWorld.add_capsule(static=True)— static capsule supportBody.friction/Body.restitutionsetters — change at runtimeBody.linear_damping/Body.angular_damping— velocity decay coefficientsWorld.weld(body, anchor, local_rotation=...)— preserve child body relative rotationWorld.raycast(origin, direction, max_dist)→RayHit(body, point, normal, distance)
Rendering — Heightfield terrain visualization
TerrainSnapshotdataclass (forge3d.TerrainSnapshot)- Automatically included in
SceneSnapshot.terrainswhenWorld.add_terrain()is called RealtimeRenderer— heightfield triangle mesh generation and rendering (shadow pass included)render.realtime.meshes.heightfield_mesh(heights, cell_size, origin)public function
Camera
FollowCamera(frame="local")— vehicle body-local frame offsetFollowCamera(smoothing_hz=6.0)— FPS-independent exponential-decay smoothingFollowCamera.to_snapshot(dt=...)— dt parameter for FPS-independent smoothing
Input
forge3d.InputBuilder— promoted to public class, auto-wired with glfw callbacks
Viewer
Viewer.draw_text(text, x, y, size, color, bg_alpha, anchor)— HUD text overlay
Serialization
World.save()— includes joint serialization (Hinge/Spring/Distance/Ball/Fixed/Prismatic)World.load()— automatically restores saved joints
Performance¶
- Removed double
detect_contactscall inWorld.step(): ~2× event dispatch speed improvement
Fixed¶
World.add_terrain()materialparameter was not forwarded to heightfield rendering
1.0.0 — 2026-06-03 — Stable Release¶
Added (P15 — MkDocs documentation site)¶
- MkDocs Material theme documentation site (
mkdocs.yml,docs_src/) - API reference, 4 tutorials, architecture overview page
Added (P16 — Joint & constraint system)¶
forge3d.constraintspackage: Sequential Impulse constraint solverFixedJoint,BallJoint,HingeJoint(motor + limits),PrismaticJoint,DistanceJoint,SpringJointWorld.add_joint(type, body_a, body_b, ...)unified API;World.remove_joint(handle)forge3d.JointHandlehandle class
Added (P17 — Collision event callbacks)¶
forge3d.CollisionEvent,forge3d.CollisionHandlerWorld.on_collision_begin,on_collision_stay,on_collision_enddecoratorsWorld.add_collision_handler(body_a, body_b)pair-wise handlerWorld.add_trigger_zone(position, size)pure-data zone (no physics collision)
Added (P18 — Scene serialization)¶
World.save(path)→ JSON saveWorld.load(path)classmethod → World restoreforge3d.StateRecorder— per-frame state recording + npz save/replay
Added (P19 — Collision layers & masks)¶
forge3d.CollisionLayerbitfield constantsBody.collision_layer,Body.collision_maskproperties
Added (P20 — API hardening)¶
forge3d.errorsmodule:Forge3dError,ValidationError,PhysicsError,RenderError- Argument validation for
World(),add_box(),add_sphere()
Added (P21 — Heightfield terrain)¶
World.add_terrain(heights, cell_size, origin)→ Heightfield- Sphere vs heightmap, box vs heightmap collision detection (bilinear interpolation)
Added (P23 — Island sleeping)¶
- Body sleep counter,
Body.is_sleepingproperty,PhysicsWorld.wake_body()
Changed¶
- Version bump:
0.4.0→1.0.0(first stable release) - Baumgarte bias sign fix (significant constraint solver stability improvement)
0.4.0 — 2026-06-03¶
Added (P14 — PyPI distribution infrastructure)¶
- GitHub Actions CI/Release/Docs workflows
- OIDC Trusted Publisher (TestPyPI → PyPI)
.readthedocs.yaml,.github/ISSUE_TEMPLATE/
Added¶
Appclass —@on_start/@on_update/@on_renderdecorator-based game loopInputclass — per-frame keyboard/mouse state snapshotKey— key constants (Key.SPACE,Key.W,Key.ESCAPE…)OrbitCamera,FollowCameraworld.bodies,world.remove(),world.clear(),world.get_body(name)- AABB broadphase pre-filter: O(n²) → O(n log n)
0.2.0 — 2026-05¶
Added¶
Shape.capsule(),Shape.convex_mesh()— capsule and convex mesh supportforge3d.io.load_obj(path)→MeshData— pure-Python OBJ parser- GJK extension: mesh/capsule support, EPA (
collision/epa.py) - PBR shader (Cook-Torrance BRDF): GGX NDF, Smith geometry, Fresnel-Schlick
- PCF shadow map 2K, Reinhard tonemap, albedo texture support
0.1.0 — 2026-03¶
Added¶
World,Body,Shape,Material,Viewer,Recorder— public API- Rigid-body physics: RNEA, CRBA, ABA; semi-implicit Euler
- Collision detection: SAT OBB-OBB, sphere, capsule
- Impulse-based contact solver (Coulomb friction + Baumgarte)
- UR5 6-DOF robot model (FK + Jacobian)
RealtimeRenderer(OpenGL 3.3 + moderngl),HQRenderer(software ray-tracer)SceneSnapshot— physics↔render pure-data contract- JAX JIT+vmap batch physics (2000× throughput)
- NumPy ↔ JAX backend switch (
ENGINE_BACKEND) - 215 automated tests