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,54 @@
|
|
|
1
|
+
# Browser Examples
|
|
2
|
+
|
|
3
|
+
These examples are the browser-facing coverage map for three-rb's browser-first alpha. They run Ruby through ruby.wasm, load pnpm-managed three.js packages, and render through `Three::Renderers::ThreeJSRenderer`.
|
|
4
|
+
|
|
5
|
+
## Run Examples
|
|
6
|
+
|
|
7
|
+
Install browser dependencies and serve the repository root:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm install
|
|
11
|
+
ruby -run -e httpd . -p 8000
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Open an example URL:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
http://localhost:8000/examples/browser/cube/
|
|
18
|
+
http://localhost:8000/examples/browser/composition/
|
|
19
|
+
http://localhost:8000/examples/browser/textures/
|
|
20
|
+
http://localhost:8000/examples/browser/cubemap/
|
|
21
|
+
http://localhost:8000/examples/browser/gltf/
|
|
22
|
+
http://localhost:8000/examples/browser/serialization/
|
|
23
|
+
http://localhost:8000/examples/browser/picking/
|
|
24
|
+
http://localhost:8000/examples/browser/primitives/
|
|
25
|
+
http://localhost:8000/examples/browser/postprocessing/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Smoke Tests
|
|
29
|
+
|
|
30
|
+
Run all browser smoke tests:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
pnpm install
|
|
34
|
+
pnpm exec playwright install chromium
|
|
35
|
+
pnpm test:browser
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Run one smoke test by using the command listed in the table below.
|
|
39
|
+
|
|
40
|
+
| Example | Primary APIs covered | Smoke command | Why it exists |
|
|
41
|
+
| --- | --- | --- | --- |
|
|
42
|
+
| `examples/browser/cube/` | `Scene`, `PerspectiveCamera`, `BoxGeometry`, `Mesh`, `MeshBasicMaterial`, `ThreeJSRenderer`, animation loop | `pnpm test:browser:cube` | Verifies the smallest Ruby-authored scene can boot through ruby.wasm and draw nonblank WebGL pixels through the three.js renderer path. |
|
|
43
|
+
| `examples/browser/composition/` | `OrthographicCamera`, ambient/directional/point/hemisphere lights, shadows, `ShadowMaterial`, `Group`, `InstancedMesh`, `TextureLoader`, `OrbitControls`, material/texture disposal | `pnpm test:browser:composition` | Exercises the broad scene-composition path used by richer browser scenes, including dynamic material updates and camera controls. |
|
|
44
|
+
| `examples/browser/textures/` | `TextureLoader`, `RGBELoader`, repeat/wrap/filter/UV-transform settings, `MeshPhysicalMaterial`, `MeshMatcapMaterial`, `MeshToonMaterial`, physical, matcap, and toon texture slots, scene environment | `pnpm test:browser:textures` | Verifies browser texture loading, HDR environment synchronization, and the current material texture bridge. |
|
|
45
|
+
| `examples/browser/cubemap/` | `CubeTextureLoader`, `CubeTexture`, scene `background`, scene `environment`, reflective `MeshStandardMaterial` | `pnpm test:browser:cubemap` | Keeps cubemap background/environment behavior covered separately from ordinary 2D texture loading. |
|
|
46
|
+
| `examples/browser/gltf/` | `GLTFLoader`, `DRACOLoader` decoder path, loaded external scenes, `AnimationMixer`, `Clock`, loaded subtree disposal | `pnpm test:browser:gltf` | Verifies that external assets can be loaded, animated, attached to Ruby scenes, and disposed through the renderer API. |
|
|
47
|
+
| `examples/browser/serialization/` | `ThreeJSONExporter`, `ThreeJSONLoader`, deterministic ids, shared geometry/material/texture resources, loaded scene rendering | `pnpm test:browser:serialization` | Confirms exported Ruby scenes round-trip through JSON and render after loading. |
|
|
48
|
+
| `examples/browser/picking/` | `Raycaster`, pointer-to-camera coordinates, intersection mapping back to Ruby `Object3D`, selected material updates | `pnpm test:browser:picking` | Verifies browser event coordinates can drive Ruby-side picking and mutate rendered objects. |
|
|
49
|
+
| `examples/browser/primitives/` | `BufferGeometry`, `Float32BufferAttribute`, `Line`, `Points`, `Sprite`, `LineBasicMaterial`, `PointsMaterial`, `SpriteMaterial` | `pnpm test:browser:primitives` | Covers non-`Mesh` primitive rendering, generic buffer attribute synchronization, and textured billboard markers. |
|
|
50
|
+
| `examples/browser/postprocessing/` | `EffectComposer`, `RenderPass`, `UnrealBloomPass`, `DotScreenPass`, `OutputPass`, composer sizing, pass property/uniform updates, `composer.render` | `pnpm test:browser:postprocessing` | Verifies the explicit postprocessing render path stays separate from direct renderer rendering and remains smoke-tested. |
|
|
51
|
+
|
|
52
|
+
## Adding Browser Coverage
|
|
53
|
+
|
|
54
|
+
New browser-facing features should add or extend one of these examples and include deterministic smoke coverage. Prefer extending an existing example when the feature strengthens the same workflow; add a new example when it introduces a distinct API surface such as a new loader family, render target workflow, or postprocessing pipeline.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"asset": {
|
|
3
|
+
"version": "2.0",
|
|
4
|
+
"generator": "three-rb animated smoke asset"
|
|
5
|
+
},
|
|
6
|
+
"scene": 0,
|
|
7
|
+
"scenes": [
|
|
8
|
+
{
|
|
9
|
+
"nodes": [0]
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"nodes": [
|
|
13
|
+
{
|
|
14
|
+
"name": "AnimatedRubyTriangle",
|
|
15
|
+
"mesh": 0,
|
|
16
|
+
"rotation": [0, 0, 0, 1]
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"meshes": [
|
|
20
|
+
{
|
|
21
|
+
"name": "AnimatedRubyTriangleMesh",
|
|
22
|
+
"primitives": [
|
|
23
|
+
{
|
|
24
|
+
"attributes": {
|
|
25
|
+
"POSITION": 0
|
|
26
|
+
},
|
|
27
|
+
"material": 0
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"materials": [
|
|
33
|
+
{
|
|
34
|
+
"name": "AnimatedRubyTriangleMaterial",
|
|
35
|
+
"pbrMetallicRoughness": {
|
|
36
|
+
"baseColorFactor": [0.35, 0.78, 1.0, 1.0],
|
|
37
|
+
"metallicFactor": 0.0,
|
|
38
|
+
"roughnessFactor": 0.45
|
|
39
|
+
},
|
|
40
|
+
"doubleSided": true
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"animations": [
|
|
44
|
+
{
|
|
45
|
+
"name": "TriangleSpin",
|
|
46
|
+
"channels": [
|
|
47
|
+
{
|
|
48
|
+
"sampler": 0,
|
|
49
|
+
"target": {
|
|
50
|
+
"node": 0,
|
|
51
|
+
"path": "rotation"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"samplers": [
|
|
56
|
+
{
|
|
57
|
+
"input": 1,
|
|
58
|
+
"output": 2,
|
|
59
|
+
"interpolation": "LINEAR"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"buffers": [
|
|
65
|
+
{
|
|
66
|
+
"uri": "data:application/octet-stream;base64,AAAAAAAAgD8AAAAAAACAvwAAgL8AAAAAAACAPwAAgL8AAAAA",
|
|
67
|
+
"byteLength": 36
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"uri": "data:application/octet-stream;base64,AAAAAAAAAEA=",
|
|
71
|
+
"byteLength": 8
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAIA/AAAAAAAAAAA=",
|
|
75
|
+
"byteLength": 32
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"bufferViews": [
|
|
79
|
+
{
|
|
80
|
+
"buffer": 0,
|
|
81
|
+
"byteOffset": 0,
|
|
82
|
+
"byteLength": 36,
|
|
83
|
+
"target": 34962
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"buffer": 1,
|
|
87
|
+
"byteOffset": 0,
|
|
88
|
+
"byteLength": 8
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"buffer": 2,
|
|
92
|
+
"byteOffset": 0,
|
|
93
|
+
"byteLength": 32
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"accessors": [
|
|
97
|
+
{
|
|
98
|
+
"bufferView": 0,
|
|
99
|
+
"byteOffset": 0,
|
|
100
|
+
"componentType": 5126,
|
|
101
|
+
"count": 3,
|
|
102
|
+
"type": "VEC3",
|
|
103
|
+
"min": [-1.0, -1.0, 0.0],
|
|
104
|
+
"max": [1.0, 1.0, 0.0]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"bufferView": 1,
|
|
108
|
+
"byteOffset": 0,
|
|
109
|
+
"componentType": 5126,
|
|
110
|
+
"count": 2,
|
|
111
|
+
"type": "SCALAR",
|
|
112
|
+
"min": [0.0],
|
|
113
|
+
"max": [2.0]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"bufferView": 2,
|
|
117
|
+
"byteOffset": 0,
|
|
118
|
+
"componentType": 5126,
|
|
119
|
+
"count": 2,
|
|
120
|
+
"type": "VEC4"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
|
|
2
|
+
<rect width="128" height="128" fill="#eef3f7"/>
|
|
3
|
+
<rect width="32" height="32" fill="#22313f"/>
|
|
4
|
+
<rect x="64" width="32" height="32" fill="#22313f"/>
|
|
5
|
+
<rect x="32" y="32" width="32" height="32" fill="#6ed69a"/>
|
|
6
|
+
<rect x="96" y="32" width="32" height="32" fill="#6ed69a"/>
|
|
7
|
+
<rect y="64" width="32" height="32" fill="#22313f"/>
|
|
8
|
+
<rect x="64" y="64" width="32" height="32" fill="#22313f"/>
|
|
9
|
+
<rect x="32" y="96" width="32" height="32" fill="#6ed69a"/>
|
|
10
|
+
<rect x="96" y="96" width="32" height="32" fill="#6ed69a"/>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"asset": {
|
|
3
|
+
"version": "2.0",
|
|
4
|
+
"generator": "three-rb draco smoke asset"
|
|
5
|
+
},
|
|
6
|
+
"extensionsUsed": ["KHR_draco_mesh_compression"],
|
|
7
|
+
"extensionsRequired": ["KHR_draco_mesh_compression"],
|
|
8
|
+
"scene": 0,
|
|
9
|
+
"scenes": [
|
|
10
|
+
{
|
|
11
|
+
"nodes": [0]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"nodes": [
|
|
15
|
+
{
|
|
16
|
+
"name": "CompressedRubyTriangle",
|
|
17
|
+
"mesh": 0
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"meshes": [
|
|
21
|
+
{
|
|
22
|
+
"name": "CompressedRubyTriangleMesh",
|
|
23
|
+
"primitives": [
|
|
24
|
+
{
|
|
25
|
+
"attributes": {
|
|
26
|
+
"POSITION": 0
|
|
27
|
+
},
|
|
28
|
+
"extensions": {
|
|
29
|
+
"KHR_draco_mesh_compression": {
|
|
30
|
+
"bufferView": 0,
|
|
31
|
+
"attributes": {
|
|
32
|
+
"POSITION": 0
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"material": 0
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"materials": [
|
|
42
|
+
{
|
|
43
|
+
"name": "CompressedRubyTriangleMaterial",
|
|
44
|
+
"pbrMetallicRoughness": {
|
|
45
|
+
"baseColorFactor": [1.0, 0.62, 0.28, 1.0],
|
|
46
|
+
"metallicFactor": 0.0,
|
|
47
|
+
"roughnessFactor": 0.35
|
|
48
|
+
},
|
|
49
|
+
"doubleSided": true
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"buffers": [
|
|
53
|
+
{
|
|
54
|
+
"uri": "data:application/octet-stream;base64,RFJBQ08CAgEBAAAAAwEAAQAAAQf/AREB/wAAAQAJAwAAAgEBAQAPA60qL1UVA6B6gUj/HwAAAAAAAAD/PwAAAACAvwAAgL8AAAAAAAAAQA4=",
|
|
55
|
+
"byteLength": 80
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"bufferViews": [
|
|
59
|
+
{
|
|
60
|
+
"buffer": 0,
|
|
61
|
+
"byteOffset": 0,
|
|
62
|
+
"byteLength": 80
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"accessors": [
|
|
66
|
+
{
|
|
67
|
+
"componentType": 5126,
|
|
68
|
+
"count": 3,
|
|
69
|
+
"type": "VEC3",
|
|
70
|
+
"min": [-1.0, -1.0, 0.0],
|
|
71
|
+
"max": [1.0, 1.0, 0.0]
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"asset": {
|
|
3
|
+
"version": "2.0",
|
|
4
|
+
"generator": "three-rb smoke asset"
|
|
5
|
+
},
|
|
6
|
+
"scene": 0,
|
|
7
|
+
"scenes": [
|
|
8
|
+
{
|
|
9
|
+
"nodes": [0]
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"nodes": [
|
|
13
|
+
{
|
|
14
|
+
"name": "RubyTriangle",
|
|
15
|
+
"mesh": 0
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"meshes": [
|
|
19
|
+
{
|
|
20
|
+
"name": "RubyTriangleMesh",
|
|
21
|
+
"primitives": [
|
|
22
|
+
{
|
|
23
|
+
"attributes": {
|
|
24
|
+
"POSITION": 0
|
|
25
|
+
},
|
|
26
|
+
"material": 0
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"materials": [
|
|
32
|
+
{
|
|
33
|
+
"name": "RubyTriangleMaterial",
|
|
34
|
+
"pbrMetallicRoughness": {
|
|
35
|
+
"baseColorFactor": [0.35, 0.78, 1.0, 1.0],
|
|
36
|
+
"metallicFactor": 0.0,
|
|
37
|
+
"roughnessFactor": 0.45
|
|
38
|
+
},
|
|
39
|
+
"doubleSided": true
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"buffers": [
|
|
43
|
+
{
|
|
44
|
+
"uri": "data:application/octet-stream;base64,AAAAAAAAgD8AAAAAAACAvwAAgL8AAAAAAACAPwAAgL8AAAAA",
|
|
45
|
+
"byteLength": 36
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"bufferViews": [
|
|
49
|
+
{
|
|
50
|
+
"buffer": 0,
|
|
51
|
+
"byteOffset": 0,
|
|
52
|
+
"byteLength": 36,
|
|
53
|
+
"target": 34962
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"accessors": [
|
|
57
|
+
{
|
|
58
|
+
"bufferView": 0,
|
|
59
|
+
"byteOffset": 0,
|
|
60
|
+
"componentType": 5126,
|
|
61
|
+
"count": 3,
|
|
62
|
+
"type": "VEC3",
|
|
63
|
+
"min": [-1.0, -1.0, 0.0],
|
|
64
|
+
"max": [1.0, 1.0, 0.0]
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Browser Composition Example
|
|
2
|
+
|
|
3
|
+
This example renders a Ruby-authored scene through ruby.wasm and the JavaScript three.js renderer. It uses ambient, directional, point, and hemisphere lights, directional shadow mapping, a `PlaneGeometry` backdrop and shadow catcher, `SphereGeometry`, grouped child meshes, `TextureLoader` repeat/wrap/filter settings, `MeshLambertMaterial`, `MeshPhongMaterial`, `MeshStandardMaterial`, `MeshNormalMaterial`, `ShadowMaterial`, `OrbitControls`, backend material/texture disposal, and a material color update in the animation loop.
|
|
4
|
+
|
|
5
|
+
Install browser dependencies and serve the repository root over HTTP:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm install
|
|
9
|
+
ruby -run -e httpd . -p 8000
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Then open:
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
http://localhost:8000/examples/browser/composition/
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The repository root must be served, not only this directory, because `boot.mjs` loads browser packages from `node_modules/` and `main.rb` loads the library source from `lib/`.
|
|
19
|
+
|
|
20
|
+
## Browser Smoke Test
|
|
21
|
+
|
|
22
|
+
Install the optional Node dependency and browser binary:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
pnpm install
|
|
26
|
+
pnpm exec playwright install chromium
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Run the browser smoke test:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
pnpm test:browser:composition
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The smoke test serves the repository root, waits for the Ruby scene to reach `Running`, samples the WebGL canvas for nonblank pixels, verifies ambient/directional/point/hemisphere lights, directional shadow mapping, grouped meshes, `PlaneGeometry`, `SphereGeometry`, `TextureLoader` repeat/wrap/filter settings, `MeshLambertMaterial`, `MeshPhongMaterial`, `MeshStandardMaterial`, `MeshNormalMaterial`, `ShadowMaterial`, backend material/texture disposal, and `OrbitControls`, then confirms that material changes and camera drag controls reach the renderer.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>three-rb composition</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
color-scheme: dark;
|
|
10
|
+
font-family:
|
|
11
|
+
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
|
12
|
+
"Segoe UI", sans-serif;
|
|
13
|
+
background: #0f1419;
|
|
14
|
+
color: #eef3f7;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
* {
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
html,
|
|
22
|
+
body {
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 100%;
|
|
25
|
+
margin: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
body {
|
|
29
|
+
display: grid;
|
|
30
|
+
place-items: stretch;
|
|
31
|
+
overflow: hidden;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.viewport {
|
|
35
|
+
position: relative;
|
|
36
|
+
width: 100%;
|
|
37
|
+
height: 100%;
|
|
38
|
+
min-width: 320px;
|
|
39
|
+
min-height: 320px;
|
|
40
|
+
background:
|
|
41
|
+
linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px),
|
|
42
|
+
linear-gradient(90deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px),
|
|
43
|
+
linear-gradient(135deg, #0f1419 0%, #17221e 48%, #151820 100%);
|
|
44
|
+
background-size: 36px 36px, 36px 36px, auto;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
canvas {
|
|
48
|
+
display: block;
|
|
49
|
+
width: 100%;
|
|
50
|
+
height: 100%;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.hud {
|
|
54
|
+
position: absolute;
|
|
55
|
+
top: 16px;
|
|
56
|
+
left: 16px;
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: 8px;
|
|
60
|
+
max-width: calc(100% - 32px);
|
|
61
|
+
padding: 8px 10px;
|
|
62
|
+
border: 1px solid rgba(255, 255, 255, 0.14);
|
|
63
|
+
border-radius: 6px;
|
|
64
|
+
background: rgba(9, 13, 17, 0.72);
|
|
65
|
+
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.24);
|
|
66
|
+
color: #dce7ef;
|
|
67
|
+
font-size: 13px;
|
|
68
|
+
line-height: 1.3;
|
|
69
|
+
backdrop-filter: blur(10px);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.status-dot {
|
|
73
|
+
width: 8px;
|
|
74
|
+
height: 8px;
|
|
75
|
+
flex: 0 0 auto;
|
|
76
|
+
border-radius: 999px;
|
|
77
|
+
background: #f2b84b;
|
|
78
|
+
box-shadow: 0 0 14px rgba(242, 184, 75, 0.72);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.status-dot[data-state="running"] {
|
|
82
|
+
background: #60d394;
|
|
83
|
+
box-shadow: 0 0 14px rgba(96, 211, 148, 0.72);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.status-dot[data-state="error"] {
|
|
87
|
+
background: #f15b5b;
|
|
88
|
+
box-shadow: 0 0 14px rgba(241, 91, 91, 0.72);
|
|
89
|
+
}
|
|
90
|
+
</style>
|
|
91
|
+
<script type="importmap">
|
|
92
|
+
{
|
|
93
|
+
"imports": {
|
|
94
|
+
"@bjorn3/browser_wasi_shim": "/node_modules/@bjorn3/browser_wasi_shim/dist/index.js",
|
|
95
|
+
"@ruby/wasm-wasi/browser": "/node_modules/@ruby/wasm-wasi/dist/esm/browser.js",
|
|
96
|
+
"three": "/node_modules/three/build/three.module.js",
|
|
97
|
+
"three/addons/": "/node_modules/three/examples/jsm/"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
</script>
|
|
101
|
+
<script type="module">
|
|
102
|
+
const status = document.querySelector("#status");
|
|
103
|
+
const statusDot = document.querySelector("#status-dot");
|
|
104
|
+
|
|
105
|
+
globalThis.__threeRbSetStatus = (message, state) => {
|
|
106
|
+
if (status) status.textContent = message;
|
|
107
|
+
if (statusDot) statusDot.dataset.state = state;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
globalThis.__threeRbBootFailed = (message) => {
|
|
111
|
+
globalThis.__threeRbSetStatus(message, "error");
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
globalThis.addEventListener("error", (event) => {
|
|
115
|
+
globalThis.__threeRbBootFailed(event.message || "Browser error");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
globalThis.addEventListener("unhandledrejection", (event) => {
|
|
119
|
+
const reason = event.reason;
|
|
120
|
+
globalThis.__threeRbBootFailed(reason && reason.message ? reason.message : "Ruby boot failed");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
globalThis.__threeRbSetStatus("Loading ruby.wasm", "loading");
|
|
124
|
+
</script>
|
|
125
|
+
<script type="module" src="./boot.mjs"></script>
|
|
126
|
+
</head>
|
|
127
|
+
<body>
|
|
128
|
+
<main class="viewport" id="viewport">
|
|
129
|
+
<canvas id="scene" data-testid="scene-canvas"></canvas>
|
|
130
|
+
<div class="hud" aria-live="polite">
|
|
131
|
+
<span class="status-dot" id="status-dot"></span>
|
|
132
|
+
<span id="status" data-testid="status">Loading ruby.wasm</span>
|
|
133
|
+
</div>
|
|
134
|
+
</main>
|
|
135
|
+
</body>
|
|
136
|
+
</html>
|