@chocodrop/mcp 0.1.0-alpha.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.
- package/LICENSE +21 -0
- package/README.md +19 -0
- package/bin/chocodrop-mcp.js +13 -0
- package/package.json +42 -0
- package/runtime/.chocodrop-mcp-runtime +1 -0
- package/runtime/asset-bridge.js +460 -0
- package/runtime/mcp-server.js +107 -0
- package/runtime/site/examples/basic/README.md +38 -0
- package/runtime/site/examples/basic/index.html +1730 -0
- package/runtime/site/examples/bookmarklet-v2.html +141 -0
- package/runtime/site/examples/bookmarklet.html +126 -0
- package/runtime/site/examples/lofi-room/index.html +2570 -0
- package/runtime/site/examples/lofi-room/sandbox-lite.html +124 -0
- package/runtime/site/examples/music-garden/index.html +2150 -0
- package/runtime/site/examples/pixel-ocean/index.html +1780 -0
- package/runtime/site/examples/react-three-fiber/AdvancedExample.jsx +338 -0
- package/runtime/site/examples/react-three-fiber/App.jsx +97 -0
- package/runtime/site/examples/react-three-fiber/README.md +294 -0
- package/runtime/site/examples/react-three-fiber/package.json +25 -0
- package/runtime/site/examples/sdk-test/README.md +70 -0
- package/runtime/site/examples/space/index.html +1975 -0
- package/runtime/site/examples/toy-city/index.html +2288 -0
- package/runtime/site/examples/wabi-sabi/index.html +2515 -0
- package/runtime/site/getting-started.html +84 -0
- package/runtime/site/index.html +1370 -0
- package/runtime/site/public/CommandUI.js +6341 -0
- package/runtime/site/public/LiveCommandClient.js +432 -0
- package/runtime/site/public/SceneManager.js +5493 -0
- package/runtime/site/public/bookmarklet.js +206 -0
- package/runtime/site/public/bootstrap.js +50 -0
- package/runtime/site/public/chocodrop-demo.umd.js +24020 -0
- package/runtime/site/public/chocodrop-demo.umd.min.js +24020 -0
- package/runtime/site/public/chocodrop.esm.js +2 -0
- package/runtime/site/public/html-sandbox/frame.js +865 -0
- package/runtime/site/public/icons/icon-192.png +0 -0
- package/runtime/site/public/icons/icon-512.png +0 -0
- package/runtime/site/public/icons/icon-maskable-512.png +0 -0
- package/runtime/site/public/immersive-launcher.js +175 -0
- package/runtime/site/public/immersive.html +165 -0
- package/runtime/site/public/index.html +1011 -0
- package/runtime/site/public/index.js +9 -0
- package/runtime/site/public/load-chocodrop.js +25 -0
- package/runtime/site/public/load-three.js +26 -0
- package/runtime/site/public/manifest.webmanifest +59 -0
- package/runtime/site/public/pwa-bootstrap.js +128 -0
- package/runtime/site/public/sample-card.svg +15 -0
- package/runtime/site/public/service-worker.js +117 -0
- package/runtime/site/public/translation-dictionary.js +257 -0
- package/runtime/site/public/xr/xr-ui.css +338 -0
- package/runtime/site/public/xr-viewer.html +263 -0
- package/runtime/site/src/client/local-bridge.js +83 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import React, { useRef, useEffect, useState } from 'react';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
4
|
+
import { SceneManager, CommandUI, createChocoDrop } from 'chocodrop';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React Three.js Scene with ChocoDrop integration
|
|
8
|
+
* This example demonstrates how to integrate ChocoDrop with React
|
|
9
|
+
*/
|
|
10
|
+
const ChocoDropScene = () => {
|
|
11
|
+
const mountRef = useRef(null);
|
|
12
|
+
const sceneRef = useRef(null);
|
|
13
|
+
const rendererRef = useRef(null);
|
|
14
|
+
const cameraRef = useRef(null);
|
|
15
|
+
const controlsRef = useRef(null);
|
|
16
|
+
const sceneManagerRef = useRef(null);
|
|
17
|
+
const commandUIRef = useRef(null);
|
|
18
|
+
const animationIdRef = useRef(null);
|
|
19
|
+
|
|
20
|
+
const [isReady, setIsReady] = useState(false);
|
|
21
|
+
const [stats, setStats] = useState({
|
|
22
|
+
objects: 0,
|
|
23
|
+
commands: 0
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!mountRef.current) return;
|
|
28
|
+
|
|
29
|
+
// Scene setup
|
|
30
|
+
const scene = new THREE.Scene();
|
|
31
|
+
const camera = new THREE.PerspectiveCamera(
|
|
32
|
+
75,
|
|
33
|
+
window.innerWidth / window.innerHeight,
|
|
34
|
+
0.1,
|
|
35
|
+
1000
|
|
36
|
+
);
|
|
37
|
+
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
38
|
+
|
|
39
|
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
40
|
+
renderer.shadowMap.enabled = true;
|
|
41
|
+
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
42
|
+
renderer.setClearColor(0x87ceeb);
|
|
43
|
+
|
|
44
|
+
mountRef.current.appendChild(renderer.domElement);
|
|
45
|
+
|
|
46
|
+
// Environment setup
|
|
47
|
+
scene.fog = new THREE.Fog(0x87ceeb, 50, 200);
|
|
48
|
+
|
|
49
|
+
// Lighting
|
|
50
|
+
const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
|
|
51
|
+
scene.add(ambientLight);
|
|
52
|
+
|
|
53
|
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
|
|
54
|
+
directionalLight.position.set(50, 50, 50);
|
|
55
|
+
directionalLight.castShadow = true;
|
|
56
|
+
scene.add(directionalLight);
|
|
57
|
+
|
|
58
|
+
// Ground
|
|
59
|
+
const groundGeometry = new THREE.PlaneGeometry(200, 200);
|
|
60
|
+
const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x90ee90 });
|
|
61
|
+
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
|
62
|
+
ground.rotation.x = -Math.PI / 2;
|
|
63
|
+
ground.receiveShadow = true;
|
|
64
|
+
scene.add(ground);
|
|
65
|
+
|
|
66
|
+
// Camera
|
|
67
|
+
camera.position.set(0, 10, 20);
|
|
68
|
+
|
|
69
|
+
// Controls
|
|
70
|
+
const controls = new OrbitControls(camera, renderer.domElement);
|
|
71
|
+
controls.enableDamping = true;
|
|
72
|
+
controls.dampingFactor = 0.05;
|
|
73
|
+
|
|
74
|
+
// ChocoDrop Integration
|
|
75
|
+
const sceneManager = new SceneManager(scene, {
|
|
76
|
+
camera,
|
|
77
|
+
renderer, // rendererを渡す
|
|
78
|
+
serverUrl: 'http://localhost:3011',
|
|
79
|
+
showLocationIndicator: true,
|
|
80
|
+
indicatorDuration: 2000,
|
|
81
|
+
enableMouseInteraction: true // マウス操作を明示的に有効化(オプション)
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const commandUI = new CommandUI({
|
|
85
|
+
sceneManager,
|
|
86
|
+
activationKey: '@',
|
|
87
|
+
position: 'bottom-right',
|
|
88
|
+
onControlsToggle: (disabled) => {
|
|
89
|
+
controls.enabled = !disabled;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Store refs
|
|
94
|
+
sceneRef.current = scene;
|
|
95
|
+
rendererRef.current = renderer;
|
|
96
|
+
cameraRef.current = camera;
|
|
97
|
+
controlsRef.current = controls;
|
|
98
|
+
sceneManagerRef.current = sceneManager;
|
|
99
|
+
commandUIRef.current = commandUI;
|
|
100
|
+
|
|
101
|
+
// Animation loop
|
|
102
|
+
const animate = () => {
|
|
103
|
+
animationIdRef.current = requestAnimationFrame(animate);
|
|
104
|
+
|
|
105
|
+
controls.update();
|
|
106
|
+
renderer.render(scene, camera);
|
|
107
|
+
|
|
108
|
+
// Update stats
|
|
109
|
+
const objects = sceneManager.getSpawnedObjects();
|
|
110
|
+
const commands = sceneManager.getCommandHistory();
|
|
111
|
+
setStats({
|
|
112
|
+
objects: objects.length,
|
|
113
|
+
commands: commands.length
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
animate();
|
|
118
|
+
setIsReady(true);
|
|
119
|
+
|
|
120
|
+
// Handle window resize
|
|
121
|
+
const handleResize = () => {
|
|
122
|
+
if (!camera || !renderer) return;
|
|
123
|
+
|
|
124
|
+
camera.aspect = window.innerWidth / window.innerHeight;
|
|
125
|
+
camera.updateProjectionMatrix();
|
|
126
|
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
window.addEventListener('resize', handleResize);
|
|
130
|
+
|
|
131
|
+
// Cleanup
|
|
132
|
+
return () => {
|
|
133
|
+
window.removeEventListener('resize', handleResize);
|
|
134
|
+
|
|
135
|
+
if (animationIdRef.current) {
|
|
136
|
+
cancelAnimationFrame(animationIdRef.current);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (commandUI) {
|
|
140
|
+
commandUI.dispose();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (sceneManager) {
|
|
144
|
+
sceneManager.dispose();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (controls) {
|
|
148
|
+
controls.dispose();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (renderer) {
|
|
152
|
+
renderer.dispose();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (mountRef.current && renderer) {
|
|
156
|
+
mountRef.current.removeChild(renderer.domElement);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}, []);
|
|
160
|
+
|
|
161
|
+
const clearAllObjects = () => {
|
|
162
|
+
if (sceneManagerRef.current) {
|
|
163
|
+
sceneManagerRef.current.clearAll();
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const showCommandHistory = () => {
|
|
168
|
+
if (commandUIRef.current) {
|
|
169
|
+
commandUIRef.current.showHistory();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const showExamples = () => {
|
|
174
|
+
if (commandUIRef.current) {
|
|
175
|
+
commandUIRef.current.showExamples();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<div className="live-command-scene">
|
|
181
|
+
{/* Control Panel */}
|
|
182
|
+
<div className="control-panel" style={{
|
|
183
|
+
position: 'fixed',
|
|
184
|
+
top: '10px',
|
|
185
|
+
left: '10px',
|
|
186
|
+
background: 'rgba(0, 0, 0, 0.8)',
|
|
187
|
+
color: 'white',
|
|
188
|
+
padding: '15px',
|
|
189
|
+
borderRadius: '8px',
|
|
190
|
+
fontFamily: 'monospace',
|
|
191
|
+
fontSize: '14px',
|
|
192
|
+
zIndex: 100
|
|
193
|
+
}}>
|
|
194
|
+
<h3 style={{ margin: '0 0 10px 0', color: '#4a90e2' }}>
|
|
195
|
+
🧪 ChocoDrop
|
|
196
|
+
</h3>
|
|
197
|
+
|
|
198
|
+
<div style={{ marginBottom: '10px' }}>
|
|
199
|
+
<strong>Status:</strong> {isReady ? '🟢 Ready' : '🟡 Loading...'}
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div style={{ marginBottom: '10px' }}>
|
|
203
|
+
<strong>Objects:</strong> {stats.objects}<br/>
|
|
204
|
+
<strong>Commands:</strong> {stats.commands}
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div style={{ marginBottom: '15px' }}>
|
|
208
|
+
<strong>Activation:</strong> Press <code>@</code>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
|
|
212
|
+
<button
|
|
213
|
+
onClick={showExamples}
|
|
214
|
+
style={{
|
|
215
|
+
padding: '8px',
|
|
216
|
+
background: '#4a90e2',
|
|
217
|
+
border: 'none',
|
|
218
|
+
borderRadius: '4px',
|
|
219
|
+
color: 'white',
|
|
220
|
+
cursor: 'pointer',
|
|
221
|
+
fontSize: '12px'
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
Show Examples
|
|
225
|
+
</button>
|
|
226
|
+
|
|
227
|
+
<button
|
|
228
|
+
onClick={showCommandHistory}
|
|
229
|
+
style={{
|
|
230
|
+
padding: '8px',
|
|
231
|
+
background: '#95a5a6',
|
|
232
|
+
border: 'none',
|
|
233
|
+
borderRadius: '4px',
|
|
234
|
+
color: 'white',
|
|
235
|
+
cursor: 'pointer',
|
|
236
|
+
fontSize: '12px'
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
Show History
|
|
240
|
+
</button>
|
|
241
|
+
|
|
242
|
+
<button
|
|
243
|
+
onClick={clearAllObjects}
|
|
244
|
+
style={{
|
|
245
|
+
padding: '8px',
|
|
246
|
+
background: '#e74c3c',
|
|
247
|
+
border: 'none',
|
|
248
|
+
borderRadius: '4px',
|
|
249
|
+
color: 'white',
|
|
250
|
+
cursor: 'pointer',
|
|
251
|
+
fontSize: '12px'
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
Clear All
|
|
255
|
+
</button>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
{/* Three.js mount point */}
|
|
260
|
+
<div
|
|
261
|
+
ref={mountRef}
|
|
262
|
+
style={{
|
|
263
|
+
width: '100vw',
|
|
264
|
+
height: '100vh',
|
|
265
|
+
overflow: 'hidden'
|
|
266
|
+
}}
|
|
267
|
+
/>
|
|
268
|
+
</div>
|
|
269
|
+
);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Main App Component
|
|
274
|
+
*/
|
|
275
|
+
const App = () => {
|
|
276
|
+
return (
|
|
277
|
+
<div className="app">
|
|
278
|
+
<ChocoDropScene />
|
|
279
|
+
|
|
280
|
+
{/* Instructions overlay */}
|
|
281
|
+
<div style={{
|
|
282
|
+
position: 'fixed',
|
|
283
|
+
bottom: '10px',
|
|
284
|
+
left: '10px',
|
|
285
|
+
background: 'rgba(0, 0, 0, 0.7)',
|
|
286
|
+
color: 'white',
|
|
287
|
+
padding: '10px',
|
|
288
|
+
borderRadius: '5px',
|
|
289
|
+
fontFamily: 'Arial, sans-serif',
|
|
290
|
+
fontSize: '12px',
|
|
291
|
+
maxWidth: '300px'
|
|
292
|
+
}}>
|
|
293
|
+
<strong>使用方法:</strong><br/>
|
|
294
|
+
• <code>@</code> キーでコマンド入力画面を表示<br/>
|
|
295
|
+
• 例: 「右上にドラゴンを作って」<br/>
|
|
296
|
+
• <code>Escape</code> でUI非表示<br/>
|
|
297
|
+
• マウスでカメラ操作
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export default App;
|
|
304
|
+
|
|
305
|
+
// Usage instructions for package.json scripts:
|
|
306
|
+
/*
|
|
307
|
+
{
|
|
308
|
+
"name": "live-command-react-example",
|
|
309
|
+
"version": "1.0.0",
|
|
310
|
+
"type": "module",
|
|
311
|
+
"dependencies": {
|
|
312
|
+
"react": "^18.0.0",
|
|
313
|
+
"react-dom": "^18.0.0",
|
|
314
|
+
"three": "^0.158.0",
|
|
315
|
+
"chocodrop": "file:../",
|
|
316
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
317
|
+
"vite": "^4.0.0"
|
|
318
|
+
},
|
|
319
|
+
"scripts": {
|
|
320
|
+
"dev": "vite",
|
|
321
|
+
"build": "vite build",
|
|
322
|
+
"preview": "vite preview"
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
*/
|
|
326
|
+
|
|
327
|
+
// Vite configuration (vite.config.js):
|
|
328
|
+
/*
|
|
329
|
+
import { defineConfig } from 'vite';
|
|
330
|
+
import react from '@vitejs/plugin-react';
|
|
331
|
+
|
|
332
|
+
export default defineConfig({
|
|
333
|
+
plugins: [react()],
|
|
334
|
+
server: {
|
|
335
|
+
port: 3000
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
*/
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React, { useRef, useEffect } from 'react';
|
|
2
|
+
import { Canvas, useThree } from '@react-three/fiber';
|
|
3
|
+
import { OrbitControls, Box, Sphere } from '@react-three/drei';
|
|
4
|
+
import { createChocoDrop } from 'chocodrop';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ChocoDrop統合コンポーネント
|
|
8
|
+
* React Three Fiberのシーンに ChocoDrop を統合
|
|
9
|
+
*/
|
|
10
|
+
function ChocoDropIntegration() {
|
|
11
|
+
const { scene, camera, gl } = useThree();
|
|
12
|
+
const chocoDropRef = useRef(null);
|
|
13
|
+
const controlsRef = useRef(null);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
// ChocoDrop初期化
|
|
17
|
+
const chocoDrop = createChocoDrop(scene, {
|
|
18
|
+
camera,
|
|
19
|
+
renderer: gl,
|
|
20
|
+
serverUrl: 'http://localhost:3011', // 省略可能(自動検出)
|
|
21
|
+
onControlsToggle: (disabled) => {
|
|
22
|
+
// UI開閉時にOrbitControlsを制御
|
|
23
|
+
if (controlsRef.current) {
|
|
24
|
+
controlsRef.current.enabled = !disabled;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
chocoDropRef.current = chocoDrop;
|
|
30
|
+
|
|
31
|
+
// クリーンアップ
|
|
32
|
+
return () => {
|
|
33
|
+
chocoDrop.dispose();
|
|
34
|
+
};
|
|
35
|
+
}, [scene, camera, gl]);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<>
|
|
39
|
+
{/* React Three Fiberのコントロール */}
|
|
40
|
+
<OrbitControls
|
|
41
|
+
ref={controlsRef}
|
|
42
|
+
enablePan={true}
|
|
43
|
+
enableZoom={true}
|
|
44
|
+
enableRotate={true}
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
{/* 既存の3Dオブジェクト */}
|
|
48
|
+
<ambientLight intensity={0.5} />
|
|
49
|
+
<pointLight position={[10, 10, 10]} />
|
|
50
|
+
|
|
51
|
+
<Box position={[-2, 0, 0]} onClick={() => console.log('Box clicked')}>
|
|
52
|
+
<meshStandardMaterial color="orange" />
|
|
53
|
+
</Box>
|
|
54
|
+
|
|
55
|
+
<Sphere position={[2, 0, 0]}>
|
|
56
|
+
<meshStandardMaterial color="hotpink" />
|
|
57
|
+
</Sphere>
|
|
58
|
+
</>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* メインアプリケーション
|
|
64
|
+
*/
|
|
65
|
+
function App() {
|
|
66
|
+
return (
|
|
67
|
+
<div style={{ width: '100vw', height: '100vh' }}>
|
|
68
|
+
<Canvas camera={{ position: [0, 0, 5] }}>
|
|
69
|
+
<ChocoDropIntegration />
|
|
70
|
+
</Canvas>
|
|
71
|
+
|
|
72
|
+
{/* 使用方法ガイド */}
|
|
73
|
+
<div style={{
|
|
74
|
+
position: 'fixed',
|
|
75
|
+
top: 10,
|
|
76
|
+
left: 10,
|
|
77
|
+
color: 'white',
|
|
78
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
79
|
+
padding: '10px',
|
|
80
|
+
borderRadius: '5px',
|
|
81
|
+
fontSize: '14px',
|
|
82
|
+
zIndex: 100
|
|
83
|
+
}}>
|
|
84
|
+
<h3>ChocoDrop + React Three Fiber</h3>
|
|
85
|
+
<p><strong>@キー</strong> - コマンドUI起動</p>
|
|
86
|
+
<p><strong>使用例:</strong></p>
|
|
87
|
+
<ul>
|
|
88
|
+
<li>"ドラゴンを右上に作って"</li>
|
|
89
|
+
<li>"桜を中央に配置"</li>
|
|
90
|
+
<li>"青い猫を左下に"</li>
|
|
91
|
+
</ul>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default App;
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# React Three Fiber + ChocoDrop
|
|
2
|
+
|
|
3
|
+
React Three Fiberプロジェクトに ChocoDrop を統合する例です。
|
|
4
|
+
|
|
5
|
+
## 🚀 セットアップ
|
|
6
|
+
|
|
7
|
+
### 1. 依存関係インストール
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install react react-dom
|
|
11
|
+
npm install @react-three/fiber @react-three/drei three
|
|
12
|
+
npm install chocodrop
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### 2. 開発環境
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -D vite @vitejs/plugin-react
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 3. Vite設定 (vite.config.js)
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
import { defineConfig } from 'vite';
|
|
25
|
+
import react from '@vitejs/plugin-react';
|
|
26
|
+
|
|
27
|
+
export default defineConfig({
|
|
28
|
+
plugins: [react()],
|
|
29
|
+
optimizeDeps: {
|
|
30
|
+
include: ['chocodrop']
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 🎮 使用方法
|
|
36
|
+
|
|
37
|
+
### 基本統合
|
|
38
|
+
|
|
39
|
+
```jsx
|
|
40
|
+
import { useThree } from '@react-three/fiber';
|
|
41
|
+
import { createChocoDrop } from 'chocodrop';
|
|
42
|
+
|
|
43
|
+
function ChocoDropIntegration() {
|
|
44
|
+
const { scene, camera, gl } = useThree();
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const chocoDrop = createChocoDrop(scene, {
|
|
48
|
+
camera,
|
|
49
|
+
renderer: gl,
|
|
50
|
+
onControlsToggle: (disabled) => {
|
|
51
|
+
// OrbitControls制御
|
|
52
|
+
controlsRef.current.enabled = !disabled;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return () => chocoDrop.dispose();
|
|
57
|
+
}, [scene, camera, gl]);
|
|
58
|
+
|
|
59
|
+
return <OrbitControls ref={controlsRef} />;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### フック化
|
|
64
|
+
|
|
65
|
+
```jsx
|
|
66
|
+
import { useChocoDrop } from './hooks/useChocoDrop';
|
|
67
|
+
|
|
68
|
+
function Scene() {
|
|
69
|
+
const chocoDropRef = useChocoDrop({
|
|
70
|
+
serverUrl: 'http://localhost:3011'
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const handleGenerateImage = async () => {
|
|
74
|
+
if (chocoDropRef.current) {
|
|
75
|
+
await chocoDropRef.current.client.generateImage('beautiful landscape');
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<>
|
|
81
|
+
<OrbitControls />
|
|
82
|
+
<button onClick={handleGenerateImage}>Generate Landscape</button>
|
|
83
|
+
</>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 🔧 カスタムフック例
|
|
89
|
+
|
|
90
|
+
### useChocoDrop.js
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
import { useRef, useEffect } from 'react';
|
|
94
|
+
import { useThree } from '@react-three/fiber';
|
|
95
|
+
import { createChocoDrop } from 'chocodrop';
|
|
96
|
+
|
|
97
|
+
export function useChocoDrop(options = {}) {
|
|
98
|
+
const { scene, camera, gl } = useThree();
|
|
99
|
+
const chocoDropRef = useRef(null);
|
|
100
|
+
const controlsRef = useRef(null);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
const chocoDrop = createChocoDrop(scene, {
|
|
104
|
+
camera,
|
|
105
|
+
renderer: gl,
|
|
106
|
+
onControlsToggle: (disabled) => {
|
|
107
|
+
if (controlsRef.current) {
|
|
108
|
+
controlsRef.current.enabled = !disabled;
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
...options
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
chocoDropRef.current = chocoDrop;
|
|
115
|
+
|
|
116
|
+
return () => {
|
|
117
|
+
chocoDrop.dispose();
|
|
118
|
+
};
|
|
119
|
+
}, [scene, camera, gl]);
|
|
120
|
+
|
|
121
|
+
return { chocoDropRef, controlsRef };
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 🎨 統合パターン
|
|
126
|
+
|
|
127
|
+
### 1. 基本統合
|
|
128
|
+
|
|
129
|
+
```jsx
|
|
130
|
+
function App() {
|
|
131
|
+
return (
|
|
132
|
+
<Canvas>
|
|
133
|
+
<ChocoDropIntegration />
|
|
134
|
+
<Box position={[0, 0, 0]} />
|
|
135
|
+
</Canvas>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 2. State管理統合
|
|
141
|
+
|
|
142
|
+
```jsx
|
|
143
|
+
import { atom, useAtom } from 'jotai';
|
|
144
|
+
|
|
145
|
+
const chocoDropAtom = atom(null);
|
|
146
|
+
|
|
147
|
+
function Scene() {
|
|
148
|
+
const [chocoDrop, setChocoDrop] = useAtom(chocoDropAtom);
|
|
149
|
+
const { scene, camera, gl } = useThree();
|
|
150
|
+
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
const instance = createChocoDrop(scene, { camera, renderer: gl });
|
|
153
|
+
setChocoDrop(instance);
|
|
154
|
+
return () => instance.dispose();
|
|
155
|
+
}, []);
|
|
156
|
+
|
|
157
|
+
return <OrbitControls />;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 3. コンテキスト統合
|
|
162
|
+
|
|
163
|
+
```jsx
|
|
164
|
+
const ChocoDropContext = createContext(null);
|
|
165
|
+
|
|
166
|
+
export function ChocoDropProvider({ children }) {
|
|
167
|
+
const [chocoDrop, setChocoDrop] = useState(null);
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<ChocoDropContext.Provider value={chocoDrop}>
|
|
171
|
+
<Canvas>
|
|
172
|
+
<ChocoDropSetup onReady={setChocoDrop} />
|
|
173
|
+
{children}
|
|
174
|
+
</Canvas>
|
|
175
|
+
</ChocoDropContext.Provider>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function useChocoDropContext() {
|
|
180
|
+
return useContext(ChocoDropContext);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## 🎭 イベント処理
|
|
185
|
+
|
|
186
|
+
### UI状態管理
|
|
187
|
+
|
|
188
|
+
```jsx
|
|
189
|
+
function ChocoDropUI() {
|
|
190
|
+
const [isUIOpen, setIsUIOpen] = useState(false);
|
|
191
|
+
const chocoDrop = useChocoDrop({
|
|
192
|
+
onControlsToggle: (disabled) => {
|
|
193
|
+
setIsUIOpen(disabled);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<>
|
|
199
|
+
{isUIOpen && (
|
|
200
|
+
<div className="ui-overlay">
|
|
201
|
+
ChocoDrop UI is active
|
|
202
|
+
</div>
|
|
203
|
+
)}
|
|
204
|
+
</>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### カスタムコマンド
|
|
210
|
+
|
|
211
|
+
```jsx
|
|
212
|
+
function CustomCommands() {
|
|
213
|
+
const { chocoDropRef } = useChocoDrop();
|
|
214
|
+
|
|
215
|
+
const handleCustomCommand = async (command) => {
|
|
216
|
+
if (chocoDropRef.current) {
|
|
217
|
+
await chocoDropRef.current.client.executeCommand(command);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<div>
|
|
223
|
+
<button onClick={() => handleCustomCommand('ドラゴンを作って')}>
|
|
224
|
+
ドラゴン生成
|
|
225
|
+
</button>
|
|
226
|
+
<button onClick={() => handleCustomCommand('全て削除')}>
|
|
227
|
+
全削除
|
|
228
|
+
</button>
|
|
229
|
+
</div>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## 🏗️ TypeScript 対応
|
|
235
|
+
|
|
236
|
+
### 型定義
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { RefObject } from 'react';
|
|
240
|
+
import { Object3D } from 'three';
|
|
241
|
+
|
|
242
|
+
interface ChocoDropInstance {
|
|
243
|
+
client: ChocoDropClient;
|
|
244
|
+
sceneManager: SceneManager;
|
|
245
|
+
ui: CommandUI;
|
|
246
|
+
dispose: () => void;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
interface UseChocoDropReturn {
|
|
250
|
+
chocoDropRef: RefObject<ChocoDropInstance | null>;
|
|
251
|
+
controlsRef: RefObject<any>;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function useChocoDrop(options?: ChocoDropOptions): UseChocoDropReturn;
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## 🔧 トラブルシューティング
|
|
258
|
+
|
|
259
|
+
### よくある問題
|
|
260
|
+
|
|
261
|
+
1. **レンダラーの取得に失敗**
|
|
262
|
+
```jsx
|
|
263
|
+
// ✅ 正しい
|
|
264
|
+
const { gl } = useThree();
|
|
265
|
+
|
|
266
|
+
// ❌ 間違い
|
|
267
|
+
const renderer = new THREE.WebGLRenderer();
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
2. **コントロールの競合**
|
|
271
|
+
```jsx
|
|
272
|
+
// ✅ 正しい
|
|
273
|
+
onControlsToggle: (disabled) => {
|
|
274
|
+
controlsRef.current.enabled = !disabled;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ❌ 間違い - コントロール無効化なし
|
|
278
|
+
onControlsToggle: () => {}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
3. **依存関係の問題**
|
|
282
|
+
```bash
|
|
283
|
+
# Three.jsバージョン確認
|
|
284
|
+
npm ls three
|
|
285
|
+
|
|
286
|
+
# React Three Fiber互換性確認
|
|
287
|
+
npm ls @react-three/fiber
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## 📚 関連リンク
|
|
291
|
+
|
|
292
|
+
- [React Three Fiber 公式](https://docs.pmnd.rs/react-three-fiber)
|
|
293
|
+
- [Three.js Drei 公式](https://github.com/pmndrs/drei)
|
|
294
|
+
- [ChocoDrop API](../docs/API.md)
|