mulmocast 2.6.5 → 2.6.7
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.
- package/lib/actions/audio.js +7 -35
- package/lib/actions/graph_option.d.ts +4 -0
- package/lib/actions/graph_option.js +19 -0
- package/lib/actions/image_references.js +3 -3
- package/lib/actions/images.d.ts +2 -3
- package/lib/actions/images.js +5 -19
- package/lib/actions/movie.d.ts +3 -0
- package/lib/actions/movie.js +38 -5
- package/lib/agents/add_bgm_agent.d.ts +10 -0
- package/lib/agents/add_bgm_agent.js +26 -4
- package/lib/agents/movie_genai_agent.js +1 -1
- package/lib/agents/movie_replicate_agent.js +29 -5
- package/lib/methods/mulmo_presentation_style.d.ts +5 -1
- package/lib/methods/mulmo_presentation_style.js +27 -3
- package/lib/types/provider2agent.d.ts +2 -0
- package/lib/types/provider2agent.js +78 -5
- package/lib/types/schema.d.ts +55 -4
- package/lib/types/schema.js +12 -1
- package/lib/utils/context.d.ts +28 -2
- package/lib/utils/image_plugins/html_tailwind.d.ts +5 -0
- package/lib/utils/image_plugins/html_tailwind.js +56 -5
- package/package.json +18 -16
- package/scripts/test/fixtures/movie_tone_high.mov +0 -0
- package/scripts/test/fixtures/movie_tone_low.mov +0 -0
- package/scripts/test/fixtures/movie_tone_mid.mov +0 -0
- package/scripts/test/glb/sample_2026-03-15T172907.296_compat.glb +0 -0
- package/scripts/test/test_audio_mix.json +91 -0
- package/scripts/test/test_audio_mix_beat_vol.json +100 -0
- package/scripts/test/test_audio_mix_ducking.json +91 -0
- package/scripts/test/test_audio_mix_legacy.json +90 -0
- package/scripts/test/test_grok.json +57 -0
- package/scripts/test/test_image_references.json +74 -0
- package/scripts/test/test_kling_v3.json +54 -0
- package/scripts/test/test_kling_v3_omni.json +54 -0
- package/scripts/test/test_lipsync2.json +48 -52
- package/scripts/test/test_lipsync5.json +66 -0
- package/scripts/test/test_runway.json +54 -0
- package/scripts/test/test_threejs.json +241 -0
- package/scripts/test/test_threejs_glb.json +154 -0
- package/scripts/test/test_veo31_lite.json +39 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": { "version": "1.1" },
|
|
3
|
+
"movieParams": {
|
|
4
|
+
"provider": "replicate",
|
|
5
|
+
"model": "kwaivgi/kling-v3-omni-video"
|
|
6
|
+
},
|
|
7
|
+
"imageParams": {
|
|
8
|
+
"provider": "google"
|
|
9
|
+
},
|
|
10
|
+
"audioParams": {
|
|
11
|
+
"bgmVolume": 0
|
|
12
|
+
},
|
|
13
|
+
"captionParams": {
|
|
14
|
+
"lang": "en"
|
|
15
|
+
},
|
|
16
|
+
"lang": "en",
|
|
17
|
+
"beats": [
|
|
18
|
+
{
|
|
19
|
+
"text": "Testing kwaivgi/kling-v3-omni-video — text-to-video and image-to-video varieties.",
|
|
20
|
+
"image": {
|
|
21
|
+
"type": "textSlide",
|
|
22
|
+
"slide": {
|
|
23
|
+
"title": "kwaivgi/kling-v3-omni-video test"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "kling-omni-t2v-5s",
|
|
29
|
+
"text": "Text-to-video, 5 seconds: a red fox trots through a snowy forest at dawn, pawprints left behind",
|
|
30
|
+
"duration": 5,
|
|
31
|
+
"moviePrompt": "a red fox trots through a snowy forest at dawn, pawprints left behind, soft golden light filtering through pine trees"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "kling-omni-t2v-10s",
|
|
35
|
+
"text": "Text-to-video, 10 seconds: bustling Tokyo crossing at night",
|
|
36
|
+
"duration": 10,
|
|
37
|
+
"moviePrompt": "aerial dolly shot over Shibuya crossing at night, neon lights reflecting on wet pavement, crowds of people crossing in all directions"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "kling-omni-i2v-5s",
|
|
41
|
+
"text": "Image-to-video, 5 seconds: ocean waves animated from a still image",
|
|
42
|
+
"duration": 5,
|
|
43
|
+
"imagePrompt": "a calm tropical beach at sunset, crystal-clear turquoise water, golden sand, photorealistic",
|
|
44
|
+
"moviePrompt": "gentle waves lapping the shore, palm trees swaying softly in the breeze, camera slowly pushing forward"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "kling-omni-i2v-10s",
|
|
48
|
+
"text": "Image-to-video, 10 seconds: city skyline coming to life",
|
|
49
|
+
"duration": 10,
|
|
50
|
+
"imagePrompt": "futuristic city skyline at dusk, glass skyscrapers with glowing windows, photorealistic",
|
|
51
|
+
"moviePrompt": "lights in the skyscraper windows flicker on one by one as night falls, slow upward pan along the tallest tower"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -1,66 +1,62 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$mulmocast": {
|
|
3
|
-
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
"canvasSize": {
|
|
7
|
-
"width": 1280,
|
|
8
|
-
"height": 720
|
|
2
|
+
"$mulmocast": { "version": "1.1" },
|
|
3
|
+
"lipSyncParams": {
|
|
4
|
+
"provider": "replicate",
|
|
5
|
+
"model": "bytedance/latentsync"
|
|
9
6
|
},
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"en": "Presenter"
|
|
15
|
-
},
|
|
16
|
-
"voiceId": "shimmer",
|
|
17
|
-
"provider": "openai"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
7
|
+
"audioParams": {
|
|
8
|
+
"bgmVolume": 0.01,
|
|
9
|
+
"introPadding": 0,
|
|
10
|
+
"padding": 0.0
|
|
20
11
|
},
|
|
21
12
|
"imageParams": {
|
|
22
|
-
"provider": "openai",
|
|
23
13
|
"images": {
|
|
24
|
-
"
|
|
25
|
-
"type": "
|
|
26
|
-
"
|
|
27
|
-
"kind": "path",
|
|
28
|
-
"path": "./image-2.png"
|
|
29
|
-
}
|
|
14
|
+
"presenter": {
|
|
15
|
+
"type": "imagePrompt",
|
|
16
|
+
"prompt": "A professional female presenter standing in front of a modern presentation screen, speaking confidently, photo realistic, front-facing, neutral expression, well-lit"
|
|
30
17
|
}
|
|
31
18
|
}
|
|
32
19
|
},
|
|
33
|
-
"movieParams": {
|
|
34
|
-
"provider": "replicate",
|
|
35
|
-
"model": "bytedance/seedance-1-lite"
|
|
36
|
-
},
|
|
37
|
-
"soundEffectParams": {
|
|
38
|
-
"provider": "replicate"
|
|
39
|
-
},
|
|
40
|
-
"lipSyncParams": {
|
|
41
|
-
"provider": "replicate",
|
|
42
|
-
"model": "bytedance/latentsync"
|
|
43
|
-
},
|
|
44
|
-
"audioParams": {
|
|
45
|
-
"padding": 0.3,
|
|
46
|
-
"introPadding": 1,
|
|
47
|
-
"closingPadding": 0.8,
|
|
48
|
-
"outroPadding": 1,
|
|
49
|
-
"bgmVolume": 0.2,
|
|
50
|
-
"audioVolume": 1,
|
|
51
|
-
"suppressSpeech": false
|
|
52
|
-
},
|
|
53
|
-
"title": "リップシンク(generate contents)",
|
|
54
|
-
"description": "mulmocast",
|
|
55
20
|
"lang": "en",
|
|
56
21
|
"beats": [
|
|
57
22
|
{
|
|
58
|
-
"
|
|
59
|
-
"text": "
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
23
|
+
"id": "latentsync",
|
|
24
|
+
"text": "Artificial intelligence is transforming the world around us, enabling machines to learn, adapt, and solve complex problems in ways we never imagined possible.",
|
|
25
|
+
"imageNames": ["presenter"],
|
|
26
|
+
"moviePrompt": "A professional female presenter speaking in front of a modern presentation screen",
|
|
27
|
+
"enableLipSync": true,
|
|
28
|
+
"lipSyncParams": {
|
|
29
|
+
"model": "bytedance/latentsync"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "tmappdev_lipsync",
|
|
34
|
+
"text": "Artificial intelligence is transforming the world around us, enabling machines to learn, adapt, and solve complex problems in ways we never imagined possible.",
|
|
35
|
+
"imageNames": ["presenter"],
|
|
36
|
+
"moviePrompt": "A professional female presenter speaking in front of a modern presentation screen",
|
|
37
|
+
"enableLipSync": true,
|
|
38
|
+
"lipSyncParams": {
|
|
39
|
+
"model": "tmappdev/lipsync"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "omni_human",
|
|
44
|
+
"text": "Artificial intelligence is transforming the world around us.",
|
|
45
|
+
"imageNames": ["presenter"],
|
|
46
|
+
"enableLipSync": true,
|
|
47
|
+
"lipSyncParams": {
|
|
48
|
+
"model": "bytedance/omni-human"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "pixverse_lipsync",
|
|
53
|
+
"text": "Artificial intelligence is transforming the world around us, enabling machines to learn, adapt, and solve complex problems in ways we never imagined possible.",
|
|
54
|
+
"imageNames": ["presenter"],
|
|
55
|
+
"moviePrompt": "A professional female presenter speaking in front of a modern presentation screen",
|
|
56
|
+
"enableLipSync": true,
|
|
57
|
+
"lipSyncParams": {
|
|
58
|
+
"model": "pixverse/lipsync"
|
|
59
|
+
}
|
|
64
60
|
}
|
|
65
61
|
]
|
|
66
62
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": {
|
|
3
|
+
"version": "1.1",
|
|
4
|
+
"credit": "closing"
|
|
5
|
+
},
|
|
6
|
+
"canvasSize": {
|
|
7
|
+
"width": 1280,
|
|
8
|
+
"height": 720
|
|
9
|
+
},
|
|
10
|
+
"speechParams": {
|
|
11
|
+
"speakers": {
|
|
12
|
+
"Presenter": {
|
|
13
|
+
"displayName": {
|
|
14
|
+
"en": "Presenter"
|
|
15
|
+
},
|
|
16
|
+
"voiceId": "shimmer",
|
|
17
|
+
"provider": "openai"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"imageParams": {
|
|
22
|
+
"provider": "openai",
|
|
23
|
+
"images": {
|
|
24
|
+
"woman": {
|
|
25
|
+
"type": "image",
|
|
26
|
+
"source": {
|
|
27
|
+
"kind": "path",
|
|
28
|
+
"path": "./image-2.png"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"movieParams": {
|
|
34
|
+
"provider": "replicate",
|
|
35
|
+
"model": "bytedance/seedance-1-lite"
|
|
36
|
+
},
|
|
37
|
+
"soundEffectParams": {
|
|
38
|
+
"provider": "replicate"
|
|
39
|
+
},
|
|
40
|
+
"lipSyncParams": {
|
|
41
|
+
"provider": "replicate",
|
|
42
|
+
"model": "bytedance/latentsync"
|
|
43
|
+
},
|
|
44
|
+
"audioParams": {
|
|
45
|
+
"padding": 0.3,
|
|
46
|
+
"introPadding": 1,
|
|
47
|
+
"closingPadding": 0.8,
|
|
48
|
+
"outroPadding": 1,
|
|
49
|
+
"bgmVolume": 0.2,
|
|
50
|
+
"audioVolume": 1,
|
|
51
|
+
"suppressSpeech": false
|
|
52
|
+
},
|
|
53
|
+
"title": "リップシンク(generate contents)",
|
|
54
|
+
"description": "mulmocast",
|
|
55
|
+
"lang": "en",
|
|
56
|
+
"beats": [
|
|
57
|
+
{
|
|
58
|
+
"speaker": "Presenter",
|
|
59
|
+
"text": "You've had a challenging day. The evidence is clear: you need rest. You're more capable than you realize. Case closed. hello hello !!",
|
|
60
|
+
"id": "c0e344e1-4ed7-4145-b7fc-c878c093cffd",
|
|
61
|
+
"imagePrompt": "in the city",
|
|
62
|
+
"moviePrompt": "never looking away from camera",
|
|
63
|
+
"enableLipSync": true
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": { "version": "1.1" },
|
|
3
|
+
"movieParams": {
|
|
4
|
+
"provider": "replicate",
|
|
5
|
+
"model": "runwayml/gen-4.5"
|
|
6
|
+
},
|
|
7
|
+
"imageParams": {
|
|
8
|
+
"provider": "google"
|
|
9
|
+
},
|
|
10
|
+
"audioParams": {
|
|
11
|
+
"bgmVolume": 0
|
|
12
|
+
},
|
|
13
|
+
"captionParams": {
|
|
14
|
+
"lang": "en"
|
|
15
|
+
},
|
|
16
|
+
"lang": "en",
|
|
17
|
+
"beats": [
|
|
18
|
+
{
|
|
19
|
+
"text": "Testing runwayml/gen-4.5 — text-to-video and image-to-video varieties.",
|
|
20
|
+
"image": {
|
|
21
|
+
"type": "textSlide",
|
|
22
|
+
"slide": {
|
|
23
|
+
"title": "runwayml/gen-4.5 test"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "runway-t2v-5s",
|
|
29
|
+
"text": "Text-to-video, 5 seconds: a red fox trots through a snowy forest at dawn, pawprints left behind",
|
|
30
|
+
"duration": 5,
|
|
31
|
+
"moviePrompt": "a red fox trots through a snowy forest at dawn, pawprints left behind, soft golden light filtering through pine trees"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "runway-t2v-10s",
|
|
35
|
+
"text": "Text-to-video, 10 seconds: bustling Tokyo crossing at night",
|
|
36
|
+
"duration": 10,
|
|
37
|
+
"moviePrompt": "aerial dolly shot over Shibuya crossing at night, neon lights reflecting on wet pavement, crowds of people crossing in all directions"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "runway-i2v-5s",
|
|
41
|
+
"text": "Image-to-video, 5 seconds: ocean waves animated from a still image",
|
|
42
|
+
"duration": 5,
|
|
43
|
+
"imagePrompt": "a calm tropical beach at sunset, crystal-clear turquoise water, golden sand, photorealistic",
|
|
44
|
+
"moviePrompt": "gentle waves lapping the shore, palm trees swaying softly in the breeze, camera slowly pushing forward"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "runway-i2v-10s",
|
|
48
|
+
"text": "Image-to-video, 10 seconds: city skyline coming to life",
|
|
49
|
+
"duration": 10,
|
|
50
|
+
"imagePrompt": "futuristic city skyline at dusk, glass skyscrapers with glowing windows, photorealistic",
|
|
51
|
+
"moviePrompt": "lights in the skyscraper windows flicker on one by one as night falls, slow upward pan along the tallest tower"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": {
|
|
3
|
+
"version": "1.1"
|
|
4
|
+
},
|
|
5
|
+
"lang": "ja",
|
|
6
|
+
"title": "Three.js 3D Objects Demo",
|
|
7
|
+
"canvasSize": { "width": 1280, "height": 720 },
|
|
8
|
+
"speechParams": {
|
|
9
|
+
"speakers": {
|
|
10
|
+
"narrator": {
|
|
11
|
+
"voiceId": "nova",
|
|
12
|
+
"displayName": { "ja": "ナレーター" }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"audioParams": {
|
|
17
|
+
"padding": 0,
|
|
18
|
+
"introPadding": 0,
|
|
19
|
+
"closingPadding": 0,
|
|
20
|
+
"outroPadding": 0
|
|
21
|
+
},
|
|
22
|
+
"beats": [
|
|
23
|
+
{
|
|
24
|
+
"id": "cube_rotate",
|
|
25
|
+
"speaker": "narrator",
|
|
26
|
+
"text": "まずは立方体。ゆっくり回転しています。",
|
|
27
|
+
"duration": 6,
|
|
28
|
+
"image": {
|
|
29
|
+
"type": "html_tailwind",
|
|
30
|
+
"html": [
|
|
31
|
+
"<div id='three-container' style='position:fixed;inset:0;overflow:hidden;background:#111122'>",
|
|
32
|
+
" <canvas id='c' style='display:block;width:100%;height:100%'></canvas>",
|
|
33
|
+
"</div>"
|
|
34
|
+
],
|
|
35
|
+
"script": [
|
|
36
|
+
"import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js';",
|
|
37
|
+
"const canvas = document.getElementById('c');",
|
|
38
|
+
"const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });",
|
|
39
|
+
"renderer.setClearColor(0x1a1a2e);",
|
|
40
|
+
"renderer.outputColorSpace = THREE.SRGBColorSpace;",
|
|
41
|
+
"",
|
|
42
|
+
"const scene = new THREE.Scene();",
|
|
43
|
+
"const camera = new THREE.PerspectiveCamera(50, 16/9, 0.1, 100);",
|
|
44
|
+
"camera.position.set(0, 1.5, 4);",
|
|
45
|
+
"camera.lookAt(0, 0, 0);",
|
|
46
|
+
"",
|
|
47
|
+
"scene.add(new THREE.AmbientLight(0x404040, 2));",
|
|
48
|
+
"const dirLight = new THREE.DirectionalLight(0xffffff, 3);",
|
|
49
|
+
"dirLight.position.set(3, 5, 4);",
|
|
50
|
+
"scene.add(dirLight);",
|
|
51
|
+
"",
|
|
52
|
+
"const cube = new THREE.Mesh(",
|
|
53
|
+
" new THREE.BoxGeometry(1.5, 1.5, 1.5),",
|
|
54
|
+
" new THREE.MeshStandardMaterial({ color: 0x00d2ff, metalness: 0.3, roughness: 0.4 })",
|
|
55
|
+
");",
|
|
56
|
+
"scene.add(cube);",
|
|
57
|
+
"",
|
|
58
|
+
"let lastW = 0;",
|
|
59
|
+
"let lastH = 0;",
|
|
60
|
+
"const syncSize = () => {",
|
|
61
|
+
" const w = Math.max(1, Math.floor(window.innerWidth || document.documentElement.clientWidth));",
|
|
62
|
+
" const h = Math.max(1, Math.floor(window.innerHeight || document.documentElement.clientHeight));",
|
|
63
|
+
" if (w !== lastW || h !== lastH) {",
|
|
64
|
+
" renderer.setSize(w, h, true);",
|
|
65
|
+
" camera.aspect = w / h;",
|
|
66
|
+
" camera.updateProjectionMatrix();",
|
|
67
|
+
" lastW = w;",
|
|
68
|
+
" lastH = h;",
|
|
69
|
+
" }",
|
|
70
|
+
"};",
|
|
71
|
+
"window.render = function(frame, total) {",
|
|
72
|
+
" document.body.style.zoom = '1';",
|
|
73
|
+
" syncSize();",
|
|
74
|
+
" const t = frame / total;",
|
|
75
|
+
" cube.rotation.x = t * Math.PI * 2;",
|
|
76
|
+
" cube.rotation.y = t * Math.PI * 3;",
|
|
77
|
+
" renderer.render(scene, camera);",
|
|
78
|
+
"};"
|
|
79
|
+
],
|
|
80
|
+
"animation": { "fps": 30 }
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "crystal_spin",
|
|
85
|
+
"speaker": "narrator",
|
|
86
|
+
"text": "次はクリスタル。光を受けてキラキラ輝きます。",
|
|
87
|
+
"duration": 6,
|
|
88
|
+
"image": {
|
|
89
|
+
"type": "html_tailwind",
|
|
90
|
+
"html": [
|
|
91
|
+
"<div id='three-container' style='position:fixed;inset:0;overflow:hidden;background:#111122'>",
|
|
92
|
+
" <canvas id='c' style='display:block;width:100%;height:100%'></canvas>",
|
|
93
|
+
"</div>"
|
|
94
|
+
],
|
|
95
|
+
"script": [
|
|
96
|
+
"import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js';",
|
|
97
|
+
"const canvas = document.getElementById('c');",
|
|
98
|
+
"const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });",
|
|
99
|
+
"renderer.setClearColor(0x0d0d1a);",
|
|
100
|
+
"renderer.outputColorSpace = THREE.SRGBColorSpace;",
|
|
101
|
+
"",
|
|
102
|
+
"const scene = new THREE.Scene();",
|
|
103
|
+
"const camera = new THREE.PerspectiveCamera(50, 16/9, 0.1, 100);",
|
|
104
|
+
"camera.position.set(0, 2, 5);",
|
|
105
|
+
"camera.lookAt(0, 0, 0);",
|
|
106
|
+
"",
|
|
107
|
+
"scene.add(new THREE.AmbientLight(0x334455, 3));",
|
|
108
|
+
"const pointLight1 = new THREE.PointLight(0xff66aa, 2, 20);",
|
|
109
|
+
"pointLight1.position.set(3, 3, 3);",
|
|
110
|
+
"scene.add(pointLight1);",
|
|
111
|
+
"const pointLight2 = new THREE.PointLight(0x66aaff, 2, 20);",
|
|
112
|
+
"pointLight2.position.set(-3, 2, -2);",
|
|
113
|
+
"scene.add(pointLight2);",
|
|
114
|
+
"",
|
|
115
|
+
"const crystal = new THREE.Mesh(",
|
|
116
|
+
" new THREE.OctahedronGeometry(1.2, 0),",
|
|
117
|
+
" new THREE.MeshStandardMaterial({ color: 0x88ccff, metalness: 0.6, roughness: 0.1 })",
|
|
118
|
+
");",
|
|
119
|
+
"crystal.scale.set(1, 1.6, 1);",
|
|
120
|
+
"scene.add(crystal);",
|
|
121
|
+
"",
|
|
122
|
+
"let lastW = 0;",
|
|
123
|
+
"let lastH = 0;",
|
|
124
|
+
"const syncSize = () => {",
|
|
125
|
+
" const w = Math.max(1, Math.floor(window.innerWidth || document.documentElement.clientWidth));",
|
|
126
|
+
" const h = Math.max(1, Math.floor(window.innerHeight || document.documentElement.clientHeight));",
|
|
127
|
+
" if (w !== lastW || h !== lastH) {",
|
|
128
|
+
" renderer.setSize(w, h, true);",
|
|
129
|
+
" camera.aspect = w / h;",
|
|
130
|
+
" camera.updateProjectionMatrix();",
|
|
131
|
+
" lastW = w;",
|
|
132
|
+
" lastH = h;",
|
|
133
|
+
" }",
|
|
134
|
+
"};",
|
|
135
|
+
"window.render = function(frame, total) {",
|
|
136
|
+
" document.body.style.zoom = '1';",
|
|
137
|
+
" syncSize();",
|
|
138
|
+
" const t = frame / total;",
|
|
139
|
+
" crystal.rotation.y = t * Math.PI * 4;",
|
|
140
|
+
" crystal.rotation.x = Math.sin(t * Math.PI * 2) * 0.3;",
|
|
141
|
+
" pointLight1.position.x = Math.cos(t * Math.PI * 4) * 4;",
|
|
142
|
+
" pointLight1.position.z = Math.sin(t * Math.PI * 4) * 4;",
|
|
143
|
+
" pointLight2.position.x = Math.cos(t * Math.PI * 3 + 2) * 3;",
|
|
144
|
+
" pointLight2.position.z = Math.sin(t * Math.PI * 3 + 2) * 3;",
|
|
145
|
+
" renderer.render(scene, camera);",
|
|
146
|
+
"};"
|
|
147
|
+
],
|
|
148
|
+
"animation": { "fps": 30 }
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "both_objects",
|
|
153
|
+
"speaker": "narrator",
|
|
154
|
+
"text": "最後に両方のオブジェクトを並べて。立方体とクリスタルが一緒に回ります。",
|
|
155
|
+
"duration": 8,
|
|
156
|
+
"image": {
|
|
157
|
+
"type": "html_tailwind",
|
|
158
|
+
"html": [
|
|
159
|
+
"<div id='three-container' style='position:fixed;inset:0;overflow:hidden;background:#111122'>",
|
|
160
|
+
" <canvas id='c' style='display:block;width:100%;height:100%'></canvas>",
|
|
161
|
+
"</div>"
|
|
162
|
+
],
|
|
163
|
+
"script": [
|
|
164
|
+
"import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js';",
|
|
165
|
+
"const canvas = document.getElementById('c');",
|
|
166
|
+
"const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });",
|
|
167
|
+
"renderer.setClearColor(0x111122);",
|
|
168
|
+
"renderer.outputColorSpace = THREE.SRGBColorSpace;",
|
|
169
|
+
"",
|
|
170
|
+
"const scene = new THREE.Scene();",
|
|
171
|
+
"const camera = new THREE.PerspectiveCamera(50, 16/9, 0.1, 100);",
|
|
172
|
+
"camera.position.set(0, 2, 6);",
|
|
173
|
+
"camera.lookAt(0, 0, 0);",
|
|
174
|
+
"",
|
|
175
|
+
"scene.add(new THREE.AmbientLight(0x404060, 2));",
|
|
176
|
+
"const dirLight = new THREE.DirectionalLight(0xffffff, 2);",
|
|
177
|
+
"dirLight.position.set(3, 5, 4);",
|
|
178
|
+
"scene.add(dirLight);",
|
|
179
|
+
"const pointLight1 = new THREE.PointLight(0xff6688, 2, 20);",
|
|
180
|
+
"pointLight1.position.set(4, 3, 3);",
|
|
181
|
+
"scene.add(pointLight1);",
|
|
182
|
+
"const pointLight2 = new THREE.PointLight(0x6688ff, 2, 20);",
|
|
183
|
+
"pointLight2.position.set(-4, 2, -2);",
|
|
184
|
+
"scene.add(pointLight2);",
|
|
185
|
+
"",
|
|
186
|
+
"const cube = new THREE.Mesh(",
|
|
187
|
+
" new THREE.BoxGeometry(1.2, 1.2, 1.2),",
|
|
188
|
+
" new THREE.MeshStandardMaterial({ color: 0x00d2ff, metalness: 0.3, roughness: 0.4 })",
|
|
189
|
+
");",
|
|
190
|
+
"cube.position.x = -2;",
|
|
191
|
+
"scene.add(cube);",
|
|
192
|
+
"",
|
|
193
|
+
"const crystal = new THREE.Mesh(",
|
|
194
|
+
" new THREE.OctahedronGeometry(1.0, 0),",
|
|
195
|
+
" new THREE.MeshStandardMaterial({ color: 0xcc88ff, metalness: 0.6, roughness: 0.1 })",
|
|
196
|
+
");",
|
|
197
|
+
"crystal.scale.set(1, 1.5, 1);",
|
|
198
|
+
"crystal.position.x = 2;",
|
|
199
|
+
"scene.add(crystal);",
|
|
200
|
+
"",
|
|
201
|
+
"const grid = new THREE.GridHelper(10, 20, 0x444466, 0x222244);",
|
|
202
|
+
"grid.position.y = -1.5;",
|
|
203
|
+
"scene.add(grid);",
|
|
204
|
+
"",
|
|
205
|
+
"let lastW = 0;",
|
|
206
|
+
"let lastH = 0;",
|
|
207
|
+
"const syncSize = () => {",
|
|
208
|
+
" const w = Math.max(1, Math.floor(window.innerWidth || document.documentElement.clientWidth));",
|
|
209
|
+
" const h = Math.max(1, Math.floor(window.innerHeight || document.documentElement.clientHeight));",
|
|
210
|
+
" if (w !== lastW || h !== lastH) {",
|
|
211
|
+
" renderer.setSize(w, h, true);",
|
|
212
|
+
" camera.aspect = w / h;",
|
|
213
|
+
" camera.updateProjectionMatrix();",
|
|
214
|
+
" lastW = w;",
|
|
215
|
+
" lastH = h;",
|
|
216
|
+
" }",
|
|
217
|
+
"};",
|
|
218
|
+
"window.render = function(frame, total) {",
|
|
219
|
+
" document.body.style.zoom = '1';",
|
|
220
|
+
" syncSize();",
|
|
221
|
+
" const t = frame / total;",
|
|
222
|
+
" cube.rotation.x = t * Math.PI * 2;",
|
|
223
|
+
" cube.rotation.y = t * Math.PI * 3;",
|
|
224
|
+
" cube.position.y = Math.sin(t * Math.PI * 4) * 0.5;",
|
|
225
|
+
" crystal.rotation.y = t * Math.PI * 4;",
|
|
226
|
+
" crystal.rotation.x = Math.sin(t * Math.PI * 2) * 0.3;",
|
|
227
|
+
" crystal.position.y = Math.cos(t * Math.PI * 3) * 0.4 + 0.3;",
|
|
228
|
+
" pointLight1.position.x = Math.cos(t * Math.PI * 4) * 4;",
|
|
229
|
+
" pointLight1.position.z = Math.sin(t * Math.PI * 4) * 4;",
|
|
230
|
+
" pointLight2.position.x = Math.cos(t * Math.PI * 3 + 2) * 3;",
|
|
231
|
+
" pointLight2.position.z = Math.sin(t * Math.PI * 3 + 2) * 3;",
|
|
232
|
+
" camera.position.x = Math.sin(t * Math.PI * 2) * 1.5;",
|
|
233
|
+
" camera.lookAt(0, 0, 0);",
|
|
234
|
+
" renderer.render(scene, camera);",
|
|
235
|
+
"};"
|
|
236
|
+
],
|
|
237
|
+
"animation": { "fps": 30 }
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|