SamSak09 commited on
Commit
fece35b
·
verified ·
1 Parent(s): 5d0caf4

Upload index.html

Browse files
Files changed (1) hide show
  1. index.html +242 -0
index.html ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.0">
6
+ <title>FloodDiffusion: Live 3D Client</title>
7
+ <style>
8
+ body { margin: 0; overflow: hidden; background-color: #ffffff; color: #333; font-family: sans-serif; }
9
+ #ui-panel {
10
+ position: absolute; top: 20px; left: 20px; z-index: 100;
11
+ background: rgba(255, 255, 255, 0.95); padding: 20px;
12
+ border: 1px solid #ccc; border-radius: 8px; width: 350px;
13
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
14
+ }
15
+ input[type="text"] { width: 100%; padding: 12px; margin-bottom: 10px; font-size: 14px; border: 2px solid #008080; border-radius: 6px; box-sizing: border-box; outline: none; transition: box-shadow 0.3s;}
16
+ input[type="text"]:focus { box-shadow: 0 0 8px rgba(0, 128, 128, 0.4); }
17
+ .metrics { font-family: monospace; font-size: 13px; margin-top: 15px; color: #555; line-height: 1.6; }
18
+ #status { font-weight: bold; margin-top: 5px; color: teal; }
19
+ #instructions { font-size: 11px; color: #666; margin-top: 15px; text-align: center; font-style: italic; border-top: 1px solid #eee; padding-top: 10px;}
20
+ #socket-status { position: absolute; top: 20px; right: 20px; padding: 6px 12px; background: rgba(255,255,255,0.9); border: 1px solid #ccc; border-radius: 20px; font-size: 12px; font-weight: bold; color: #666; z-index: 100; box-shadow: 0 2px 5px rgba(0,0,0,0.1);}
21
+ </style>
22
+ </head>
23
+ <body>
24
+ <div id="socket-status">🔌 Disconnected</div>
25
+
26
+ <div id="ui-panel">
27
+ <h3 style="margin-top: 0; color: #222;">Live Motion Prompt</h3>
28
+ <input type="text" id="promptInput" placeholder="Start typing (e.g., 'running fast')..." />
29
+ <div id="status">Waiting for input...</div>
30
+
31
+ <div class="metrics" id="metricsPanel">
32
+ <div id="latency">Inference Latency: --</div>
33
+ <div id="payloadSize">Payload Size: --</div>
34
+ </div>
35
+
36
+ <div id="instructions">🖱️ Left Click to Rotate | 📜 Scroll to Zoom</div>
37
+ </div>
38
+
39
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
40
+ <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
41
+
42
+ <script>
43
+ // --- 1. SET UP THE 3D WORLD (WITH SHADOWS) ---
44
+ const scene = new THREE.Scene();
45
+ // Add a soft fog to blend the horizon
46
+ scene.fog = new THREE.FogExp2(0xf0f0f0, 0.05);
47
+ scene.background = new THREE.Color(0xf0f0f0);
48
+
49
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
50
+ renderer.setSize(window.innerWidth, window.innerHeight);
51
+ // ENABLE SHADOWS
52
+ renderer.shadowMap.enabled = true;
53
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
54
+ document.body.appendChild(renderer.domElement);
55
+
56
+ const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
57
+ const controls = new THREE.OrbitControls(camera, renderer.domElement);
58
+ controls.enableDamping = true;
59
+ controls.dampingFactor = 0.05;
60
+
61
+ // --- 2. LIGHTING & ENVIRONMENT ---
62
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
63
+ scene.add(ambientLight);
64
+
65
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
66
+ directionalLight.position.set(5, 10, 5);
67
+ // LIGHT CASTS SHADOW
68
+ directionalLight.castShadow = true;
69
+ directionalLight.shadow.mapSize.width = 2048;
70
+ directionalLight.shadow.mapSize.height = 2048;
71
+ scene.add(directionalLight);
72
+
73
+ // Solid Floor to receive shadows
74
+ const floorGeometry = new THREE.PlaneGeometry(100, 100);
75
+ const floorMaterial = new THREE.MeshStandardMaterial({ color: 0xdddddd, roughness: 0.8 });
76
+ const floor = new THREE.Mesh(floorGeometry, floorMaterial);
77
+ floor.rotation.x = -Math.PI / 2;
78
+ floor.receiveShadow = true; // FLOOR RECEIVES SHADOW
79
+ scene.add(floor);
80
+
81
+ const gridHelper = new THREE.GridHelper(100, 100, 0xcccccc, 0xcccccc);
82
+ scene.add(gridHelper);
83
+
84
+ // --- 3. CREATE THE 3D MANNEQUIN ---
85
+ const maxPoints = 22;
86
+ const boneConnections = [
87
+ 0,1, 0,2, 0,3,
88
+ 1,4, 2,5, 3,6,
89
+ 4,7, 5,8, 6,9,
90
+ 7,10, 8,11, 9,12,
91
+ 9,13, 9,14, 12,15,
92
+ 13,16, 14,17,
93
+ 16,18, 17,19,
94
+ 18,20, 19,21
95
+ ];
96
+
97
+ const jointMaterial = new THREE.MeshStandardMaterial({ color: 0x008080, roughness: 0.3, metalness: 0.2 });
98
+ const boneMaterial = new THREE.MeshStandardMaterial({ color: 0xbbbbbb, roughness: 0.5, metalness: 0.1 });
99
+
100
+ const jointMeshes = [];
101
+ const sphereGeo = new THREE.SphereGeometry(0.04, 16, 16);
102
+ for (let i = 0; i < maxPoints; i++) {
103
+ const sphere = new THREE.Mesh(sphereGeo, jointMaterial);
104
+ sphere.castShadow = true; // JOINTS CAST SHADOWS
105
+ scene.add(sphere);
106
+ jointMeshes.push(sphere);
107
+ }
108
+
109
+ const boneMeshes = [];
110
+ const cylinderGeo = new THREE.CylinderGeometry(0.02, 0.02, 1, 8);
111
+ cylinderGeo.rotateX(Math.PI / 2);
112
+
113
+ for (let i = 0; i < boneConnections.length; i += 2) {
114
+ const cylinder = new THREE.Mesh(cylinderGeo, boneMaterial);
115
+ cylinder.castShadow = true; // BONES CAST SHADOWS
116
+ scene.add(cylinder);
117
+ boneMeshes.push(cylinder);
118
+ }
119
+
120
+ // --- 4. ANIMATION & STATE LOGIC ---
121
+ let motionData = [];
122
+ let currentFrame = 0;
123
+ let isPlaying = false;
124
+ let lastFrameTime = 0;
125
+
126
+ const pA = new THREE.Vector3();
127
+ const pB = new THREE.Vector3();
128
+ const cameraTarget = new THREE.Vector3(0, 1, 0);
129
+
130
+ function animate(timestamp) {
131
+ requestAnimationFrame(animate);
132
+
133
+ if (isPlaying && motionData.length > 0) {
134
+ if (timestamp - lastFrameTime > 33) {
135
+ const frameData = motionData[currentFrame];
136
+
137
+ // Update Spheres
138
+ for (let i = 0; i < maxPoints; i++) {
139
+ jointMeshes[i].position.set(frameData[i][0], frameData[i][1], frameData[i][2]);
140
+ }
141
+
142
+ // Update Cylinders
143
+ let boneIndex = 0;
144
+ for (let i = 0; i < boneConnections.length; i += 2) {
145
+ const idxA = boneConnections[i];
146
+ const idxB = boneConnections[i+1];
147
+
148
+ pA.set(frameData[idxA][0], frameData[idxA][1], frameData[idxA][2]);
149
+ pB.set(frameData[idxB][0], frameData[idxB][1], frameData[idxB][2]);
150
+
151
+ const distance = pA.distanceTo(pB);
152
+ const midPoint = pA.clone().lerp(pB, 0.5);
153
+
154
+ const boneMesh = boneMeshes[boneIndex];
155
+ boneMesh.position.copy(midPoint);
156
+ boneMesh.scale.set(1, 1, distance);
157
+ boneMesh.lookAt(pB);
158
+
159
+ boneIndex++;
160
+ }
161
+
162
+ // SMOOTH CHASE CAMERA
163
+ const rootJoint = frameData[0];
164
+ // Lerp the camera target smoothly instead of snapping it
165
+ cameraTarget.lerp(new THREE.Vector3(rootJoint[0], rootJoint[1], rootJoint[2]), 0.1);
166
+ controls.target.copy(cameraTarget);
167
+
168
+ // Keep camera at a fixed offset so we can see the character move through space
169
+ if (currentFrame === 0) {
170
+ camera.position.set(rootJoint[0], rootJoint[1] + 1, rootJoint[2] + 4);
171
+ }
172
+
173
+ currentFrame = (currentFrame + 1) % motionData.length;
174
+ lastFrameTime = timestamp;
175
+ }
176
+ }
177
+
178
+ controls.update();
179
+ renderer.render(scene, camera);
180
+ }
181
+ animate(0);
182
+
183
+ window.addEventListener('resize', () => {
184
+ camera.aspect = window.innerWidth / window.innerHeight;
185
+ camera.updateProjectionMatrix();
186
+ renderer.setSize(window.innerWidth, window.innerHeight);
187
+ });
188
+
189
+ // --- 5. WEBSOCKET PIPELINE ---
190
+ const socketStatus = document.getElementById('socket-status');
191
+ const statusText = document.getElementById('status');
192
+ const ws = new WebSocket('wss://immodestly-tenacious-kraig.ngrok-free.dev/api/generate_stream');
193
+
194
+ ws.onopen = () => {
195
+ socketStatus.innerHTML = "🟢 Live TCP Connection";
196
+ socketStatus.style.color = "green";
197
+ };
198
+
199
+ ws.onclose = () => {
200
+ socketStatus.innerHTML = "🔴 Disconnected";
201
+ socketStatus.style.color = "red";
202
+ };
203
+
204
+ ws.onmessage = (event) => {
205
+ const data = JSON.parse(event.data);
206
+
207
+ if (data.status === "success" && data.data) {
208
+ motionData = data.data;
209
+ currentFrame = 0;
210
+ isPlaying = true;
211
+
212
+ const kbSize = (new Blob([event.data]).size / 1024).toFixed(2);
213
+ document.getElementById('latency').textContent = `Inference Latency: ${data.latency_ms} ms`;
214
+ document.getElementById('payloadSize').textContent = `Payload Size: ${kbSize} KB`;
215
+ statusText.textContent = "Rendering...";
216
+ statusText.style.color = "teal";
217
+ }
218
+ };
219
+
220
+ let typingTimer;
221
+ const inputField = document.getElementById('promptInput');
222
+
223
+ inputField.addEventListener('input', () => {
224
+ clearTimeout(typingTimer);
225
+ statusText.textContent = "Typing...";
226
+ statusText.style.color = "#ff8c00";
227
+
228
+ typingTimer = setTimeout(() => {
229
+ const promptText = inputField.value.trim();
230
+
231
+ if (promptText.length > 0 && ws.readyState === WebSocket.OPEN) {
232
+ statusText.textContent = "Transmitting to Edge GPU...";
233
+ ws.send(JSON.stringify({ prompt: promptText }));
234
+ } else if (promptText.length === 0) {
235
+ statusText.textContent = "Waiting for input...";
236
+ statusText.style.color = "teal";
237
+ }
238
+ }, 300); // Reduced debounce for a slightly snappier feel
239
+ });
240
+ </script>
241
+ </body>
242
+ </html>