three-rb 0.1.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +19 -0
- data/LICENSE +21 -0
- data/README.md +153 -0
- data/docs/browser-runtime.md +153 -0
- data/docs/implementation-plan.md +874 -0
- data/docs/loaded-assets-design.md +400 -0
- data/docs/next-work.md +107 -0
- data/docs/publishing.md +64 -0
- data/docs/release-readiness.md +83 -0
- data/examples/browser/README.md +54 -0
- data/examples/browser/assets/animated_triangle.gltf +123 -0
- data/examples/browser/assets/checker.svg +11 -0
- data/examples/browser/assets/compressed_triangle.gltf +74 -0
- data/examples/browser/assets/studio.hdr +5 -0
- data/examples/browser/assets/triangle.gltf +67 -0
- data/examples/browser/composition/README.md +35 -0
- data/examples/browser/composition/boot.mjs +6 -0
- data/examples/browser/composition/index.html +136 -0
- data/examples/browser/composition/main.rb +216 -0
- data/examples/browser/composition/smoke_test.mjs +266 -0
- data/examples/browser/cube/README.md +41 -0
- data/examples/browser/cube/boot.mjs +6 -0
- data/examples/browser/cube/index.html +142 -0
- data/examples/browser/cube/main.rb +62 -0
- data/examples/browser/cube/smoke_test.mjs +99 -0
- data/examples/browser/cubemap/README.md +23 -0
- data/examples/browser/cubemap/boot.mjs +6 -0
- data/examples/browser/cubemap/index.html +142 -0
- data/examples/browser/cubemap/main.rb +84 -0
- data/examples/browser/cubemap/smoke_test.mjs +91 -0
- data/examples/browser/gltf/README.md +23 -0
- data/examples/browser/gltf/boot.mjs +6 -0
- data/examples/browser/gltf/index.html +142 -0
- data/examples/browser/gltf/main.rb +105 -0
- data/examples/browser/gltf/smoke_test.mjs +162 -0
- data/examples/browser/picking/README.md +33 -0
- data/examples/browser/picking/boot.mjs +6 -0
- data/examples/browser/picking/index.html +142 -0
- data/examples/browser/picking/main.rb +113 -0
- data/examples/browser/picking/smoke_test.mjs +78 -0
- data/examples/browser/postprocessing/README.md +26 -0
- data/examples/browser/postprocessing/boot.mjs +6 -0
- data/examples/browser/postprocessing/index.html +142 -0
- data/examples/browser/postprocessing/main.rb +117 -0
- data/examples/browser/postprocessing/smoke_test.mjs +121 -0
- data/examples/browser/primitives/README.md +33 -0
- data/examples/browser/primitives/boot.mjs +6 -0
- data/examples/browser/primitives/index.html +142 -0
- data/examples/browser/primitives/main.rb +116 -0
- data/examples/browser/primitives/smoke_test.mjs +113 -0
- data/examples/browser/serialization/README.md +33 -0
- data/examples/browser/serialization/boot.mjs +6 -0
- data/examples/browser/serialization/index.html +142 -0
- data/examples/browser/serialization/main.rb +97 -0
- data/examples/browser/serialization/smoke_test.mjs +67 -0
- data/examples/browser/shared/boot.mjs +79 -0
- data/examples/browser/shared/smoke_test_helpers.mjs +151 -0
- data/examples/browser/textures/README.md +35 -0
- data/examples/browser/textures/boot.mjs +6 -0
- data/examples/browser/textures/index.html +142 -0
- data/examples/browser/textures/main.rb +142 -0
- data/examples/browser/textures/smoke_test.mjs +189 -0
- data/lib/three/animation/animation_action.rb +57 -0
- data/lib/three/animation/animation_clip.rb +22 -0
- data/lib/three/animation/animation_mixer.rb +43 -0
- data/lib/three/backends/base.rb +87 -0
- data/lib/three/backends/threejs/materialization.rb +143 -0
- data/lib/three/backends/threejs/parameters.rb +97 -0
- data/lib/three/backends/threejs/resource_management.rb +69 -0
- data/lib/three/backends/threejs/ruby_wasm_adapter.rb +873 -0
- data/lib/three/backends/threejs/synchronization.rb +224 -0
- data/lib/three/backends/threejs.rb +365 -0
- data/lib/three/cameras/camera.rb +39 -0
- data/lib/three/cameras/orthographic_camera.rb +107 -0
- data/lib/three/cameras/perspective_camera.rb +137 -0
- data/lib/three/constants.rb +40 -0
- data/lib/three/controls/orbit_controls.rb +118 -0
- data/lib/three/core/buffer_attribute.rb +151 -0
- data/lib/three/core/buffer_geometry.rb +181 -0
- data/lib/three/core/clock.rb +58 -0
- data/lib/three/core/event_dispatcher.rb +57 -0
- data/lib/three/core/layers.rb +75 -0
- data/lib/three/core/object3d.rb +331 -0
- data/lib/three/core/raycaster.rb +73 -0
- data/lib/three/dirty.rb +58 -0
- data/lib/three/exporters/three_json_exporter.rb +187 -0
- data/lib/three/geometries/box_geometry.rb +97 -0
- data/lib/three/geometries/plane_geometry.rb +70 -0
- data/lib/three/geometries/sphere_geometry.rb +107 -0
- data/lib/three/lights/ambient_light.rb +12 -0
- data/lib/three/lights/directional_light.rb +38 -0
- data/lib/three/lights/hemisphere_light.rb +34 -0
- data/lib/three/lights/light.rb +85 -0
- data/lib/three/lights/point_light.rb +33 -0
- data/lib/three/loaders/cube_texture_loader.rb +13 -0
- data/lib/three/loaders/gltf_loader.rb +48 -0
- data/lib/three/loaders/rgbe_loader.rb +15 -0
- data/lib/three/loaders/texture_loader.rb +13 -0
- data/lib/three/loaders/three_json_loader.rb +409 -0
- data/lib/three/materials/line_basic_material.rb +65 -0
- data/lib/three/materials/material.rb +158 -0
- data/lib/three/materials/mesh_basic_material.rb +64 -0
- data/lib/three/materials/mesh_lambert_material.rb +71 -0
- data/lib/three/materials/mesh_matcap_material.rb +86 -0
- data/lib/three/materials/mesh_normal_material.rb +42 -0
- data/lib/three/materials/mesh_phong_material.rb +119 -0
- data/lib/three/materials/mesh_physical_material.rb +155 -0
- data/lib/three/materials/mesh_standard_material.rb +149 -0
- data/lib/three/materials/mesh_toon_material.rb +98 -0
- data/lib/three/materials/points_material.rb +74 -0
- data/lib/three/materials/shadow_material.rb +45 -0
- data/lib/three/materials/sprite_material.rb +75 -0
- data/lib/three/math/color.rb +133 -0
- data/lib/three/math/euler.rb +197 -0
- data/lib/three/math/math_utils.rb +36 -0
- data/lib/three/math/matrix3.rb +255 -0
- data/lib/three/math/matrix4.rb +448 -0
- data/lib/three/math/quaternion.rb +277 -0
- data/lib/three/math/vector2.rb +95 -0
- data/lib/three/math/vector3.rb +396 -0
- data/lib/three/objects/external_object3d.rb +28 -0
- data/lib/three/objects/group.rb +12 -0
- data/lib/three/objects/instanced_mesh.rb +110 -0
- data/lib/three/objects/line.rb +41 -0
- data/lib/three/objects/mesh.rb +45 -0
- data/lib/three/objects/points.rb +41 -0
- data/lib/three/objects/sprite.rb +57 -0
- data/lib/three/postprocessing/dot_screen_pass.rb +83 -0
- data/lib/three/postprocessing/effect_composer.rb +56 -0
- data/lib/three/postprocessing/output_pass.rb +40 -0
- data/lib/three/postprocessing/render_pass.rb +42 -0
- data/lib/three/postprocessing/unreal_bloom_pass.rb +56 -0
- data/lib/three/renderers/renderer.rb +11 -0
- data/lib/three/renderers/threejs_renderer.rb +85 -0
- data/lib/three/scenes/scene.rb +29 -0
- data/lib/three/textures/cube_texture.rb +72 -0
- data/lib/three/textures/rgbe_texture.rb +45 -0
- data/lib/three/textures/texture.rb +200 -0
- data/lib/three/version.rb +5 -0
- data/lib/three-rb.rb +3 -0
- data/lib/three.rb +77 -0
- data/package.json +30 -0
- data/pnpm-lock.yaml +86 -0
- metadata +216 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# Loaded Asset Traversal and Disposal Design
|
|
2
|
+
|
|
3
|
+
This document records the recommended design for loaded assets such as glTF scenes. It is intended to be self-contained enough to resume implementation even if the conversation context is lost.
|
|
4
|
+
|
|
5
|
+
## Status Snapshot
|
|
6
|
+
|
|
7
|
+
Repository state when this decision was written:
|
|
8
|
+
|
|
9
|
+
- Branch: `main`
|
|
10
|
+
- Relevant recent commits:
|
|
11
|
+
- `421a89c Add GLTF loader`
|
|
12
|
+
- `3bc389d Add CubeTexture loader`
|
|
13
|
+
- `9b6307c Add renderer disposal helper`
|
|
14
|
+
- Current implementation:
|
|
15
|
+
- `Three::Loaders::GLTFLoader` delegates to JavaScript `GLTFLoader#loadAsync` and can attach JavaScript `DRACOLoader` when `draco_decoder_path:` is configured.
|
|
16
|
+
- `Three::Loaders::GLTF#scene` is a `Three::ExternalObject3D`.
|
|
17
|
+
- `Three::ExternalObject3D` stores a loaded JavaScript `Object3D` handle.
|
|
18
|
+
- `Three::Backends::ThreeJS#materialize` returns that handle directly for `ExternalObject3D`.
|
|
19
|
+
- `examples/browser/gltf` verifies that a loaded glTF scene can be added to a Ruby-authored scene and rendered.
|
|
20
|
+
|
|
21
|
+
Important local files:
|
|
22
|
+
|
|
23
|
+
- `lib/three/loaders/gltf_loader.rb`
|
|
24
|
+
- `lib/three/objects/external_object3d.rb`
|
|
25
|
+
- `lib/three/backends/threejs.rb`
|
|
26
|
+
- `examples/browser/gltf/main.rb`
|
|
27
|
+
- `examples/browser/gltf/smoke_test.mjs`
|
|
28
|
+
- `test/three/loaders/gltf_loader_test.rb`
|
|
29
|
+
- `test/three/objects/external_object3d_test.rb`
|
|
30
|
+
- `test/three/backends/threejs_test.rb`
|
|
31
|
+
|
|
32
|
+
## Implementation Status
|
|
33
|
+
|
|
34
|
+
The recommended design has been implemented for the current loaded-asset MVP:
|
|
35
|
+
|
|
36
|
+
- `ExternalObject3D#add`, `#remove`, and `#clear` now reject Ruby child mutation.
|
|
37
|
+
- `Three::Backends::ThreeJS#traverse_handles` traverses backend object handles without changing `Object3D#traverse`.
|
|
38
|
+
- `Three::Renderers::ThreeJSRenderer#traverse_handles` exposes the same traversal at renderer scope.
|
|
39
|
+
- `Three::Backends::ThreeJS#dispose_subtree` delegates subtree cleanup to the backend adapter.
|
|
40
|
+
- `Three::Renderers::ThreeJSRenderer#dispose_subtree` provides high-level loaded-asset cleanup and defaults `dispose_textures` to `true`.
|
|
41
|
+
- `RubyWasmAdapter` collects unique geometries, materials, common material texture slots, scene background/environment textures, and skeletons before disposing.
|
|
42
|
+
- `FakeThreeJSAdapter` mirrors this behavior for unit tests.
|
|
43
|
+
- `examples/browser/gltf/smoke_test.mjs` verifies that the Ruby renderer API decodes a Draco-compressed glTF fixture, detaches loaded glTF roots, and dispatches geometry/material/texture dispose events.
|
|
44
|
+
|
|
45
|
+
Remaining work:
|
|
46
|
+
|
|
47
|
+
- The Ruby material model exposes common `MeshStandardMaterial` PBR texture slots, `MeshPhysicalMaterial` physical texture slots, and `MeshMatcapMaterial` matcap texture slots. Broaden ownership helpers further if additional material classes introduce new texture slots.
|
|
48
|
+
- If future APIs need to inspect or edit loaded child objects, design explicit wrapper types instead of changing `Object3D#traverse`.
|
|
49
|
+
|
|
50
|
+
## Decision
|
|
51
|
+
|
|
52
|
+
Keep loaded three.js assets opaque by default.
|
|
53
|
+
|
|
54
|
+
Do not fully convert a loaded glTF scene into Ruby `Mesh`, `Material`, `BufferGeometry`, `Texture`, `Camera`, or animation objects at this stage. Instead:
|
|
55
|
+
|
|
56
|
+
1. Use `ExternalObject3D` as an opaque root wrapper around the loaded JavaScript `Object3D`.
|
|
57
|
+
2. Let Ruby own only the attachment point and transform-level concerns for that external root.
|
|
58
|
+
3. Add explicit backend/renderer helpers for traversing and disposing resources inside the external JavaScript subtree.
|
|
59
|
+
4. Preserve the existing pure Ruby `Object3D#traverse` semantics for Ruby-authored objects.
|
|
60
|
+
|
|
61
|
+
The first target user-facing API should be:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
gltf = Three::Loaders::GLTFLoader.new.load("/models/model.gltf")
|
|
65
|
+
scene.add(gltf.scene)
|
|
66
|
+
|
|
67
|
+
# Later, when the loaded asset is no longer needed:
|
|
68
|
+
renderer.dispose_subtree(gltf.scene, remove: true, dispose_textures: true)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Rationale
|
|
72
|
+
|
|
73
|
+
three.js `GLTFLoader` already constructs a complete JavaScript scene graph. Its documented usage is to load a glTF asset and add `gltf.scene` directly to the scene. The returned object also carries `animations`, `scenes`, `cameras`, `asset`, `parser`, and `userData`.
|
|
74
|
+
|
|
75
|
+
`GLTFLoader` performs non-trivial work that should not be duplicated in Ruby early:
|
|
76
|
+
|
|
77
|
+
- It returns `Group` scenes, not `THREE.Scene` instances.
|
|
78
|
+
- It handles shared node references and clones reused nodes when necessary.
|
|
79
|
+
- It reuses non-scene resources such as materials, geometries, and textures by reference.
|
|
80
|
+
- It preserves parser associations and glTF extension data.
|
|
81
|
+
- It can involve skins, skeletons, morph targets, animations, compressed geometry, texture transforms, and custom extensions.
|
|
82
|
+
|
|
83
|
+
Ruby-side full conversion would either lose these semantics or force a large, fragile mirror of three.js loader internals. That is not justified for the current goal: write Ruby scene code and render through three.js in the browser.
|
|
84
|
+
|
|
85
|
+
## Source Findings
|
|
86
|
+
|
|
87
|
+
These findings were verified against the local vendored/reference repositories.
|
|
88
|
+
|
|
89
|
+
three.js facts:
|
|
90
|
+
|
|
91
|
+
- `Object3D#add` removes an object from its previous parent and attaches it to the new parent.
|
|
92
|
+
- `Object3D#traverse` walks JavaScript children depth-first and discourages modifying the scene graph inside the callback.
|
|
93
|
+
- `BufferGeometry#dispose`, `Material#dispose`, and `Texture#dispose` dispatch disposal events for GPU-related resources.
|
|
94
|
+
- Removing a mesh from a scene does not dispose its geometry or material.
|
|
95
|
+
- Disposing a material does not dispose textures, because textures can be shared.
|
|
96
|
+
- The three.js manual recommends explicit resource tracking for cleanup.
|
|
97
|
+
- Scene-related resources such as `scene.background`, `scene.environment`, and `material.envMap` may leave renderer-internal resources visible in `renderer.info.memory`, even after reachable app resources are disposed.
|
|
98
|
+
|
|
99
|
+
Local source locations:
|
|
100
|
+
|
|
101
|
+
- `node_modules/three/src/core/Object3D.js`
|
|
102
|
+
- `node_modules/three/src/core/BufferGeometry.js`
|
|
103
|
+
- `node_modules/three/src/materials/Material.js`
|
|
104
|
+
- `node_modules/three/src/textures/Texture.js`
|
|
105
|
+
- `node_modules/three/examples/jsm/loaders/GLTFLoader.js`
|
|
106
|
+
- `node_modules/three/examples/jsm/utils/SkeletonUtils.js`
|
|
107
|
+
- `~/ghq/github.com/mrdoob/three.js/manual/en/how-to-dispose-of-objects.html`
|
|
108
|
+
- `~/ghq/github.com/mrdoob/three.js/manual/en/cleanup.html`
|
|
109
|
+
|
|
110
|
+
ruby.wasm facts:
|
|
111
|
+
|
|
112
|
+
- `JS::Object#await` works in `evalAsync` / `callAsync` contexts.
|
|
113
|
+
- `JS::Object` supports property access with `object[:name]`, method calls with `call`, and array conversion with `to_a`.
|
|
114
|
+
- Passing blocks/procs to JavaScript callbacks is supported enough for traversal-style APIs.
|
|
115
|
+
|
|
116
|
+
Local source locations:
|
|
117
|
+
|
|
118
|
+
- `~/ghq/github.com/ruby/ruby.wasm/packages/gems/js/lib/js.rb`
|
|
119
|
+
- `~/ghq/github.com/ruby/ruby.wasm/packages/npm-packages/ruby-wasm-wasi/test/eval_async.test.js`
|
|
120
|
+
|
|
121
|
+
## Verified Behavior
|
|
122
|
+
|
|
123
|
+
The existing glTF browser smoke test verifies:
|
|
124
|
+
|
|
125
|
+
- `Three::Loaders::GLTFLoader` loads `examples/browser/assets/triangle.gltf`.
|
|
126
|
+
- The returned `gltf.scene` can be added to a Ruby `Scene`.
|
|
127
|
+
- The loaded JavaScript scene renders through `Three::Renderers::ThreeJSRenderer`.
|
|
128
|
+
- The animation loop can mutate the external root transform.
|
|
129
|
+
|
|
130
|
+
Additional manual verification was performed in a real headless browser:
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
objects=2
|
|
134
|
+
meshes=1
|
|
135
|
+
geometries=1
|
|
136
|
+
materials=1
|
|
137
|
+
textures=0
|
|
138
|
+
geometryDisposeEvents=1
|
|
139
|
+
materialDisposeEvents=1
|
|
140
|
+
textureDisposeEvents=0
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
This confirmed that a loaded glTF JavaScript subtree can be traversed through its handle and that geometry/material disposal events fire as expected.
|
|
144
|
+
|
|
145
|
+
## Recommended API Shape
|
|
146
|
+
|
|
147
|
+
### External Object Traversal
|
|
148
|
+
|
|
149
|
+
Keep Ruby-authored traversal and JavaScript-loaded traversal separate.
|
|
150
|
+
|
|
151
|
+
Recommended:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
gltf.scene.traverse_handles do |handle|
|
|
155
|
+
# handle is a JS object in ruby.wasm, or an adapter-provided object in tests.
|
|
156
|
+
end
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
or renderer/backend scoped:
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
renderer.traverse_handles(gltf.scene) do |handle|
|
|
163
|
+
# inspect loaded JS Object3D handles
|
|
164
|
+
end
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Do not make `Object3D#traverse` silently enter the JavaScript subtree. That method currently traverses Ruby-owned `Object3D` instances and should remain predictable on MRI Ruby and ruby.wasm.
|
|
168
|
+
|
|
169
|
+
### External Object Mutability
|
|
170
|
+
|
|
171
|
+
Guard `ExternalObject3D#add`, `#remove`, and `#clear` for now.
|
|
172
|
+
|
|
173
|
+
Reason: the backend currently syncs Ruby child-list changes by clearing and re-adding children on the JavaScript handle. If a user adds Ruby children under an `ExternalObject3D`, a later sync can wipe the loaded glTF children. Until mixed Ruby/loaded children are explicitly designed, this should fail loudly.
|
|
174
|
+
|
|
175
|
+
Recommended behavior:
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
class Three::ExternalObject3D
|
|
179
|
+
def add(*)
|
|
180
|
+
raise NotImplementedError, "ExternalObject3D does not support Ruby child mutation yet"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def remove(*)
|
|
184
|
+
raise NotImplementedError, "ExternalObject3D does not support Ruby child mutation yet"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def clear
|
|
188
|
+
raise NotImplementedError, "ExternalObject3D does not support Ruby child mutation yet"
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Transform properties should continue to work:
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
gltf.scene.position.y = 1
|
|
197
|
+
gltf.scene.scale.set(2, 2, 2)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Resource Disposal
|
|
201
|
+
|
|
202
|
+
Add `dispose_subtree` to renderer/backend instead of putting disposal directly on `GLTF`.
|
|
203
|
+
|
|
204
|
+
Recommended renderer API:
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
renderer.dispose_subtree(
|
|
208
|
+
gltf.scene,
|
|
209
|
+
remove: true,
|
|
210
|
+
dispose_geometries: true,
|
|
211
|
+
dispose_materials: true,
|
|
212
|
+
dispose_textures: true,
|
|
213
|
+
dispose_skeletons: true
|
|
214
|
+
)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Default recommendation:
|
|
218
|
+
|
|
219
|
+
- `remove: true`
|
|
220
|
+
- `dispose_geometries: true`
|
|
221
|
+
- `dispose_materials: true`
|
|
222
|
+
- `dispose_textures: false` at the lowest backend layer
|
|
223
|
+
- `dispose_textures: true` for high-level loaded-asset cleanup helpers
|
|
224
|
+
- `dispose_skeletons: true`, but de-duplicate skeletons
|
|
225
|
+
|
|
226
|
+
Texture disposal must be explicit because textures can be shared. High-level asset cleanup may opt into it because it is normally called when unloading the whole asset.
|
|
227
|
+
|
|
228
|
+
### Resource Collection
|
|
229
|
+
|
|
230
|
+
Disposal should collect unique resources before disposing:
|
|
231
|
+
|
|
232
|
+
- Object roots:
|
|
233
|
+
- optionally remove root from parent
|
|
234
|
+
- Geometries:
|
|
235
|
+
- `object.geometry`
|
|
236
|
+
- Materials:
|
|
237
|
+
- `object.material`
|
|
238
|
+
- arrays of materials
|
|
239
|
+
- Textures:
|
|
240
|
+
- common material slots:
|
|
241
|
+
- `map`
|
|
242
|
+
- `normalMap`
|
|
243
|
+
- `roughnessMap`
|
|
244
|
+
- `metalnessMap`
|
|
245
|
+
- `aoMap`
|
|
246
|
+
- `emissiveMap`
|
|
247
|
+
- `alphaMap`
|
|
248
|
+
- `bumpMap`
|
|
249
|
+
- `displacementMap`
|
|
250
|
+
- `envMap`
|
|
251
|
+
- `lightMap`
|
|
252
|
+
- `specularMap`
|
|
253
|
+
- `anisotropyMap`
|
|
254
|
+
- `clearcoatMap`
|
|
255
|
+
- `clearcoatNormalMap`
|
|
256
|
+
- `clearcoatRoughnessMap`
|
|
257
|
+
- `transmissionMap`
|
|
258
|
+
- `thicknessMap`
|
|
259
|
+
- `iridescenceMap`
|
|
260
|
+
- `iridescenceThicknessMap`
|
|
261
|
+
- `sheenColorMap`
|
|
262
|
+
- `sheenRoughnessMap`
|
|
263
|
+
- `specularColorMap`
|
|
264
|
+
- `specularIntensityMap`
|
|
265
|
+
- Skeletons:
|
|
266
|
+
- `object.skeleton`
|
|
267
|
+
- Scene resources when disposing a `Scene` or when explicitly requested:
|
|
268
|
+
- `scene.background`
|
|
269
|
+
- `scene.environment`
|
|
270
|
+
|
|
271
|
+
Do not rely on `renderer.info.memory` reaching zero as a strict assertion. three.js may keep internal resources for backgrounds, environments, and other renderer internals.
|
|
272
|
+
|
|
273
|
+
## Backend Boundary
|
|
274
|
+
|
|
275
|
+
Prefer implementing loaded-asset traversal/disposal behind adapter methods:
|
|
276
|
+
|
|
277
|
+
```ruby
|
|
278
|
+
def dispose_subtree(object, **options)
|
|
279
|
+
handle = materialize(object)
|
|
280
|
+
@adapter.dispose_object3d_subtree(handle, **options)
|
|
281
|
+
@handles.delete(object.uuid) if object.respond_to?(:uuid)
|
|
282
|
+
handle
|
|
283
|
+
end
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Adapter responsibilities:
|
|
287
|
+
|
|
288
|
+
- RubyWasmAdapter:
|
|
289
|
+
- use JavaScript `Object3D#traverse`
|
|
290
|
+
- collect JS `Set`s of resources
|
|
291
|
+
- call `parent.remove(object)` when `remove: true`
|
|
292
|
+
- call `dispose()` on collected resources
|
|
293
|
+
- FakeThreeJSAdapter:
|
|
294
|
+
- provide equivalent behavior for hash handles
|
|
295
|
+
- record calls for unit tests
|
|
296
|
+
|
|
297
|
+
This keeps browser-specific JavaScript traversal out of the pure Ruby object model.
|
|
298
|
+
|
|
299
|
+
## Rejected Alternatives
|
|
300
|
+
|
|
301
|
+
### Full Ruby Conversion of glTF
|
|
302
|
+
|
|
303
|
+
Rejected for now.
|
|
304
|
+
|
|
305
|
+
Why:
|
|
306
|
+
|
|
307
|
+
- It would require mirroring too many three.js loader semantics.
|
|
308
|
+
- It risks breaking shared references, skins, animations, extension metadata, and parser associations.
|
|
309
|
+
- It increases maintenance burden without improving the current browser-first MVP.
|
|
310
|
+
|
|
311
|
+
### Make `ExternalObject3D#traverse` Enter the JS Subtree
|
|
312
|
+
|
|
313
|
+
Rejected for now.
|
|
314
|
+
|
|
315
|
+
Why:
|
|
316
|
+
|
|
317
|
+
- It changes the meaning of existing Ruby traversal.
|
|
318
|
+
- It makes behavior runtime-dependent: MRI Ruby cannot traverse JS handles.
|
|
319
|
+
- It hides expensive JS bridge calls behind a familiar pure Ruby method.
|
|
320
|
+
|
|
321
|
+
### Dispose Everything Automatically on Remove
|
|
322
|
+
|
|
323
|
+
Rejected.
|
|
324
|
+
|
|
325
|
+
Why:
|
|
326
|
+
|
|
327
|
+
- three.js explicitly separates removal from disposal.
|
|
328
|
+
- Geometry, material, texture, and skeleton resources can be shared.
|
|
329
|
+
- Automatic disposal would surprise users and could break reused assets.
|
|
330
|
+
|
|
331
|
+
## Implementation Plan
|
|
332
|
+
|
|
333
|
+
1. Guard `ExternalObject3D#add`, `#remove`, and `#clear`.
|
|
334
|
+
2. Add backend adapter support for traversing an external JS `Object3D` handle.
|
|
335
|
+
3. Add resource collection in `RubyWasmAdapter`.
|
|
336
|
+
4. Add `Three::Backends::ThreeJS#dispose_subtree`.
|
|
337
|
+
5. Add `Three::Renderers::ThreeJSRenderer#dispose_subtree`.
|
|
338
|
+
6. Add unit tests with `FakeThreeJSAdapter`.
|
|
339
|
+
7. Add browser smoke coverage to `examples/browser/gltf/smoke_test.mjs`:
|
|
340
|
+
- attach disposal listeners to loaded geometry/material
|
|
341
|
+
- call `renderer.dispose_subtree(gltf.scene, remove: true, dispose_textures: true)`
|
|
342
|
+
- assert geometry/material dispose events fired
|
|
343
|
+
- assert root was removed from parent
|
|
344
|
+
8. Update `docs/implementation-plan.md` current status and next tasks.
|
|
345
|
+
|
|
346
|
+
## Suggested First Patch
|
|
347
|
+
|
|
348
|
+
Implement only the guard first:
|
|
349
|
+
|
|
350
|
+
```ruby
|
|
351
|
+
class Three::ExternalObject3D < Object3D
|
|
352
|
+
def add(*)
|
|
353
|
+
raise NotImplementedError, "ExternalObject3D does not support Ruby child mutation yet"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def remove(*)
|
|
357
|
+
raise NotImplementedError, "ExternalObject3D does not support Ruby child mutation yet"
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def clear
|
|
361
|
+
raise NotImplementedError, "ExternalObject3D does not support Ruby child mutation yet"
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Tests:
|
|
367
|
+
|
|
368
|
+
```sh
|
|
369
|
+
ruby -Itest test/three/objects/external_object3d_test.rb
|
|
370
|
+
bundle exec rake test
|
|
371
|
+
pnpm test:browser:gltf
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Validation Commands
|
|
375
|
+
|
|
376
|
+
After implementing traversal/disposal helpers, run:
|
|
377
|
+
|
|
378
|
+
```sh
|
|
379
|
+
bundle exec rake test
|
|
380
|
+
pnpm test:browser
|
|
381
|
+
pnpm audit --audit-level moderate
|
|
382
|
+
pnpm audit signatures
|
|
383
|
+
gem build three-rb.gemspec --output /private/tmp/three-rb-check.gem
|
|
384
|
+
git diff --check
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
If the browser smoke tests fail with a local server sandbox error, rerun the same pnpm command with the approved escalated prefix.
|
|
388
|
+
|
|
389
|
+
## Resume Checklist
|
|
390
|
+
|
|
391
|
+
If work resumes from this document alone:
|
|
392
|
+
|
|
393
|
+
1. Read the status snapshot and decision sections above before editing code.
|
|
394
|
+
2. Confirm the repository state with `git status --short --branch`.
|
|
395
|
+
3. Start with the implementation plan in order unless the current code already includes some steps.
|
|
396
|
+
4. Keep `Object3D#traverse` Ruby-only.
|
|
397
|
+
5. Keep loaded glTF internals opaque unless a later design explicitly changes that.
|
|
398
|
+
6. Add disposal behavior behind backend/adapter APIs, not directly inside the pure Ruby object graph.
|
|
399
|
+
7. Use pnpm-managed browser commands for browser verification.
|
|
400
|
+
8. Update this document if implementation results force a design change.
|
data/docs/next-work.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Next Work
|
|
2
|
+
|
|
3
|
+
This document is the resume point for the next implementation session. It is intentionally narrower than `docs/implementation-plan.md`: use it to decide what to do next after conversation context is lost.
|
|
4
|
+
|
|
5
|
+
Last updated: 2026-05-14.
|
|
6
|
+
|
|
7
|
+
## Current Position
|
|
8
|
+
|
|
9
|
+
The project is past the original MVP and is in Phase 8, renderer maturity. The `0.1.0` browser-first alpha target is implemented and release-gate ready; the remaining release work is manual metadata finalization and publishing after owner confirmation.
|
|
10
|
+
|
|
11
|
+
Recent completed work:
|
|
12
|
+
|
|
13
|
+
- `MeshPhysicalMaterial` support.
|
|
14
|
+
- `RGBELoader` / `RGBETexture` support.
|
|
15
|
+
- `DRACOLoader` integration through `GLTFLoader#draco_decoder_path`.
|
|
16
|
+
- Initial postprocessing support with `EffectComposer`, `RenderPass`, and `UnrealBloomPass`.
|
|
17
|
+
- Release readiness checks, gem install smoke, release preflight, and publishing documentation.
|
|
18
|
+
- Saved JSON export/load fixture regression coverage for `Three::Exporters::ThreeJSONExporter` and `Three::Loaders::ThreeJSONLoader`.
|
|
19
|
+
- Browser examples overview and smoke command map for cube, composition, textures, cubemap, glTF, serialization, picking, primitives, and postprocessing.
|
|
20
|
+
- Browser runtime guide documenting the current ruby.wasm, import-map, `globalThis.THREE`, and `Three::Renderers::ThreeJSRenderer` boot contract.
|
|
21
|
+
- `Three::Postprocessing::OutputPass` support in the postprocessing composer example and browser smoke test.
|
|
22
|
+
- `MeshMatcapMaterial` support with matcap texture-slot sync, JSON export/load, resource disposal, and texture browser smoke coverage.
|
|
23
|
+
- `ShadowMaterial` support with backend sync, JSON export/load, and composition browser smoke coverage.
|
|
24
|
+
- `MeshToonMaterial` support with gradient-map texture-slot sync, JSON export/load, resource disposal, and texture browser smoke coverage.
|
|
25
|
+
- `Three::Postprocessing::DotScreenPass` support with composer integration, uniform update coverage, browser runtime boot contract updates, and postprocessing browser smoke coverage.
|
|
26
|
+
- `Sprite` / `SpriteMaterial` support with textured billboard marker sync, JSON export/load, resource disposal, and primitives browser smoke coverage.
|
|
27
|
+
- Saved JSON fixture coverage now includes `Sprite` / `SpriteMaterial` so the representative exporter/loader regression fixture matches the current primitive surface.
|
|
28
|
+
- Completed public API and documentation consistency pass for the current browser-first alpha scope.
|
|
29
|
+
- Local release gate and latest `main` CI are green for the `0.1.0` release candidate.
|
|
30
|
+
|
|
31
|
+
Do not start Phase 9 native renderer work yet. The implementation plan still recommends keeping browser rendering delegated to three.js through ruby.wasm until the browser-first API is more stable.
|
|
32
|
+
|
|
33
|
+
## Recommended Next Task
|
|
34
|
+
|
|
35
|
+
Keep the `0.1.0` release candidate current while the release is deferred. Publish only after the release owner confirms the date and RubyGems credentials.
|
|
36
|
+
|
|
37
|
+
This is the best next step because:
|
|
38
|
+
|
|
39
|
+
- The representative JSON fixture covers the current primitive/material surface, including `Sprite` / `SpriteMaterial`.
|
|
40
|
+
- The public docs now cover release readiness, publishing, browser example coverage, browser runtime boot contract, and the current implemented API scope.
|
|
41
|
+
- `lib/three/version.rb` already contains `0.1.0`.
|
|
42
|
+
- `CHANGELOG.md` intentionally remains `## 0.1.0 - Unreleased` until the owner confirms the release date, but its contents should continue to reflect the current release-candidate surface.
|
|
43
|
+
- Further feature scope should wait until after the `0.1.0` release unless an already-advertised example breaks.
|
|
44
|
+
|
|
45
|
+
## Scope
|
|
46
|
+
|
|
47
|
+
For deferred-release maintenance, keep `CHANGELOG.md`, `docs/release-readiness.md`, and this resume point aligned with the implemented release-candidate surface. Do not date the changelog, tag, push, or publish until the release owner confirms the release window.
|
|
48
|
+
|
|
49
|
+
When the owner is ready to publish, follow `docs/publishing.md`: confirm the release date, update `CHANGELOG.md`, run `bundle exec rake release:preflight`, commit the metadata update, publish the gem, tag `v0.1.0`, push `main` and the tag, then verify installation from RubyGems.
|
|
50
|
+
|
|
51
|
+
After the release is published, pick one feature target and keep the change small enough to verify through one browser example.
|
|
52
|
+
|
|
53
|
+
Candidate targets, in recommended order when there is no stronger product signal:
|
|
54
|
+
|
|
55
|
+
1. Another small primitive or material workflow only when it reuses existing object/material parameter, JSON, backend sync, and browser smoke patterns. After `Sprite` / `SpriteMaterial`, prefer this only for a concrete example gap rather than API breadth.
|
|
56
|
+
2. Another small postprocessing pass only when it can extend `examples/browser/postprocessing` without adding render targets. After `DotScreenPass`, prefer this only for a specific visual workflow rather than pass count.
|
|
57
|
+
3. Render target support, but only with a focused example that proves why it is needed.
|
|
58
|
+
4. A new addon loader only when a committed fixture requires it.
|
|
59
|
+
5. KTX2 loader after texture-compression fixture coverage and decoder-path handling are planned.
|
|
60
|
+
|
|
61
|
+
## Suggested Implementation Plan
|
|
62
|
+
|
|
63
|
+
1. While release is deferred, keep the unreleased changelog and release-readiness docs current without changing the version, date, tag, or published artifact.
|
|
64
|
+
2. When release is confirmed, finalize `CHANGELOG.md` with the confirmed release date.
|
|
65
|
+
3. Run `bundle exec rake release:preflight`.
|
|
66
|
+
4. Commit the release metadata with a message that does not include co-author trailers.
|
|
67
|
+
5. Publish and tag using `docs/publishing.md`.
|
|
68
|
+
6. After publishing, start the next feature from a user-visible workflow and choose exactly one feature target.
|
|
69
|
+
7. Add Ruby API coverage, fake adapter/backend tests, JSON export/load coverage when the object is serializable, and resource-disposal coverage when it owns GPU resources.
|
|
70
|
+
8. Add or extend one browser example and keep `examples/browser/README.md` in sync.
|
|
71
|
+
9. Add or update a deterministic Playwright smoke command in `package.json`.
|
|
72
|
+
10. Run Ruby tests, the affected browser smoke test, `bundle exec rake release:gem_smoke`, and `bundle exec rake release:preflight` before release work.
|
|
73
|
+
|
|
74
|
+
## Acceptance Criteria
|
|
75
|
+
|
|
76
|
+
- `bundle exec rake test` passes.
|
|
77
|
+
- The chosen feature has one dedicated or clearly extended browser example.
|
|
78
|
+
- `examples/browser/README.md` documents the new coverage.
|
|
79
|
+
- `package.json` has a matching `test:browser:*` command when a new example is added.
|
|
80
|
+
- The affected browser smoke test passes.
|
|
81
|
+
- `bundle exec rake release:gem_smoke` still passes.
|
|
82
|
+
|
|
83
|
+
Optional after the next feature task:
|
|
84
|
+
|
|
85
|
+
- Run `bundle exec rake release:preflight` before release work or when browser/runtime code changed.
|
|
86
|
+
- Run `pnpm benchmark:browser` only if synchronization or renderer internals changed.
|
|
87
|
+
|
|
88
|
+
## What Not To Do Next
|
|
89
|
+
|
|
90
|
+
Do not prioritize these without a clear product need:
|
|
91
|
+
|
|
92
|
+
- KTX2 loader.
|
|
93
|
+
- Additional postprocessing passes.
|
|
94
|
+
- Render target API.
|
|
95
|
+
- WebGPU renderer.
|
|
96
|
+
- Native renderer.
|
|
97
|
+
- Broad public API documentation beyond the current README, release readiness, implementation plan, browser examples overview, and browser runtime guide.
|
|
98
|
+
|
|
99
|
+
Those are valid later tasks, but they expand feature scope. The immediate gap is keeping the deferred release candidate accurate, then getting final release-owner confirmation and publishing before choosing future feature work by visible workflow and smoke-testability instead of API breadth.
|
|
100
|
+
|
|
101
|
+
## After This Task
|
|
102
|
+
|
|
103
|
+
After the next feature task, reassess in this order:
|
|
104
|
+
|
|
105
|
+
1. Keep the release gate passing after each change.
|
|
106
|
+
2. Decide whether the new example reveals a natural follow-up feature.
|
|
107
|
+
3. Delay Phase 9 native renderer work until the browser-first API is stable enough to justify a second renderer target.
|
data/docs/publishing.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Publishing
|
|
2
|
+
|
|
3
|
+
This document records the manual steps for publishing `three-rb`. Do not run the publish step until the release owner has confirmed the version, changelog date, and RubyGems credentials.
|
|
4
|
+
|
|
5
|
+
## Preflight
|
|
6
|
+
|
|
7
|
+
Start from a clean `main` branch:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
git status --short
|
|
11
|
+
pnpm install --frozen-lockfile --ignore-scripts
|
|
12
|
+
pnpm audit --audit-level moderate
|
|
13
|
+
pnpm audit signatures
|
|
14
|
+
pnpm exec playwright install chromium
|
|
15
|
+
bundle exec rake release:preflight
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`release:preflight` runs Ruby tests, builds and installs the gem into a temporary `GEM_HOME`, runs the install smoke test, runs all browser smoke tests, and builds the gem. It does not publish anything.
|
|
19
|
+
|
|
20
|
+
## Prepare Release Metadata
|
|
21
|
+
|
|
22
|
+
For version `0.1.0`:
|
|
23
|
+
|
|
24
|
+
1. Confirm `lib/three/version.rb` contains `VERSION = "0.1.0"`.
|
|
25
|
+
2. Change `CHANGELOG.md` from `## 0.1.0 - Unreleased` to the release date.
|
|
26
|
+
3. Run `bundle exec rake release:preflight` again.
|
|
27
|
+
4. Commit the metadata update:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
git add CHANGELOG.md lib/three/version.rb
|
|
31
|
+
git commit -m "Prepare 0.1.0 release"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Skip `lib/three/version.rb` in the commit if the version was already correct.
|
|
35
|
+
|
|
36
|
+
## Publish
|
|
37
|
+
|
|
38
|
+
Publishing requires RubyGems credentials with MFA enabled.
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
gem build three-rb.gemspec
|
|
42
|
+
gem push three-rb-0.1.0.gem
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
After RubyGems accepts the gem, create and push the git tag:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
git tag -a v0.1.0 -m "Release 0.1.0"
|
|
49
|
+
git push origin main
|
|
50
|
+
git push origin v0.1.0
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
If `gem push` fails, do not create the tag until the published artifact is confirmed.
|
|
54
|
+
|
|
55
|
+
## Post-publish
|
|
56
|
+
|
|
57
|
+
Verify the public install path from a clean temporary directory:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
gem install three-rb -v 0.1.0
|
|
61
|
+
ruby -e 'require "three"; puts Three::VERSION'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Then update issue tracker or release notes with the published RubyGems URL.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Release Readiness
|
|
2
|
+
|
|
3
|
+
This document defines the quality gate for publishing the first public `three-rb` release.
|
|
4
|
+
|
|
5
|
+
## Current Position
|
|
6
|
+
|
|
7
|
+
The project is past the original MVP. It is now in Phase 8, renderer maturity: core Ruby scene authoring, browser rendering, JSON export/load, interaction, asset loading, instancing, picking, shadows, and initial postprocessing are implemented and covered by unit or browser smoke tests.
|
|
8
|
+
|
|
9
|
+
The first public release should be positioned as browser-first alpha. The stable promise is narrow: users can build Ruby-authored scenes and render them in a browser through ruby.wasm and the delegated three.js backend. Native rendering and full three.js API compatibility are not part of the first release.
|
|
10
|
+
|
|
11
|
+
## 0.1.0 Target and Completion Definition
|
|
12
|
+
|
|
13
|
+
The current release plan targets `0.1.0` as the first public browser-first alpha. This is not a `1.0` stable API commitment. A future `1.0` should get a separate stability plan covering compatibility guarantees, deprecation policy, support windows, and any non-browser renderer commitment.
|
|
14
|
+
|
|
15
|
+
The `0.1.0` target is complete when all of these are true:
|
|
16
|
+
|
|
17
|
+
- Scope is frozen to the browser-first alpha surface in this document and `README.md`; no additional material, loader, render-target, postprocessing, WebGPU, XR, native-renderer, or broad compatibility work is required for the release unless an already-advertised example is broken.
|
|
18
|
+
- Public API documentation matches the implemented, tested Ruby API surface loaded by `require "three"`.
|
|
19
|
+
- Snake-case Ruby methods are the documented API style. Broad camelCase three.js compatibility aliases are deferred until after `0.1.0`.
|
|
20
|
+
- `Three::Exporters::ThreeJSONExporter` and `Three::Loaders::ThreeJSONLoader` continue to round-trip Ruby-authored scenes covered by the saved `test/fixtures/scene_export_v1.json` regression fixture.
|
|
21
|
+
- JavaScript-loaded assets remain opaque `ExternalObject3D` roots for `0.1.0`; transform-level use, renderer traversal helpers, animation mixer usage, and explicit subtree disposal are in scope, while Ruby child mutation inside loaded external roots is out of scope.
|
|
22
|
+
- Every browser example advertised in `README.md` and `examples/browser/README.md` has a matching deterministic Playwright smoke command, and `pnpm test:browser` runs them all.
|
|
23
|
+
- The required gate below passes locally before tagging, and the CI workflow passes on the release commit.
|
|
24
|
+
- Release metadata is final: `lib/three/version.rb` contains `0.1.0`, `CHANGELOG.md` has either the unreleased heading during development or the final release date before publishing, and `docs/publishing.md` has the manual publish steps.
|
|
25
|
+
|
|
26
|
+
## Public Scope
|
|
27
|
+
|
|
28
|
+
Included in the first public scope:
|
|
29
|
+
|
|
30
|
+
- Ruby object model for scenes, groups, transforms, cameras, lights, geometries, non-mesh primitives including sprites, common materials including matcap, toon, sprite, and shadow materials, textures, and common math primitives.
|
|
31
|
+
- Browser rendering through `Three::Renderers::ThreeJSRenderer`, ruby.wasm, and `three@0.184.0`.
|
|
32
|
+
- Dirty-tracked synchronization from Ruby objects into three.js handles.
|
|
33
|
+
- JSON export/load for Ruby-authored scenes.
|
|
34
|
+
- JavaScript-delegated browser integrations for textures, cube maps, RGBE environment maps, glTF, DRACO, animation mixers, OrbitControls, raycasting, instancing, shadows, and initial postprocessing with composer/render/bloom/dot-screen/output passes.
|
|
35
|
+
- Browser examples and smoke tests that verify visible rendering paths.
|
|
36
|
+
|
|
37
|
+
Explicitly out of scope for the first public scope:
|
|
38
|
+
|
|
39
|
+
- A native Ruby renderer.
|
|
40
|
+
- Full three.js API coverage.
|
|
41
|
+
- Compatibility aliases for every camelCase three.js API.
|
|
42
|
+
- Broad addon coverage beyond examples that are already tested.
|
|
43
|
+
- WebGPU, WebXR, node materials, editor integration, physics, and production asset pipeline guarantees.
|
|
44
|
+
|
|
45
|
+
## Required Gate
|
|
46
|
+
|
|
47
|
+
Run these before publishing or tagging:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
pnpm install --frozen-lockfile --ignore-scripts
|
|
51
|
+
pnpm audit --audit-level moderate
|
|
52
|
+
pnpm audit signatures
|
|
53
|
+
pnpm exec playwright install chromium
|
|
54
|
+
bundle exec rake release:preflight
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Optional but recommended when renderer internals change:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
pnpm benchmark:browser
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Release Criteria
|
|
64
|
+
|
|
65
|
+
The release is acceptable when:
|
|
66
|
+
|
|
67
|
+
- The required gate passes locally and in CI.
|
|
68
|
+
- `CHANGELOG.md` describes the release as unreleased or tagged with the final date.
|
|
69
|
+
- README documents the browser-first alpha scope and unsupported areas.
|
|
70
|
+
- `bundle exec rake release:gem_smoke` proves the built gem can be installed into a temporary `GEM_HOME` and used without the repository `lib/` path.
|
|
71
|
+
- `bundle exec rake release:preflight` proves the Ruby tests, install smoke, browser smoke tests, and gem build pass without publishing.
|
|
72
|
+
- Browser smoke tests cover every advertised browser example.
|
|
73
|
+
|
|
74
|
+
## Recommended Next Work
|
|
75
|
+
|
|
76
|
+
Before expanding feature scope, prefer:
|
|
77
|
+
|
|
78
|
+
1. Keep release checks fast and deterministic.
|
|
79
|
+
2. Keep fixture-based JSON export/load regression tests current.
|
|
80
|
+
3. Keep `CHANGELOG.md` accurate while it remains under the `0.1.0 - Unreleased` heading.
|
|
81
|
+
4. Improve public docs around browser examples, browser boot, and unsupported APIs; see `docs/next-work.md`.
|
|
82
|
+
5. Add new material classes, postprocessing passes, render targets, or loaders only when a dedicated example and smoke test need them.
|
|
83
|
+
6. Treat KTX2, WebGPU, WebXR, and native rendering as post-0.1 planning items.
|