pyforge3d 2.1.1__tar.gz → 2.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/PKG-INFO +100 -77
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/README.md +84 -59
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/pyproject.toml +21 -21
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/__init__.py +3 -2
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/app.py +43 -25
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/camera.py +181 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/character.py +103 -4
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/collision/detection.py +103 -7
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/collision/gjk.py +2 -2
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/collision/heightfield.py +91 -0
- pyforge3d-2.2.1/src/forge3d/ecs/component.py +102 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ecs/system.py +14 -2
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/facade.py +531 -30
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/input.py +123 -3
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/realtime/meshes.py +190 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/realtime/window_renderer.py +47 -24
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/sim/world.py +4 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/viewer.py +60 -9
- pyforge3d-2.1.1/src/forge3d/ecs/component.py +0 -56
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/Cargo.lock +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/Cargo.toml +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/LICENSE +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/_core.pyi +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/animation/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/animation/clip.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/animation/ik_fabrik.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/animation/player.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/animation/skeleton.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/animation/system.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/audio/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/audio/clip.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/audio/null_driver.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/audio/openal_driver.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/audio/source.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/audio/system.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/backend.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/collision/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/collision/epa.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/collision/layers.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/collision/raycast.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/constraints/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/constraints/base.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/constraints/joint_type.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/constraints/joints.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/contact/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/contact/solver.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/dynamics/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/dynamics/aba.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/dynamics/crba.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/dynamics/model.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/dynamics/rnea.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ecs/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ecs/bridge.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ecs/entity.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ecs/serialization.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ecs/transform.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/editor/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/editor/editor_app.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/editor/gizmo.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/editor/layout.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/errors.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/events.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/io/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/io/mesh_data.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/io/obj_loader.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/io/world_snapshot.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/logging.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/math/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/math/inertia.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/math/quaternion.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/math/se3.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/math/spatial.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/model/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/model/kinematics.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/model/robot_config.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/model/urdf_loader.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/particle/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/particle/emitter.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/particle/presets.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/particle/system.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/profiler.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/py.typed +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/recorder.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/base.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/deferred/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/deferred/renderer.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/hq/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/hq/raytracer.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/hq/renderer.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/hq/scene.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/passes/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/passes/base.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/realtime/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/realtime/context.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/realtime/renderer.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/realtime/shaders.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/bloom_down.frag +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/bloom_up.frag +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/fullscreen.vert +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/gbuffer.frag +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/gbuffer.vert +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/lighting.frag +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/pbr.wgsl +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/shadow.frag +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/shadow.vert +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/ssao.frag +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/ssao_blur.frag +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/tonemap.frag +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/shaders/update_particles.comp +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/snapshot.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/wgpu_backend/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/wgpu_backend/pipeline.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/render/wgpu_backend/renderer.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/robot/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/robot/presets.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/robot/robot.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/scene/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/scene/manager.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/scene/node.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/scene/prefab.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/sim/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/sim/domain_rand.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/sim/jax_batch.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ui/__init__.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ui/backend.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ui/canvas.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ui/panels.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d/ui/system.py +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d_core/Cargo.toml +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d_core/benches/physics_bench.rs +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d_core/src/bvh.rs +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d_core/src/gjk_epa.rs +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d_core/src/lib.rs +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d_core/src/math_simd.rs +0 -0
- {pyforge3d-2.1.1 → pyforge3d-2.2.1}/src/forge3d_core/src/pgs_solver.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyforge3d
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Classifier: Development Status :: 5 - Production/Stable
|
|
5
5
|
Classifier: Programming Language :: Rust
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -16,35 +16,33 @@ Classifier: Topic :: Scientific/Engineering :: Physics
|
|
|
16
16
|
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
18
|
Classifier: Typing :: Typed
|
|
19
|
-
Requires-Dist: numpy>=
|
|
19
|
+
Requires-Dist: numpy>=2.0
|
|
20
20
|
Requires-Dist: scipy>=1.12
|
|
21
21
|
Requires-Dist: sympy>=1.13
|
|
22
22
|
Requires-Dist: jax[cpu]>=0.4.25
|
|
23
|
-
Requires-Dist:
|
|
23
|
+
Requires-Dist: moderngl>=5.10
|
|
24
|
+
Requires-Dist: glfw>=2.5
|
|
25
|
+
Requires-Dist: pillow>=10.0
|
|
26
|
+
Requires-Dist: imageio>=2.34
|
|
27
|
+
Requires-Dist: imageio-ffmpeg>=0.5
|
|
28
|
+
Requires-Dist: soundfile>=0.12
|
|
29
|
+
Requires-Dist: gymnasium>=0.29
|
|
30
|
+
Requires-Dist: stable-baselines3>=2.2
|
|
31
|
+
Requires-Dist: optax>=0.2
|
|
32
|
+
Requires-Dist: matplotlib>=3.8
|
|
33
|
+
Requires-Dist: pyforge3d[experimental,dev,docs] ; extra == 'all'
|
|
24
34
|
Requires-Dist: pytest>=8.0 ; extra == 'dev'
|
|
25
35
|
Requires-Dist: pytest-cov>=5.0 ; extra == 'dev'
|
|
26
36
|
Requires-Dist: ruff>=0.4 ; extra == 'dev'
|
|
27
37
|
Requires-Dist: mypy>=1.9 ; extra == 'dev'
|
|
28
|
-
Requires-Dist: moderngl>=5.10 ; extra == 'dev'
|
|
29
|
-
Requires-Dist: glfw>=2.5 ; extra == 'dev'
|
|
30
|
-
Requires-Dist: pillow>=10.0 ; extra == 'dev'
|
|
31
|
-
Requires-Dist: imageio>=2.34 ; extra == 'dev'
|
|
32
|
-
Requires-Dist: imageio-ffmpeg>=0.5 ; extra == 'dev'
|
|
33
38
|
Requires-Dist: mkdocs-material>=9.5 ; extra == 'docs'
|
|
34
39
|
Requires-Dist: mkdocstrings[python]>=0.25 ; extra == 'docs'
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Requires-Dist: pillow>=10.0 ; extra == 'render'
|
|
38
|
-
Requires-Dist: imageio>=2.34 ; extra == 'render'
|
|
39
|
-
Requires-Dist: imageio-ffmpeg>=0.5 ; extra == 'render'
|
|
40
|
-
Requires-Dist: gymnasium>=0.29 ; extra == 'rl'
|
|
41
|
-
Requires-Dist: stable-baselines3>=2.2 ; extra == 'rl'
|
|
42
|
-
Requires-Dist: optax>=0.2 ; extra == 'rl'
|
|
40
|
+
Requires-Dist: wgpu>=0.19 ; extra == 'experimental'
|
|
41
|
+
Requires-Dist: rendercanvas>=2.0 ; extra == 'experimental'
|
|
43
42
|
Provides-Extra: all
|
|
44
43
|
Provides-Extra: dev
|
|
45
44
|
Provides-Extra: docs
|
|
46
|
-
Provides-Extra:
|
|
47
|
-
Provides-Extra: rl
|
|
45
|
+
Provides-Extra: experimental
|
|
48
46
|
License-File: LICENSE
|
|
49
47
|
Summary: Pure-Python 3D physics game engine — own dynamics, own rules, no compromises.
|
|
50
48
|
Keywords: game-engine,physics,simulation,3d,rigid-body,collision,rendering,robotics,reinforcement-learning,numpy,jax
|
|
@@ -92,17 +90,26 @@ all solved by forge3d's own code, backed by NumPy and optional JAX acceleration.
|
|
|
92
90
|
| Category | Details |
|
|
93
91
|
|---|---|
|
|
94
92
|
| **Game loop** | `App` class — `@on_start` / `@on_update` / `@on_render` decorators |
|
|
95
|
-
| **Input** | `Input` snapshot — `key_held`, `key_pressed`, `mouse_pos`, `scroll_delta` |
|
|
96
|
-
| **Camera** | `OrbitCamera
|
|
93
|
+
| **Input** | `Input` snapshot — `key_held`, `key_pressed`, `axis()`, `mouse_pos`, `scroll_delta` |
|
|
94
|
+
| **Camera** | `OrbitCamera` (with `handle_input()`), `FollowCamera` — orbit, zoom, pan, smooth follow |
|
|
95
|
+
| **Character** | `CharacterController` — `move()`, `jump()`, `glide()`, `move_camera_relative()` |
|
|
97
96
|
| **Physics** | Rigid-body dynamics (RNEA / CRBA / ABA), semi-implicit Euler integrator |
|
|
98
|
-
| **Collision** | SAT OBB-OBB (15-axis), sphere, capsule, GJK/EPA convex-mesh; AABB broad-phase |
|
|
99
|
-
| **Contact** | Impulse solver, Coulomb friction, Baumgarte
|
|
100
|
-
| **
|
|
97
|
+
| **Collision** | SAT OBB-OBB (15-axis), sphere, capsule, GJK/EPA convex-mesh; AABB broad-phase; BVH (Rust) |
|
|
98
|
+
| **Contact** | Impulse-based PGS solver, Coulomb friction, Baumgarte stabilization |
|
|
99
|
+
| **Joints** | Hinge, Ball, Prismatic, Fixed, Distance, Spring; `weld` / `release` |
|
|
100
|
+
| **Terrain** | Heightfield collision + rendering (32×32 to 512×512) |
|
|
101
101
|
| **Robots** | UR5 6-DOF FK/IK, DH parameters, Jacobian |
|
|
102
|
-
| **Rendering** |
|
|
103
|
-
| **
|
|
104
|
-
| **
|
|
105
|
-
| **
|
|
102
|
+
| **Rendering** | `RealtimeRenderer` (OpenGL 3.3 PBR), `DeferredRenderer` (OpenGL 4.3 + SSAO + CSM + Bloom), `HQRenderer` (software ray-tracer) |
|
|
103
|
+
| **ECS** | `EntityWorld`, `Transform`, `Rigidbody`, `MeshRenderer`, `Script`, `PhysicsSystem` |
|
|
104
|
+
| **Animation** | `Skeleton`, `AnimationClip`, `AnimationPlayer`, `BlendTree`, `FABRIKSolver` |
|
|
105
|
+
| **Audio** | `AudioSystem`, `AudioSource`, `AudioClip` — OpenAL, headless null fallback |
|
|
106
|
+
| **Particles** | `ParticleEmitter`, `ParticleSystem` — `sparks`, `smoke`, `debris`, `rain` presets |
|
|
107
|
+
| **Scene** | `SceneManager`, `SceneNode`, `Prefab` — additive scene transitions |
|
|
108
|
+
| **UI** | `Canvas`, `DebugPanel`, `InspectorPanel`, `HierarchyPanel` |
|
|
109
|
+
| **Editor** | `EditorApp` — Play/Pause/Step, transform gizmos |
|
|
110
|
+
| **RL** | Gymnasium-compatible environments; JAX JIT+vmap batch rollouts |
|
|
111
|
+
| **Performance** | JAX JIT+vmap: 2 000× throughput; Rust PyO3 core (PGS, GJK/EPA, BVH) |
|
|
112
|
+
| **Testing** | 459+ automated tests; PyBullet baseline comparison |
|
|
106
113
|
| **Typed** | Fully annotated with `py.typed` marker |
|
|
107
114
|
|
|
108
115
|
---
|
|
@@ -110,27 +117,21 @@ all solved by forge3d's own code, backed by NumPy and optional JAX acceleration.
|
|
|
110
117
|
## Installation
|
|
111
118
|
|
|
112
119
|
```bash
|
|
113
|
-
#
|
|
120
|
+
# Full install — physics, rendering, and RL in one command
|
|
114
121
|
pip install pyforge3d
|
|
115
122
|
|
|
116
|
-
# With rendering dependencies
|
|
117
|
-
pip install "pyforge3d[render]"
|
|
118
|
-
|
|
119
|
-
# With RL dependencies
|
|
120
|
-
pip install "pyforge3d[rl]"
|
|
121
|
-
|
|
122
|
-
# Everything
|
|
123
|
-
pip install "pyforge3d[all]"
|
|
124
|
-
|
|
125
123
|
# Development install
|
|
126
124
|
git clone https://github.com/iruki-dev/forge3d
|
|
127
125
|
cd forge3d
|
|
128
126
|
pip install -e ".[dev]"
|
|
127
|
+
|
|
128
|
+
# WebGPU renderer (experimental, P34 — falls back to OpenGL if omitted)
|
|
129
|
+
pip install "pyforge3d[experimental]"
|
|
129
130
|
```
|
|
130
131
|
|
|
131
132
|
> **Install name vs import name:** `pip install pyforge3d` — then `import forge3d` in your code.
|
|
132
133
|
|
|
133
|
-
**Requirements:** Python 3.12+, NumPy, SciPy, JAX (CPU wheel)
|
|
134
|
+
**Requirements:** Python 3.12+, NumPy, SciPy, JAX (CPU wheel), moderngl, glfw, gymnasium
|
|
134
135
|
|
|
135
136
|
---
|
|
136
137
|
|
|
@@ -257,11 +258,21 @@ print(f"End-effector: {ee_pos.round(4)}")
|
|
|
257
258
|
|
|
258
259
|
## Reinforcement learning
|
|
259
260
|
|
|
261
|
+
forge3d ships a Gymnasium-compatible physics world. Use it directly with any RL library:
|
|
262
|
+
|
|
260
263
|
```python
|
|
261
|
-
|
|
264
|
+
import gymnasium as gym
|
|
265
|
+
import forge3d as f3d
|
|
262
266
|
from stable_baselines3 import PPO
|
|
263
267
|
|
|
264
|
-
env
|
|
268
|
+
# Build a custom Gymnasium env backed by forge3d physics
|
|
269
|
+
class ReachEnv(gym.Env):
|
|
270
|
+
def __init__(self):
|
|
271
|
+
super().__init__()
|
|
272
|
+
self.world = f3d.World(gravity=(0, 0, -9.81))
|
|
273
|
+
# ... define observation_space / action_space
|
|
274
|
+
|
|
275
|
+
env = ReachEnv()
|
|
265
276
|
model = PPO("MlpPolicy", env, verbose=1)
|
|
266
277
|
model.learn(total_timesteps=200_000)
|
|
267
278
|
model.save("reach_policy")
|
|
@@ -436,43 +447,55 @@ Physics core → SceneSnapshot → RealtimeRenderer (OpenGL)
|
|
|
436
447
|
|
|
437
448
|
```
|
|
438
449
|
forge3d/
|
|
439
|
-
├── src/
|
|
440
|
-
│ ├──
|
|
441
|
-
│ ├──
|
|
442
|
-
│ ├──
|
|
443
|
-
│ ├──
|
|
444
|
-
│ ├──
|
|
445
|
-
│ ├──
|
|
446
|
-
│ ├──
|
|
447
|
-
│ ├──
|
|
448
|
-
│ ├──
|
|
449
|
-
│ ├──
|
|
450
|
-
│ ├──
|
|
451
|
-
│ │ ├──
|
|
452
|
-
│ │ ├──
|
|
453
|
-
│ │
|
|
454
|
-
│ ├──
|
|
455
|
-
│ │
|
|
456
|
-
│ ├──
|
|
457
|
-
│ ├──
|
|
458
|
-
│ │ ├──
|
|
459
|
-
│ │
|
|
460
|
-
│ │
|
|
461
|
-
│ ├──
|
|
462
|
-
│ ├──
|
|
463
|
-
│ │ ├──
|
|
464
|
-
│ │ ├──
|
|
465
|
-
│ │ ├──
|
|
466
|
-
│ │
|
|
467
|
-
│ ├──
|
|
468
|
-
│ │ ├──
|
|
469
|
-
│ │
|
|
470
|
-
│
|
|
471
|
-
├──
|
|
472
|
-
├──
|
|
473
|
-
├──
|
|
474
|
-
├──
|
|
475
|
-
|
|
450
|
+
├── src/
|
|
451
|
+
│ ├── forge3d/
|
|
452
|
+
│ │ ├── __init__.py # Public API: World, App, Body, Shape, Material…
|
|
453
|
+
│ │ ├── app.py # App — game-loop abstraction
|
|
454
|
+
│ │ ├── input.py # Input, Key, InputBuilder
|
|
455
|
+
│ │ ├── camera.py # OrbitCamera, FollowCamera
|
|
456
|
+
│ │ ├── character.py # CharacterController
|
|
457
|
+
│ │ ├── facade.py # World, Body, Shape, Material facades
|
|
458
|
+
│ │ ├── viewer.py # Viewer (realtime render loop)
|
|
459
|
+
│ │ ├── recorder.py # Recorder (video capture)
|
|
460
|
+
│ │ ├── backend.py # NumPy ↔ JAX backend switch
|
|
461
|
+
│ │ ├── math/ # SE3, quaternion, spatial algebra
|
|
462
|
+
│ │ ├── dynamics/ # RNEA, CRBA, ABA
|
|
463
|
+
│ │ ├── collision/ # SAT, GJK/EPA, heightfield, layers, raycast
|
|
464
|
+
│ │ ├── contact/ # Impulse solver (PGS + Coulomb friction)
|
|
465
|
+
│ │ ├── constraints/ # Joints: Hinge, Ball, Prismatic, Fixed, Distance, Spring
|
|
466
|
+
│ │ ├── model/ # RigidBodyModel, DH joints, kinematics
|
|
467
|
+
│ │ ├── sim/
|
|
468
|
+
│ │ │ ├── world.py # PhysicsWorld (step, snapshot, CRUD)
|
|
469
|
+
│ │ │ ├── jax_batch.py # JAX JIT+vmap batch physics
|
|
470
|
+
│ │ │ └── domain_rand.py # Domain randomisation
|
|
471
|
+
│ │ ├── robot/ # Robot, UR5 preset, FK/IK
|
|
472
|
+
│ │ ├── ecs/ # EntityWorld, Transform, Rigidbody, Systems…
|
|
473
|
+
│ │ ├── animation/ # Skeleton, AnimationClip, FABRIKSolver…
|
|
474
|
+
│ │ ├── audio/ # AudioSystem, AudioSource, AudioClip (OpenAL)
|
|
475
|
+
│ │ ├── particle/ # ParticleEmitter, ParticleSystem, presets
|
|
476
|
+
│ │ ├── scene/ # SceneManager, SceneNode, Prefab
|
|
477
|
+
│ │ ├── ui/ # Canvas, DebugPanel, InspectorPanel…
|
|
478
|
+
│ │ ├── editor/ # EditorApp, gizmos, layout
|
|
479
|
+
│ │ ├── render/
|
|
480
|
+
│ │ │ ├── snapshot.py # SceneSnapshot — pure-data physics↔render contract
|
|
481
|
+
│ │ │ ├── base.py # Renderer ABC
|
|
482
|
+
│ │ │ ├── realtime/ # OpenGL 3.3 PBR rasteriser
|
|
483
|
+
│ │ │ ├── deferred/ # OpenGL 4.3 deferred (SSAO, CSM, HDR, Bloom)
|
|
484
|
+
│ │ │ ├── hq/ # Software ray-tracer (Blinn-Phong, AO, AA)
|
|
485
|
+
│ │ │ ├── wgpu_backend/ # WebGPU renderer (experimental, P34)
|
|
486
|
+
│ │ │ └── shaders/ # GLSL + WGSL shader sources
|
|
487
|
+
│ │ ├── io/ # OBJ loader, MeshData, world snapshot
|
|
488
|
+
│ │ └── py.typed # PEP 561 type marker
|
|
489
|
+
│ └── forge3d_core/ # Rust crate (PyO3): PGS, GJK/EPA, BVH, SIMD math
|
|
490
|
+
├── apps/
|
|
491
|
+
│ ├── game/ # FORGE RUNNER — 3D physics platformer
|
|
492
|
+
│ ├── fps_battleroyal/ # FPS battle-royale demo
|
|
493
|
+
│ └── showcase/ # Engine feature showcase
|
|
494
|
+
├── examples/ # Self-contained examples (≤15 lines each)
|
|
495
|
+
├── tests/ # 459+ automated tests
|
|
496
|
+
├── validation/ # PyBullet / MuJoCo baseline comparison
|
|
497
|
+
├── assets/ # 3D models, textures
|
|
498
|
+
├── docs_src/ # MkDocs documentation source
|
|
476
499
|
├── CHANGELOG.md
|
|
477
500
|
├── CONTRIBUTING.md
|
|
478
501
|
├── LICENSE
|
|
@@ -33,17 +33,26 @@ all solved by forge3d's own code, backed by NumPy and optional JAX acceleration.
|
|
|
33
33
|
| Category | Details |
|
|
34
34
|
|---|---|
|
|
35
35
|
| **Game loop** | `App` class — `@on_start` / `@on_update` / `@on_render` decorators |
|
|
36
|
-
| **Input** | `Input` snapshot — `key_held`, `key_pressed`, `mouse_pos`, `scroll_delta` |
|
|
37
|
-
| **Camera** | `OrbitCamera
|
|
36
|
+
| **Input** | `Input` snapshot — `key_held`, `key_pressed`, `axis()`, `mouse_pos`, `scroll_delta` |
|
|
37
|
+
| **Camera** | `OrbitCamera` (with `handle_input()`), `FollowCamera` — orbit, zoom, pan, smooth follow |
|
|
38
|
+
| **Character** | `CharacterController` — `move()`, `jump()`, `glide()`, `move_camera_relative()` |
|
|
38
39
|
| **Physics** | Rigid-body dynamics (RNEA / CRBA / ABA), semi-implicit Euler integrator |
|
|
39
|
-
| **Collision** | SAT OBB-OBB (15-axis), sphere, capsule, GJK/EPA convex-mesh; AABB broad-phase |
|
|
40
|
-
| **Contact** | Impulse solver, Coulomb friction, Baumgarte
|
|
41
|
-
| **
|
|
40
|
+
| **Collision** | SAT OBB-OBB (15-axis), sphere, capsule, GJK/EPA convex-mesh; AABB broad-phase; BVH (Rust) |
|
|
41
|
+
| **Contact** | Impulse-based PGS solver, Coulomb friction, Baumgarte stabilization |
|
|
42
|
+
| **Joints** | Hinge, Ball, Prismatic, Fixed, Distance, Spring; `weld` / `release` |
|
|
43
|
+
| **Terrain** | Heightfield collision + rendering (32×32 to 512×512) |
|
|
42
44
|
| **Robots** | UR5 6-DOF FK/IK, DH parameters, Jacobian |
|
|
43
|
-
| **Rendering** |
|
|
44
|
-
| **
|
|
45
|
-
| **
|
|
46
|
-
| **
|
|
45
|
+
| **Rendering** | `RealtimeRenderer` (OpenGL 3.3 PBR), `DeferredRenderer` (OpenGL 4.3 + SSAO + CSM + Bloom), `HQRenderer` (software ray-tracer) |
|
|
46
|
+
| **ECS** | `EntityWorld`, `Transform`, `Rigidbody`, `MeshRenderer`, `Script`, `PhysicsSystem` |
|
|
47
|
+
| **Animation** | `Skeleton`, `AnimationClip`, `AnimationPlayer`, `BlendTree`, `FABRIKSolver` |
|
|
48
|
+
| **Audio** | `AudioSystem`, `AudioSource`, `AudioClip` — OpenAL, headless null fallback |
|
|
49
|
+
| **Particles** | `ParticleEmitter`, `ParticleSystem` — `sparks`, `smoke`, `debris`, `rain` presets |
|
|
50
|
+
| **Scene** | `SceneManager`, `SceneNode`, `Prefab` — additive scene transitions |
|
|
51
|
+
| **UI** | `Canvas`, `DebugPanel`, `InspectorPanel`, `HierarchyPanel` |
|
|
52
|
+
| **Editor** | `EditorApp` — Play/Pause/Step, transform gizmos |
|
|
53
|
+
| **RL** | Gymnasium-compatible environments; JAX JIT+vmap batch rollouts |
|
|
54
|
+
| **Performance** | JAX JIT+vmap: 2 000× throughput; Rust PyO3 core (PGS, GJK/EPA, BVH) |
|
|
55
|
+
| **Testing** | 459+ automated tests; PyBullet baseline comparison |
|
|
47
56
|
| **Typed** | Fully annotated with `py.typed` marker |
|
|
48
57
|
|
|
49
58
|
---
|
|
@@ -51,27 +60,21 @@ all solved by forge3d's own code, backed by NumPy and optional JAX acceleration.
|
|
|
51
60
|
## Installation
|
|
52
61
|
|
|
53
62
|
```bash
|
|
54
|
-
#
|
|
63
|
+
# Full install — physics, rendering, and RL in one command
|
|
55
64
|
pip install pyforge3d
|
|
56
65
|
|
|
57
|
-
# With rendering dependencies
|
|
58
|
-
pip install "pyforge3d[render]"
|
|
59
|
-
|
|
60
|
-
# With RL dependencies
|
|
61
|
-
pip install "pyforge3d[rl]"
|
|
62
|
-
|
|
63
|
-
# Everything
|
|
64
|
-
pip install "pyforge3d[all]"
|
|
65
|
-
|
|
66
66
|
# Development install
|
|
67
67
|
git clone https://github.com/iruki-dev/forge3d
|
|
68
68
|
cd forge3d
|
|
69
69
|
pip install -e ".[dev]"
|
|
70
|
+
|
|
71
|
+
# WebGPU renderer (experimental, P34 — falls back to OpenGL if omitted)
|
|
72
|
+
pip install "pyforge3d[experimental]"
|
|
70
73
|
```
|
|
71
74
|
|
|
72
75
|
> **Install name vs import name:** `pip install pyforge3d` — then `import forge3d` in your code.
|
|
73
76
|
|
|
74
|
-
**Requirements:** Python 3.12+, NumPy, SciPy, JAX (CPU wheel)
|
|
77
|
+
**Requirements:** Python 3.12+, NumPy, SciPy, JAX (CPU wheel), moderngl, glfw, gymnasium
|
|
75
78
|
|
|
76
79
|
---
|
|
77
80
|
|
|
@@ -198,11 +201,21 @@ print(f"End-effector: {ee_pos.round(4)}")
|
|
|
198
201
|
|
|
199
202
|
## Reinforcement learning
|
|
200
203
|
|
|
204
|
+
forge3d ships a Gymnasium-compatible physics world. Use it directly with any RL library:
|
|
205
|
+
|
|
201
206
|
```python
|
|
202
|
-
|
|
207
|
+
import gymnasium as gym
|
|
208
|
+
import forge3d as f3d
|
|
203
209
|
from stable_baselines3 import PPO
|
|
204
210
|
|
|
205
|
-
env
|
|
211
|
+
# Build a custom Gymnasium env backed by forge3d physics
|
|
212
|
+
class ReachEnv(gym.Env):
|
|
213
|
+
def __init__(self):
|
|
214
|
+
super().__init__()
|
|
215
|
+
self.world = f3d.World(gravity=(0, 0, -9.81))
|
|
216
|
+
# ... define observation_space / action_space
|
|
217
|
+
|
|
218
|
+
env = ReachEnv()
|
|
206
219
|
model = PPO("MlpPolicy", env, verbose=1)
|
|
207
220
|
model.learn(total_timesteps=200_000)
|
|
208
221
|
model.save("reach_policy")
|
|
@@ -377,43 +390,55 @@ Physics core → SceneSnapshot → RealtimeRenderer (OpenGL)
|
|
|
377
390
|
|
|
378
391
|
```
|
|
379
392
|
forge3d/
|
|
380
|
-
├── src/
|
|
381
|
-
│ ├──
|
|
382
|
-
│ ├──
|
|
383
|
-
│ ├──
|
|
384
|
-
│ ├──
|
|
385
|
-
│ ├──
|
|
386
|
-
│ ├──
|
|
387
|
-
│ ├──
|
|
388
|
-
│ ├──
|
|
389
|
-
│ ├──
|
|
390
|
-
│ ├──
|
|
391
|
-
│ ├──
|
|
392
|
-
│ │ ├──
|
|
393
|
-
│ │ ├──
|
|
394
|
-
│ │
|
|
395
|
-
│ ├──
|
|
396
|
-
│ │
|
|
397
|
-
│ ├──
|
|
398
|
-
│ ├──
|
|
399
|
-
│ │ ├──
|
|
400
|
-
│ │
|
|
401
|
-
│ │
|
|
402
|
-
│ ├──
|
|
403
|
-
│ ├──
|
|
404
|
-
│ │ ├──
|
|
405
|
-
│ │ ├──
|
|
406
|
-
│ │ ├──
|
|
407
|
-
│ │
|
|
408
|
-
│ ├──
|
|
409
|
-
│ │ ├──
|
|
410
|
-
│ │
|
|
411
|
-
│
|
|
412
|
-
├──
|
|
413
|
-
├──
|
|
414
|
-
├──
|
|
415
|
-
├──
|
|
416
|
-
|
|
393
|
+
├── src/
|
|
394
|
+
│ ├── forge3d/
|
|
395
|
+
│ │ ├── __init__.py # Public API: World, App, Body, Shape, Material…
|
|
396
|
+
│ │ ├── app.py # App — game-loop abstraction
|
|
397
|
+
│ │ ├── input.py # Input, Key, InputBuilder
|
|
398
|
+
│ │ ├── camera.py # OrbitCamera, FollowCamera
|
|
399
|
+
│ │ ├── character.py # CharacterController
|
|
400
|
+
│ │ ├── facade.py # World, Body, Shape, Material facades
|
|
401
|
+
│ │ ├── viewer.py # Viewer (realtime render loop)
|
|
402
|
+
│ │ ├── recorder.py # Recorder (video capture)
|
|
403
|
+
│ │ ├── backend.py # NumPy ↔ JAX backend switch
|
|
404
|
+
│ │ ├── math/ # SE3, quaternion, spatial algebra
|
|
405
|
+
│ │ ├── dynamics/ # RNEA, CRBA, ABA
|
|
406
|
+
│ │ ├── collision/ # SAT, GJK/EPA, heightfield, layers, raycast
|
|
407
|
+
│ │ ├── contact/ # Impulse solver (PGS + Coulomb friction)
|
|
408
|
+
│ │ ├── constraints/ # Joints: Hinge, Ball, Prismatic, Fixed, Distance, Spring
|
|
409
|
+
│ │ ├── model/ # RigidBodyModel, DH joints, kinematics
|
|
410
|
+
│ │ ├── sim/
|
|
411
|
+
│ │ │ ├── world.py # PhysicsWorld (step, snapshot, CRUD)
|
|
412
|
+
│ │ │ ├── jax_batch.py # JAX JIT+vmap batch physics
|
|
413
|
+
│ │ │ └── domain_rand.py # Domain randomisation
|
|
414
|
+
│ │ ├── robot/ # Robot, UR5 preset, FK/IK
|
|
415
|
+
│ │ ├── ecs/ # EntityWorld, Transform, Rigidbody, Systems…
|
|
416
|
+
│ │ ├── animation/ # Skeleton, AnimationClip, FABRIKSolver…
|
|
417
|
+
│ │ ├── audio/ # AudioSystem, AudioSource, AudioClip (OpenAL)
|
|
418
|
+
│ │ ├── particle/ # ParticleEmitter, ParticleSystem, presets
|
|
419
|
+
│ │ ├── scene/ # SceneManager, SceneNode, Prefab
|
|
420
|
+
│ │ ├── ui/ # Canvas, DebugPanel, InspectorPanel…
|
|
421
|
+
│ │ ├── editor/ # EditorApp, gizmos, layout
|
|
422
|
+
│ │ ├── render/
|
|
423
|
+
│ │ │ ├── snapshot.py # SceneSnapshot — pure-data physics↔render contract
|
|
424
|
+
│ │ │ ├── base.py # Renderer ABC
|
|
425
|
+
│ │ │ ├── realtime/ # OpenGL 3.3 PBR rasteriser
|
|
426
|
+
│ │ │ ├── deferred/ # OpenGL 4.3 deferred (SSAO, CSM, HDR, Bloom)
|
|
427
|
+
│ │ │ ├── hq/ # Software ray-tracer (Blinn-Phong, AO, AA)
|
|
428
|
+
│ │ │ ├── wgpu_backend/ # WebGPU renderer (experimental, P34)
|
|
429
|
+
│ │ │ └── shaders/ # GLSL + WGSL shader sources
|
|
430
|
+
│ │ ├── io/ # OBJ loader, MeshData, world snapshot
|
|
431
|
+
│ │ └── py.typed # PEP 561 type marker
|
|
432
|
+
│ └── forge3d_core/ # Rust crate (PyO3): PGS, GJK/EPA, BVH, SIMD math
|
|
433
|
+
├── apps/
|
|
434
|
+
│ ├── game/ # FORGE RUNNER — 3D physics platformer
|
|
435
|
+
│ ├── fps_battleroyal/ # FPS battle-royale demo
|
|
436
|
+
│ └── showcase/ # Engine feature showcase
|
|
437
|
+
├── examples/ # Self-contained examples (≤15 lines each)
|
|
438
|
+
├── tests/ # 459+ automated tests
|
|
439
|
+
├── validation/ # PyBullet / MuJoCo baseline comparison
|
|
440
|
+
├── assets/ # 3D models, textures
|
|
441
|
+
├── docs_src/ # MkDocs documentation source
|
|
417
442
|
├── CHANGELOG.md
|
|
418
443
|
├── CONTRIBUTING.md
|
|
419
444
|
├── LICENSE
|
|
@@ -6,7 +6,7 @@ build-backend = "maturin"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "pyforge3d"
|
|
9
|
-
version = "2.
|
|
9
|
+
version = "2.2.1"
|
|
10
10
|
description = "Pure-Python 3D physics game engine — own dynamics, own rules, no compromises."
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
license = { file = "LICENSE" }
|
|
@@ -35,10 +35,22 @@ classifiers = [
|
|
|
35
35
|
"Typing :: Typed",
|
|
36
36
|
]
|
|
37
37
|
dependencies = [
|
|
38
|
-
"numpy>=
|
|
38
|
+
"numpy>=2.0",
|
|
39
39
|
"scipy>=1.12",
|
|
40
40
|
"sympy>=1.13",
|
|
41
41
|
"jax[cpu]>=0.4.25",
|
|
42
|
+
# rendering
|
|
43
|
+
"moderngl>=5.10",
|
|
44
|
+
"glfw>=2.5",
|
|
45
|
+
"Pillow>=10.0",
|
|
46
|
+
"imageio>=2.34",
|
|
47
|
+
"imageio-ffmpeg>=0.5",
|
|
48
|
+
"soundfile>=0.12",
|
|
49
|
+
# RL / training
|
|
50
|
+
"gymnasium>=0.29",
|
|
51
|
+
"stable-baselines3>=2.2",
|
|
52
|
+
"optax>=0.2",
|
|
53
|
+
"matplotlib>=3.8",
|
|
42
54
|
]
|
|
43
55
|
|
|
44
56
|
[project.urls]
|
|
@@ -51,36 +63,22 @@ Changelog = "https://github.com/iruki-dev/forge3d/blob/main/CHANGELOG.md"
|
|
|
51
63
|
# ── Optional dependency groups ────────────────────────────────────────────────
|
|
52
64
|
|
|
53
65
|
[project.optional-dependencies]
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"Pillow>=10.0",
|
|
58
|
-
"imageio>=2.34",
|
|
59
|
-
"imageio-ffmpeg>=0.5",
|
|
60
|
-
]
|
|
61
|
-
rl = [
|
|
62
|
-
"gymnasium>=0.29",
|
|
63
|
-
"stable-baselines3>=2.2",
|
|
64
|
-
"optax>=0.2",
|
|
66
|
+
experimental = [
|
|
67
|
+
"wgpu>=0.19", # P34 WebGPU 렌더러 (없으면 GL 폴백)
|
|
68
|
+
"rendercanvas>=2.0", # wgpu 캔버스 백엔드
|
|
65
69
|
]
|
|
66
70
|
dev = [
|
|
67
71
|
"pytest>=8.0",
|
|
68
72
|
"pytest-cov>=5.0",
|
|
69
73
|
"ruff>=0.4",
|
|
70
74
|
"mypy>=1.9",
|
|
71
|
-
# render deps for tests
|
|
72
|
-
"moderngl>=5.10",
|
|
73
|
-
"glfw>=2.5",
|
|
74
|
-
"Pillow>=10.0",
|
|
75
|
-
"imageio>=2.34",
|
|
76
|
-
"imageio-ffmpeg>=0.5",
|
|
77
75
|
]
|
|
78
76
|
docs = [
|
|
79
77
|
"mkdocs-material>=9.5",
|
|
80
78
|
"mkdocstrings[python]>=0.25",
|
|
81
79
|
]
|
|
82
80
|
all = [
|
|
83
|
-
"pyforge3d[
|
|
81
|
+
"pyforge3d[experimental,dev,docs]",
|
|
84
82
|
]
|
|
85
83
|
|
|
86
84
|
# ── Build (maturin — Python + Rust 혼합) ──────────────────────────────────────
|
|
@@ -103,13 +101,15 @@ addopts = "-q --tb=short"
|
|
|
103
101
|
line-length = 100
|
|
104
102
|
target-version = "py312"
|
|
105
103
|
src = ["src"]
|
|
104
|
+
exclude = ["archive", "demos"]
|
|
106
105
|
|
|
107
106
|
[tool.ruff.lint]
|
|
108
107
|
select = ["E", "F", "I", "UP", "B", "C4", "SIM"]
|
|
109
108
|
ignore = ["B008", "SIM118"]
|
|
110
109
|
|
|
111
110
|
[tool.ruff.lint.per-file-ignores]
|
|
112
|
-
"tests/**"
|
|
111
|
+
"tests/**" = ["E501"]
|
|
112
|
+
"apps/**" = ["E501", "E701", "E702", "B905", "E741", "SIM105", "SIM108"]
|
|
113
113
|
|
|
114
114
|
# ── Type-check ────────────────────────────────────────────────────────────────
|
|
115
115
|
|
|
@@ -103,7 +103,7 @@ from forge3d.editor import EditorApp, PlayState
|
|
|
103
103
|
from forge3d.errors import Forge3dError, PhysicsError, RenderError, ValidationError
|
|
104
104
|
from forge3d.events import CollisionEvent, CollisionHandler
|
|
105
105
|
from forge3d.facade import Body, Material, Shape, World
|
|
106
|
-
from forge3d.input import Input, InputBuilder, Key
|
|
106
|
+
from forge3d.input import Input, InputBuilder, Key, ScriptedInput
|
|
107
107
|
from forge3d.io.world_snapshot import StateRecorder
|
|
108
108
|
|
|
109
109
|
# Particle system (P31)
|
|
@@ -121,7 +121,7 @@ from forge3d.scene import Prefab, SceneManager, SceneNode
|
|
|
121
121
|
from forge3d.ui import Canvas, DebugPanel, HierarchyPanel, InspectorPanel, UISystem
|
|
122
122
|
from forge3d.viewer import Viewer
|
|
123
123
|
|
|
124
|
-
__version__ = "2.
|
|
124
|
+
__version__ = "2.2.1"
|
|
125
125
|
|
|
126
126
|
# ── API 안정성 선언 ───────────────────────────────────────────────────────────
|
|
127
127
|
# Stable (v3까지 Breaking change 없음):
|
|
@@ -158,6 +158,7 @@ __all__ = [
|
|
|
158
158
|
"Input",
|
|
159
159
|
"InputBuilder",
|
|
160
160
|
"Key",
|
|
161
|
+
"ScriptedInput",
|
|
161
162
|
# Camera
|
|
162
163
|
"OrbitCamera",
|
|
163
164
|
"FollowCamera",
|