three-rb 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +21 -1
- data/README.md +64 -3
- data/docs/browser-runtime.md +90 -24
- data/docs/next-work.md +9 -5
- data/docs/publishing.md +116 -23
- data/docs/release-readiness.md +5 -3
- data/docs/standalone-browser-app.md +102 -0
- data/examples/browser/README.md +6 -0
- data/examples/browser/composition/main.rb +41 -61
- data/examples/browser/cube/main.rb +4 -34
- data/examples/browser/cubemap/main.rb +16 -39
- data/examples/browser/gltf/main.rb +28 -48
- data/examples/browser/picking/main.rb +27 -53
- data/examples/browser/postprocessing/main.rb +23 -42
- data/examples/browser/primitives/main.rb +18 -41
- data/examples/browser/ruby/README.md +22 -0
- data/examples/browser/ruby/boot.mjs +6 -0
- data/examples/browser/ruby/index.html +142 -0
- data/examples/browser/ruby/main.rb +313 -0
- data/examples/browser/ruby/smoke_test.mjs +126 -0
- data/examples/browser/serialization/main.rb +19 -41
- data/examples/browser/shared/boot.mjs +37 -5
- data/examples/browser/textures/main.rb +21 -39
- data/exe/three-rb +55 -0
- data/lib/three/backends/threejs/materialization.rb +6 -0
- data/lib/three/backends/threejs/parameters.rb +17 -0
- data/lib/three/backends/threejs/ruby_wasm_adapter.rb +166 -59
- data/lib/three/backends/threejs/synchronization.rb +38 -4
- data/lib/three/backends/threejs.rb +24 -0
- data/lib/three/browser.rb +389 -0
- data/lib/three/constants.rb +6 -0
- data/lib/three/core/buffer_attribute.rb +5 -1
- data/lib/three/core/buffer_geometry.rb +29 -1
- data/lib/three/core/object3d.rb +39 -1
- data/lib/three/exporters/three_json_exporter.rb +3 -0
- data/lib/three/generators/browser_example.rb +315 -0
- data/lib/three/geometries/text_geometry.rb +41 -0
- data/lib/three/loaders/font_loader.rb +29 -0
- data/lib/three/loaders/three_json_loader.rb +92 -46
- data/lib/three/materials/material.rb +2 -1
- data/lib/three/math/matrix4.rb +27 -0
- data/lib/three/renderers/threejs_renderer.rb +19 -0
- data/lib/three/scenes/fog.rb +86 -0
- data/lib/three/scenes/scene.rb +19 -1
- data/lib/three/textures/texture.rb +2 -1
- data/lib/three/version.rb +1 -1
- data/lib/three.rb +4 -0
- data/package.json +2 -1
- metadata +16 -3
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertCanvasHasDimensions,
|
|
3
|
+
assertNoDiagnostics,
|
|
4
|
+
createDiagnostics,
|
|
5
|
+
createSmokePage,
|
|
6
|
+
loadPlaywright,
|
|
7
|
+
sampleCanvas,
|
|
8
|
+
startServer,
|
|
9
|
+
waitForRunning
|
|
10
|
+
} from "../shared/smoke_test_helpers.mjs";
|
|
11
|
+
|
|
12
|
+
async function redPixelCount(page) {
|
|
13
|
+
return page.evaluate(() => {
|
|
14
|
+
const element = document.querySelector("[data-testid='scene-canvas']");
|
|
15
|
+
const gl = element.getContext("webgl2") || element.getContext("webgl");
|
|
16
|
+
const width = element.width;
|
|
17
|
+
const height = element.height;
|
|
18
|
+
const pixels = new Uint8Array(width * height * 4);
|
|
19
|
+
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
|
|
20
|
+
|
|
21
|
+
let count = 0;
|
|
22
|
+
for (let index = 0; index < pixels.length; index += 4) {
|
|
23
|
+
const red = pixels[index];
|
|
24
|
+
const green = pixels[index + 1];
|
|
25
|
+
const blue = pixels[index + 2];
|
|
26
|
+
if (red > 120 && red > green * 1.18 && red > blue * 1.12) count += 1;
|
|
27
|
+
}
|
|
28
|
+
return count;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function main() {
|
|
33
|
+
const { chromium } = await loadPlaywright();
|
|
34
|
+
const server = await startServer();
|
|
35
|
+
const browser = await chromium.launch({ headless: process.env.HEADLESS !== "0" });
|
|
36
|
+
const diagnostics = createDiagnostics();
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const page = await createSmokePage(browser, diagnostics, { viewport: { width: 1040, height: 680 } });
|
|
40
|
+
|
|
41
|
+
await page.goto(`${server.url}/examples/browser/ruby/`, { waitUntil: "load" });
|
|
42
|
+
await waitForRunning(page, diagnostics);
|
|
43
|
+
await page.waitForTimeout(1_000);
|
|
44
|
+
|
|
45
|
+
const canvas = await sampleCanvas(page);
|
|
46
|
+
assertCanvasHasDimensions(canvas);
|
|
47
|
+
if (canvas.nonBlankPixels === 0) {
|
|
48
|
+
throw new Error(`canvas sample is blank: ${JSON.stringify(canvas)}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const debug = await page.evaluate(() => ({
|
|
52
|
+
frame: globalThis.__threeRbRubyFrame,
|
|
53
|
+
sceneChildren: globalThis.__threeRbScene?.children?.length,
|
|
54
|
+
rubyType: globalThis.__threeRbRubyGem?.type,
|
|
55
|
+
rubyGeometryType: globalThis.__threeRbRubyGeometry?.type,
|
|
56
|
+
rubyPositionCount: globalThis.__threeRbRubyGeometry?.attributes?.position?.count,
|
|
57
|
+
rubyMaterial: {
|
|
58
|
+
type: globalThis.__threeRbRubyMaterial?.type,
|
|
59
|
+
color: globalThis.__threeRbRubyMaterial?.color?.getHex?.(),
|
|
60
|
+
transmission: globalThis.__threeRbRubyMaterial?.transmission,
|
|
61
|
+
thickness: globalThis.__threeRbRubyMaterial?.thickness,
|
|
62
|
+
ior: globalThis.__threeRbRubyMaterial?.ior,
|
|
63
|
+
clearcoat: globalThis.__threeRbRubyMaterial?.clearcoat
|
|
64
|
+
},
|
|
65
|
+
backdropMaterial: {
|
|
66
|
+
type: globalThis.__threeRbRubyBackdropMaterial?.type,
|
|
67
|
+
color: globalThis.__threeRbRubyBackdropMaterial?.color?.getHex?.(),
|
|
68
|
+
transparent: globalThis.__threeRbRubyBackdropMaterial?.transparent,
|
|
69
|
+
opacity: globalThis.__threeRbRubyBackdropMaterial?.opacity
|
|
70
|
+
},
|
|
71
|
+
sparkleCount: globalThis.__threeRbRubySparkles?.length,
|
|
72
|
+
titleType: globalThis.__threeRbRubyTitle?.type,
|
|
73
|
+
titleGeometryType: globalThis.__threeRbRubyTitleGeometry?.type,
|
|
74
|
+
titlePositionCount: globalThis.__threeRbRubyTitleGeometry?.attributes?.position?.count,
|
|
75
|
+
fontLoaded: globalThis.__threeRbRubyFontLoaded,
|
|
76
|
+
renderInfo: globalThis.__threeRbRenderer?.info?.render
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
if (debug.rubyType !== "Mesh" || debug.rubyGeometryType !== "BufferGeometry") {
|
|
80
|
+
throw new Error(`ruby gemstone was not materialized as expected: ${JSON.stringify(debug, null, 2)}`);
|
|
81
|
+
}
|
|
82
|
+
if (debug.rubyPositionCount < 220) {
|
|
83
|
+
throw new Error(`ruby gemstone has too few facet vertices: ${JSON.stringify(debug, null, 2)}`);
|
|
84
|
+
}
|
|
85
|
+
if (debug.rubyMaterial.type !== "MeshPhysicalMaterial" || debug.rubyMaterial.transmission < 0.7) {
|
|
86
|
+
throw new Error(`ruby material is not transmissive: ${JSON.stringify(debug, null, 2)}`);
|
|
87
|
+
}
|
|
88
|
+
if (
|
|
89
|
+
debug.backdropMaterial.type !== "MeshBasicMaterial" ||
|
|
90
|
+
debug.backdropMaterial.color !== 0x58c2ff ||
|
|
91
|
+
!debug.backdropMaterial.transparent ||
|
|
92
|
+
debug.backdropMaterial.opacity > 0.12
|
|
93
|
+
) {
|
|
94
|
+
throw new Error(`ruby backdrop is not a subtle transparent blue: ${JSON.stringify(debug, null, 2)}`);
|
|
95
|
+
}
|
|
96
|
+
if (debug.titleType !== "Mesh" || debug.titleGeometryType !== "TextGeometry" || !debug.fontLoaded) {
|
|
97
|
+
throw new Error(`title text geometry did not load: ${JSON.stringify(debug, null, 2)}`);
|
|
98
|
+
}
|
|
99
|
+
if (debug.sparkleCount < 5) {
|
|
100
|
+
throw new Error(`ruby sparkles did not materialize: ${JSON.stringify(debug, null, 2)}`);
|
|
101
|
+
}
|
|
102
|
+
if (debug.titlePositionCount < 200 || !debug.renderInfo || debug.renderInfo.triangles < 250) {
|
|
103
|
+
throw new Error(`title did not render enough geometry: ${JSON.stringify(debug, null, 2)}`);
|
|
104
|
+
}
|
|
105
|
+
if (debug.frame <= 0) {
|
|
106
|
+
throw new Error(`animation loop did not advance: ${JSON.stringify(debug, null, 2)}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const rubyPixels = await redPixelCount(page);
|
|
110
|
+
if (rubyPixels < 500) {
|
|
111
|
+
throw new Error(`ruby gemstone is not visibly red enough: redPixels=${rubyPixels}\n${JSON.stringify(debug, null, 2)}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
assertNoDiagnostics(diagnostics);
|
|
115
|
+
|
|
116
|
+
console.log(`ruby smoke test passed at ${server.url}/examples/browser/ruby/`);
|
|
117
|
+
} finally {
|
|
118
|
+
await browser.close();
|
|
119
|
+
await new Promise((resolve) => server.instance.close(resolve));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
main().catch((error) => {
|
|
124
|
+
console.error(error);
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
});
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
begin
|
|
6
|
-
JS.global[:__threeReady].await
|
|
7
|
-
|
|
8
|
-
require_relative "../../../lib/three"
|
|
9
|
-
|
|
10
|
-
document = JS.global[:document]
|
|
11
|
-
window = JS.global[:window]
|
|
12
|
-
viewport = document.call(:querySelector, "#viewport")
|
|
13
|
-
status = document.call(:querySelector, "#status")
|
|
14
|
-
status_dot = document.call(:querySelector, "#status-dot")
|
|
15
|
-
status[:textContent] = "Exporting Ruby scene"
|
|
3
|
+
require_relative "../../../lib/three"
|
|
16
4
|
|
|
5
|
+
Three::Browser.run(starting: "Exporting Ruby scene") do |app|
|
|
17
6
|
source_scene = Three::Scene.new
|
|
18
7
|
source_scene.name = "serialization-source"
|
|
19
8
|
|
|
@@ -53,45 +42,34 @@ begin
|
|
|
53
42
|
)
|
|
54
43
|
renderer.set_clear_color(0x101418, 1)
|
|
55
44
|
|
|
56
|
-
|
|
57
|
-
width = [viewport[:clientWidth].to_i, 1].max
|
|
58
|
-
height = [viewport[:clientHeight].to_i, 1].max
|
|
59
|
-
|
|
60
|
-
camera.aspect = width.to_f / height
|
|
61
|
-
camera.update_projection_matrix
|
|
62
|
-
renderer.set_size(width, height)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
resize.call
|
|
66
|
-
window.call(:addEventListener, "resize", resize)
|
|
45
|
+
app.resize_renderer(renderer, camera)
|
|
67
46
|
renderer.render(scene, camera)
|
|
68
47
|
|
|
69
48
|
left = scene.get_object_by_name("loaded-left")
|
|
70
49
|
right = scene.get_object_by_name("loaded-right")
|
|
71
50
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
51
|
+
app.expose(
|
|
52
|
+
{
|
|
53
|
+
renderer: renderer,
|
|
54
|
+
scene: scene,
|
|
55
|
+
camera: camera,
|
|
56
|
+
serialized_json: json,
|
|
57
|
+
loaded_left: left,
|
|
58
|
+
loaded_right: right,
|
|
59
|
+
loaded_shared_geometry: left.geometry.equal?(right.geometry),
|
|
60
|
+
loaded_shared_material: left.material.equal?(right.material),
|
|
61
|
+
loaded_shared_texture: left.material.map.equal?(right.material.map),
|
|
62
|
+
serialization_frame: 0
|
|
63
|
+
},
|
|
64
|
+
renderer: renderer
|
|
65
|
+
)
|
|
82
66
|
|
|
83
67
|
renderer.animation_loop do
|
|
84
|
-
|
|
68
|
+
app.increment(:serialization_frame)
|
|
85
69
|
left.rotation.x += 0.012
|
|
86
70
|
left.rotation.y += 0.018
|
|
87
71
|
right.rotation.x -= 0.01
|
|
88
72
|
right.rotation.y += 0.014
|
|
89
73
|
renderer.render(scene, camera)
|
|
90
74
|
end
|
|
91
|
-
|
|
92
|
-
status[:textContent] = "Running"
|
|
93
|
-
status_dot[:dataset][:state] = "running"
|
|
94
|
-
rescue StandardError => error
|
|
95
|
-
JS.global.call(:__threeRbBootFailed, error.message) if JS.global[:__threeRbBootFailed]
|
|
96
|
-
raise
|
|
97
75
|
end
|
|
@@ -3,6 +3,8 @@ import * as THREE from "three";
|
|
|
3
3
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
4
4
|
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
|
|
5
5
|
import { HDRLoader } from "three/addons/loaders/HDRLoader.js";
|
|
6
|
+
import { FontLoader } from "three/addons/loaders/FontLoader.js";
|
|
7
|
+
import { TextGeometry } from "three/addons/geometries/TextGeometry.js";
|
|
6
8
|
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
|
7
9
|
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
|
|
8
10
|
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
|
|
@@ -20,6 +22,8 @@ export async function bootRubyExample({ main, clearColor }) {
|
|
|
20
22
|
globalThis.THREE_GLTF_LOADER = GLTFLoader;
|
|
21
23
|
globalThis.THREE_DRACO_LOADER = DRACOLoader;
|
|
22
24
|
globalThis.THREE_RGBE_LOADER = HDRLoader;
|
|
25
|
+
globalThis.THREE_FONT_LOADER = FontLoader;
|
|
26
|
+
globalThis.THREE_TEXT_GEOMETRY = TextGeometry;
|
|
23
27
|
globalThis.THREE_ORBIT_CONTROLS = OrbitControls;
|
|
24
28
|
globalThis.THREE_EFFECT_COMPOSER = EffectComposer;
|
|
25
29
|
globalThis.THREE_RENDER_PASS = RenderPass;
|
|
@@ -50,17 +54,45 @@ export async function bootRubyExample({ main, clearColor }) {
|
|
|
50
54
|
globalThis.rubyVM = vm;
|
|
51
55
|
|
|
52
56
|
setStatus("Starting Ruby VM", "loading");
|
|
53
|
-
await
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
await withNoStoreRubySourceFetch(async () => {
|
|
58
|
+
await vm.evalAsync(`
|
|
59
|
+
require "js/require_remote/relative_shim"
|
|
60
|
+
JS::RequireRemote.instance.base_url = "/"
|
|
61
|
+
JS::RequireRemote.instance.load(${JSON.stringify(main)})
|
|
62
|
+
`);
|
|
63
|
+
});
|
|
58
64
|
} catch (error) {
|
|
59
65
|
bootFailed(error && error.message ? error.message : "Ruby boot failed");
|
|
60
66
|
throw error;
|
|
61
67
|
}
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
async function withNoStoreRubySourceFetch(callback) {
|
|
71
|
+
const originalFetch = globalThis.fetch;
|
|
72
|
+
globalThis.fetch = (input, init = {}) => {
|
|
73
|
+
if (!shouldBypassCache(input)) return originalFetch(input, init);
|
|
74
|
+
|
|
75
|
+
return originalFetch(input, { ...init, cache: "no-store" });
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
return await callback();
|
|
80
|
+
} finally {
|
|
81
|
+
globalThis.fetch = originalFetch;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function shouldBypassCache(input) {
|
|
86
|
+
const rawUrl = typeof input === "string" ? input : input?.url;
|
|
87
|
+
if (!rawUrl) return false;
|
|
88
|
+
|
|
89
|
+
const { pathname } = new URL(rawUrl, globalThis.location?.href || "http://localhost/");
|
|
90
|
+
if (pathname.startsWith("/lib/")) return true;
|
|
91
|
+
if (!pathname.startsWith("/examples/browser/")) return false;
|
|
92
|
+
|
|
93
|
+
return !pathname.startsWith("/examples/browser/assets/");
|
|
94
|
+
}
|
|
95
|
+
|
|
64
96
|
async function compileWasm(url) {
|
|
65
97
|
const response = fetch(url);
|
|
66
98
|
if (WebAssembly.compileStreaming) {
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
begin
|
|
6
|
-
JS.global[:__threeReady].await
|
|
7
|
-
|
|
8
|
-
require_relative "../../../lib/three"
|
|
9
|
-
|
|
10
|
-
document = JS.global[:document]
|
|
11
|
-
window = JS.global[:window]
|
|
12
|
-
viewport = document.call(:querySelector, "#viewport")
|
|
13
|
-
status = document.call(:querySelector, "#status")
|
|
14
|
-
status_dot = document.call(:querySelector, "#status-dot")
|
|
15
|
-
status[:textContent] = "Starting Ruby scene"
|
|
3
|
+
require_relative "../../../lib/three"
|
|
16
4
|
|
|
5
|
+
Three::Browser.run(starting: "Starting Ruby scene") do |app|
|
|
17
6
|
scene = Three::Scene.new
|
|
18
7
|
camera = Three::OrthographicCamera.new(-2.5, 2.5, 1.6, -1.6, near: 0.1, far: 100)
|
|
19
8
|
camera.position.z = 5
|
|
@@ -91,9 +80,7 @@ begin
|
|
|
91
80
|
)
|
|
92
81
|
renderer.set_clear_color(0x11161a, 1)
|
|
93
82
|
|
|
94
|
-
|
|
95
|
-
width = [viewport[:clientWidth].to_i, 1].max
|
|
96
|
-
height = [viewport[:clientHeight].to_i, 1].max
|
|
83
|
+
app.resize_renderer(renderer, camera) do |width, height, _aspect|
|
|
97
84
|
view_height = 3.4
|
|
98
85
|
view_width = view_height * width.to_f / height
|
|
99
86
|
|
|
@@ -102,25 +89,26 @@ begin
|
|
|
102
89
|
camera.top = view_height / 2
|
|
103
90
|
camera.bottom = -view_height / 2
|
|
104
91
|
camera.update_projection_matrix
|
|
105
|
-
renderer.set_size(width, height)
|
|
106
92
|
end
|
|
107
|
-
|
|
108
|
-
resize.call
|
|
109
|
-
window.call(:addEventListener, "resize", resize)
|
|
110
93
|
renderer.render(scene, camera)
|
|
111
94
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
95
|
+
app.expose(
|
|
96
|
+
{
|
|
97
|
+
renderer: renderer,
|
|
98
|
+
scene: scene,
|
|
99
|
+
camera: camera,
|
|
100
|
+
textured_mesh: mesh,
|
|
101
|
+
texture_material: material,
|
|
102
|
+
matcap_mesh: matcap_mesh,
|
|
103
|
+
matcap_material: matcap_material,
|
|
104
|
+
toon_mesh: toon_mesh,
|
|
105
|
+
toon_material: toon_material,
|
|
106
|
+
texture_example_texture: texture,
|
|
107
|
+
texture_example_environment: environment_texture,
|
|
108
|
+
texture_example_frame: 0
|
|
109
|
+
},
|
|
110
|
+
renderer: renderer
|
|
111
|
+
)
|
|
124
112
|
|
|
125
113
|
frame = 0
|
|
126
114
|
renderer.animation_loop do
|
|
@@ -130,13 +118,7 @@ begin
|
|
|
130
118
|
matcap_mesh.rotation.x += 0.005
|
|
131
119
|
matcap_mesh.rotation.y -= 0.009
|
|
132
120
|
toon_mesh.rotation.y += 0.012
|
|
133
|
-
|
|
121
|
+
app.set(:texture_example_frame, frame)
|
|
134
122
|
renderer.render(scene, camera)
|
|
135
123
|
end
|
|
136
|
-
|
|
137
|
-
status[:textContent] = "Running"
|
|
138
|
-
status_dot[:dataset][:state] = "running"
|
|
139
|
-
rescue StandardError => error
|
|
140
|
-
JS.global.call(:__threeRbBootFailed, error.message) if JS.global[:__threeRbBootFailed]
|
|
141
|
-
raise
|
|
142
124
|
end
|
data/exe/three-rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "optparse"
|
|
5
|
+
require "three/generators/browser_example"
|
|
6
|
+
|
|
7
|
+
def usage
|
|
8
|
+
<<~TEXT
|
|
9
|
+
Usage:
|
|
10
|
+
three-rb browser PATH [--force]
|
|
11
|
+
|
|
12
|
+
Commands:
|
|
13
|
+
browser PATH Generate a Ruby-only browser example at PATH.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
three-rb browser examples/browser/quickstart
|
|
17
|
+
TEXT
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
command = ARGV.shift
|
|
21
|
+
|
|
22
|
+
case command
|
|
23
|
+
when "browser", "browser-example"
|
|
24
|
+
options = { force: false }
|
|
25
|
+
parser = OptionParser.new do |opts|
|
|
26
|
+
opts.banner = "Usage: three-rb #{command} PATH [--force]"
|
|
27
|
+
opts.on("--force", "Overwrite generated example files") { options[:force] = true }
|
|
28
|
+
opts.on("-h", "--help", "Show help") do
|
|
29
|
+
puts opts
|
|
30
|
+
exit 0
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
begin
|
|
34
|
+
parser.parse!(ARGV)
|
|
35
|
+
rescue OptionParser::ParseError => error
|
|
36
|
+
warn error.message
|
|
37
|
+
warn parser
|
|
38
|
+
exit 1
|
|
39
|
+
end
|
|
40
|
+
target = ARGV.shift
|
|
41
|
+
|
|
42
|
+
unless target
|
|
43
|
+
warn parser
|
|
44
|
+
exit 1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Three::Generators::BrowserExample.new(target: target, force: options[:force]).call
|
|
48
|
+
when "-h", "--help", nil
|
|
49
|
+
puts usage
|
|
50
|
+
exit(command ? 0 : 1)
|
|
51
|
+
else
|
|
52
|
+
warn "Unknown command: #{command}"
|
|
53
|
+
warn usage
|
|
54
|
+
exit 1
|
|
55
|
+
end
|
|
@@ -30,6 +30,10 @@ module Three
|
|
|
30
30
|
@adapter.load_rgbe_texture(object.source, texture_parameters(object))
|
|
31
31
|
when Texture
|
|
32
32
|
@adapter.load_texture(object.source, texture_parameters(object))
|
|
33
|
+
when FogExp2
|
|
34
|
+
@adapter.new_fog_exp2(object.color.hex, object.density)
|
|
35
|
+
when Fog
|
|
36
|
+
@adapter.new_fog(object.color.hex, object.near, object.far)
|
|
33
37
|
when AmbientLight
|
|
34
38
|
@adapter.new_ambient_light(object.color.hex, object.intensity)
|
|
35
39
|
when DirectionalLight
|
|
@@ -67,6 +71,8 @@ module Three
|
|
|
67
71
|
parameters[:theta_start],
|
|
68
72
|
parameters[:theta_length]
|
|
69
73
|
)
|
|
74
|
+
when TextGeometry
|
|
75
|
+
@adapter.new_text_geometry(object.text, text_geometry_parameters(object))
|
|
70
76
|
when BufferGeometry
|
|
71
77
|
build_buffer_geometry(object)
|
|
72
78
|
when LineBasicMaterial
|
|
@@ -89,6 +89,23 @@ module Three
|
|
|
89
89
|
matrix: texture.matrix.to_a
|
|
90
90
|
}
|
|
91
91
|
end
|
|
92
|
+
|
|
93
|
+
def text_geometry_parameters(geometry)
|
|
94
|
+
parameters = geometry.parameters
|
|
95
|
+
{
|
|
96
|
+
font: parameters[:font].handle,
|
|
97
|
+
size: parameters[:size],
|
|
98
|
+
depth: parameters[:depth],
|
|
99
|
+
curveSegments: parameters[:curve_segments],
|
|
100
|
+
steps: parameters[:steps],
|
|
101
|
+
bevelEnabled: parameters[:bevel_enabled],
|
|
102
|
+
bevelThickness: parameters[:bevel_thickness],
|
|
103
|
+
bevelSize: parameters[:bevel_size],
|
|
104
|
+
bevelOffset: parameters[:bevel_offset],
|
|
105
|
+
bevelSegments: parameters[:bevel_segments],
|
|
106
|
+
direction: parameters[:direction]
|
|
107
|
+
}
|
|
108
|
+
end
|
|
92
109
|
end
|
|
93
110
|
|
|
94
111
|
include Parameters
|