Glif
Toggle theme
More info
Sign In
[gliffy]-spawn (remix)
dham
0
0
likes
Remix
Share
Open options
{ "type": "updateSimState", "args": { "key": "main", "mimeType": "text/html", "value": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Gliffy - 3D Character Viewer</title>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/DRACOLoader.js\"></script>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.min.js\"></script>\n <style>\n body {\n margin: 0;\n padding: 0;\n width: 100%;\n height: 100vh;\n overflow: hidden;\n }\n #container {\n width: 100%;\n height: 100%;\n }\n #loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-family: Arial, sans-serif;\n }\n </style>\n</head>\n<body>\n <div id=\"container\"></div>\n <div id=\"loading\">Loading Gliffy...</div>\n\n <script>\n const scene = new THREE.Scene();\n scene.background = new THREE.Color(244, 244, 246);\n \n const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000);\n camera.position.z = 5;\n \n const renderer = new THREE.WebGLRenderer({ antialias: true });\n renderer.setSize(512, 512);\n renderer.setPixelRatio(window.devicePixelRatio);\n renderer.outputEncoding = THREE.sRGBEncoding;\n document.getElementById('container').appendChild(renderer.domElement);\n \n const ambientLight = new THREE.AmbientLight(0xffffff, 1.5);\n scene.add(ambientLight);\n \n const directionalLight = new THREE.DirectionalLight(0xffffff, 2);\n directionalLight.position.set(1, 1, 1);\n scene.add(directionalLight);\n\n const backLight = new THREE.DirectionalLight(0x6441a5, 1);\n backLight.position.set(-1, 0.5, -1);\n scene.add(backLight);\n \n const morphMeshes = [];\n \n let currentExpressionState = { 1: 0, 2: 0, 3: 0 };\n \n const loader = new THREE.GLTFLoader();\n \n const dracoLoader = new THREE.DRACOLoader();\n dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');\n loader.setDRACOLoader(dracoLoader);\n \n // Improved mouse tracking for gaze following\n const mouse = {\n current: new THREE.Vector2(),\n target: new THREE.Vector2(),\n lerp: 0.1\n };\n \n document.addEventListener('mousemove', (event) => {\n const rect = renderer.domElement.getBoundingClientRect();\n // Convert mouse position to normalized device coordinates (-1 to +1)\n mouse.target.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n mouse.target.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n });\n\n function lerpAngle(start, end, t) {\n return start + t * (end - start);\n }\n\n function calculateGazeRotation(mouseX, mouseY) {\n // Update smoothed mouse position\n mouse.current.x = lerpAngle(mouse.current.x, mouse.target.x, mouse.lerp);\n mouse.current.y = lerpAngle(mouse.current.y, mouse.target.y, mouse.lerp);\n \n // Scale and adjust the rotation ranges\n const rotX = -mouse.current.y * 0.4; // Look up/down (inverted)\n const rotY = mouse.current.x * 0.6; // Look left/right\n \n return {\n x: THREE.MathUtils.clamp(rotX, -0.4, 0.4),\n y: THREE.MathUtils.clamp(rotY, -0.6, 0.6)\n };\n }\n \n loader.load(\n 'https://glifxyz--glif-eternum-web-server.modal.run/gliffy/glb',\n function (gltf) {\n document.getElementById('loading').style.display = 'none';\n \n const model = gltf.scene;\n scene.add(model);\n \n const box = new THREE.Box3().setFromObject(model);\n const center = box.getCenter(new THREE.Vector3());\n const size = box.getSize(new THREE.Vector3());\n \n model.position.x = -center.x;\n model.position.y = -center.y;\n model.position.z = -center.z;\n \n const maxDim = Math.max(size.x, size.y, size.z);\n camera.position.z = maxDim * 2;\n camera.far = maxDim * 10;\n camera.updateProjectionMatrix();\n \n model.traverse((node) => {\n if (node.isMesh && node.morphTargetInfluences && node.morphTargetInfluences.length > 0) {\n morphMeshes.push(node); \n for (let i = 0; i < node.morphTargetInfluences.length; i++) {\n node.morphTargetInfluences[i] = 0;\n }\n }\n });\n \n startExpressionPolling();\n \n addIdleAnimation(model);\n },\n function (xhr) {\n const loadingPercentage = Math.floor((xhr.loaded / xhr.total) * 100);\n document.getElementById('loading').textContent = 'Loading Gliffy... ' + loadingPercentage + '%';\n },\n function (error) {\n console.error('Error loading model:', error);\n document.getElementById('loading').textContent = 'Error loading model. Please try again.';\n }\n );\n \n function startExpressionPolling() {\n fetchExpressionState();\n \n setInterval(fetchExpressionState, 2000);\n }\n \n function fetchExpressionState() {\n fetch('https://glifxyz--glif-eternum-web-server.modal.run/gliffy/expression')\n .then(response => response.json())\n .then(data => {\n console.log(data);\n console.log(currentExpressionState);\n if (JSON.stringify(data) !== JSON.stringify(currentExpressionState)) {\n currentExpressionState = data;\n console.log(currentExpressionState);\n applyExpressionState(data); }\n })\n .catch(error => {\n console.error('Error fetching expression state:', error);\n });\n }\n \n function applyExpressionState(expressionState) {\n const targetState = {};\n for (const [key, value] of Object.entries(expressionState)) {\n targetState[parseInt(key) - 1] = value;\n }\n \n morphMeshes.forEach(mesh => {\n console.log(mesh);\n console.log(mesh.morphTargetInfluences);\n const currentValues = {};\n for (let i = 0; i < mesh.morphTargetInfluences.length; i++) {\n currentValues[i] = mesh.morphTargetInfluences[i];\n }\n \n new TWEEN.Tween(currentValues)\n .to(targetState, 1500)\n .easing(TWEEN.Easing.Quadratic.InOut)\n .onUpdate(() => {\n for (let i = 0; i < mesh.morphTargetInfluences.length; i++) {\n if (i in targetState) {\n mesh.morphTargetInfluences[i] = currentValues[i];\n }\n }\n })\n .start();\n });\n }\n \n function addIdleAnimation(model) {\n // Remove floating animation\n \n // Improved gaze following\n function updateGaze() {\n const targetRotation = calculateGazeRotation(mouse.current.x, mouse.current.y);\n \n // Smooth rotation interpolation\n model.rotation.x = lerpAngle(model.rotation.x, targetRotation.x, 0.05);\n model.rotation.y = lerpAngle(model.rotation.y, targetRotation.y, 0.05);\n }\n\n // Add updateGaze to the animation loop\n const originalAnimate = animate;\n animate = function(time) {\n updateGaze();\n originalAnimate(time);\n };\n }\n \n window.addEventListener('resize', () => {\n camera.aspect = 512 / 512;\n camera.updateProjectionMatrix();\n renderer.setSize(512, 512);\n });\n \n function animate(time) {\n requestAnimationFrame(animate);\n TWEEN.update(time);\n renderer.render(scene, camera);\n }\n \n animate();\n </script>\n</body>\n</html>" } }
{ "type": "updateSimState", "args": { "key": "main", "mimeType": "text/html", "value": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Gliffy - 3D Character Viewer</title>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/DRACOLoader.js\"></script>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.min.js\"></script>\n <style>\n body {\n margin: 0;\n padding: 0;\n width: 100%;\n height: 100vh;\n overflow: hidden;\n }\n #container {\n width: 100%;\n height: 100%;\n }\n #loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-family: Arial, sans-serif;\n }\n </style>\n</head>\n<body>\n <div id=\"container\"></div>\n <div id=\"loading\">Loading Gliffy...</div>\n\n <script>\n const scene = new THREE.Scene();\n scene.background = new THREE.Color(244, 244, 246);\n \n const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000);\n camera.position.z = 5;\n \n const renderer = new THREE.WebGLRenderer({ antialias: true });\n renderer.setSize(512, 512);\n renderer.setPixelRatio(window.devicePixelRatio);\n renderer.outputEncoding = THREE.sRGBEncoding;\n document.getElementById('container').appendChild(renderer.domElement);\n \n const ambientLight = new THREE.AmbientLight(0xffffff, 1.5);\n scene.add(ambientLight);\n \n const directionalLight = new THREE.DirectionalLight(0xffffff, 2);\n directionalLight.position.set(1, 1, 1);\n scene.add(directionalLight);\n\n const backLight = new THREE.DirectionalLight(0x6441a5, 1);\n backLight.position.set(-1, 0.5, -1);\n scene.add(backLight);\n \n const morphMeshes = [];\n \n let currentExpressionState = { 1: 0, 2: 0, 3: 0 };\n \n const loader = new THREE.GLTFLoader();\n \n const dracoLoader = new THREE.DRACOLoader();\n dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');\n loader.setDRACOLoader(dracoLoader);\n \n // Improved mouse tracking for gaze following\n const mouse = {\n current: new THREE.Vector2(),\n target: new THREE.Vector2(),\n lerp: 0.1\n };\n \n document.addEventListener('mousemove', (event) => {\n const rect = renderer.domElement.getBoundingClientRect();\n // Convert mouse position to normalized device coordinates (-1 to +1)\n mouse.target.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n mouse.target.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n });\n\n function lerpAngle(start, end, t) {\n return start + t * (end - start);\n }\n\n function calculateGazeRotation(mouseX, mouseY) {\n // Update smoothed mouse position\n mouse.current.x = lerpAngle(mouse.current.x, mouse.target.x, mouse.lerp);\n mouse.current.y = lerpAngle(mouse.current.y, mouse.target.y, mouse.lerp);\n \n // Scale and adjust the rotation ranges\n const rotX = -mouse.current.y * 0.4; // Look up/down (inverted)\n const rotY = mouse.current.x * 0.6; // Look left/right\n \n return {\n x: THREE.MathUtils.clamp(rotX, -0.4, 0.4),\n y: THREE.MathUtils.clamp(rotY, -0.6, 0.6)\n };\n }\n \n loader.load(\n 'https://glifxyz--glif-eternum-web-server.modal.run/gliffy/glb',\n function (gltf) {\n document.getElementById('loading').style.display = 'none';\n \n const model = gltf.scene;\n scene.add(model);\n \n const box = new THREE.Box3().setFromObject(model);\n const center = box.getCenter(new THREE.Vector3());\n const size = box.getSize(new THREE.Vector3());\n \n model.position.x = -center.x;\n model.position.y = -center.y;\n model.position.z = -center.z;\n \n const maxDim = Math.max(size.x, size.y, size.z);\n camera.position.z = maxDim * 2;\n camera.far = maxDim * 10;\n camera.updateProjectionMatrix();\n \n model.traverse((node) => {\n if (node.isMesh && node.morphTargetInfluences && node.morphTargetInfluences.length > 0) {\n morphMeshes.push(node); \n for (let i = 0; i < node.morphTargetInfluences.length; i++) {\n node.morphTargetInfluences[i] = 0;\n }\n }\n });\n \n startExpressionPolling();\n \n addIdleAnimation(model);\n },\n function (xhr) {\n const loadingPercentage = Math.floor((xhr.loaded / xhr.total) * 100);\n document.getElementById('loading').textContent = 'Loading Gliffy... ' + loadingPercentage + '%';\n },\n function (error) {\n console.error('Error loading model:', error);\n document.getElementById('loading').textContent = 'Error loading model. Please try again.';\n }\n );\n \n function startExpressionPolling() {\n fetchExpressionState();\n \n setInterval(fetchExpressionState, 2000);\n }\n \n function fetchExpressionState() {\n fetch('https://glifxyz--glif-eternum-web-server.modal.run/gliffy/expression')\n .then(response => response.json())\n .then(data => {\n console.log(data);\n console.log(currentExpressionState);\n if (JSON.stringify(data) !== JSON.stringify(currentExpressionState)) {\n currentExpressionState = data;\n console.log(currentExpressionState);\n applyExpressionState(data); }\n })\n .catch(error => {\n console.error('Error fetching expression state:', error);\n });\n }\n \n function applyExpressionState(expressionState) {\n const targetState = {};\n for (const [key, value] of Object.entries(expressionState)) {\n targetState[parseInt(key) - 1] = value;\n }\n \n morphMeshes.forEach(mesh => {\n console.log(mesh);\n console.log(mesh.morphTargetInfluences);\n const currentValues = {};\n for (let i = 0; i < mesh.morphTargetInfluences.length; i++) {\n currentValues[i] = mesh.morphTargetInfluences[i];\n }\n \n new TWEEN.Tween(currentValues)\n .to(targetState, 1500)\n .easing(TWEEN.Easing.Quadratic.InOut)\n .onUpdate(() => {\n for (let i = 0; i < mesh.morphTargetInfluences.length; i++) {\n if (i in targetState) {\n mesh.morphTargetInfluences[i] = currentValues[i];\n }\n }\n })\n .start();\n });\n }\n \n function addIdleAnimation(model) {\n // Remove floating animation\n \n // Improved gaze following\n function updateGaze() {\n const targetRotation = calculateGazeRotation(mouse.current.x, mouse.current.y);\n \n // Smooth rotation interpolation\n model.rotation.x = lerpAngle(model.rotation.x, targetRotation.x, 0.05);\n model.rotation.y = lerpAngle(model.rotation.y, targetRotation.y, 0.05);\n }\n\n // Add updateGaze to the animation loop\n const originalAnimate = animate;\n animate = function(time) {\n updateGaze();\n originalAnimate(time);\n };\n }\n \n window.addEventListener('resize', () => {\n camera.aspect = 512 / 512;\n camera.updateProjectionMatrix();\n renderer.setSize(512, 512);\n });\n \n function animate(time) {\n requestAnimationFrame(animate);\n TWEEN.update(time);\n renderer.render(scene, camera);\n }\n \n animate();\n </script>\n</body>\n</html>" } }
Sign in to run
Remix of
[gliffy]-spawn
2 Runs
Created on
4/1/2025, 9:03:50 AM
Jason-ef05
3 mo. ago
Open options
{ "type": "updateSimState", "args": { "key": "main", "mimeType": "text/html", "value": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Gliffy - 3D Character Viewer</title>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/DRACOLoader.js\"></script>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.min.js\"></script>\n <style>\n body {\n margin: 0;\n padding: 0;\n width: 100%;\n height: 100vh;\n overflow: hidden;\n }\n #container {\n width: 100%;\n height: 100%;\n }\n #loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-family: Arial, sans-serif;\n }\n </style>\n</head>\n<body>\n <div id=\"container\"></div>\n <div id=\"loading\">Loading Gliffy...</div>\n\n <script>\n const scene = new THREE.Scene();\n scene.background = new THREE.Color(244, 244, 246);\n \n const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000);\n camera.position.z = 5;\n \n const renderer = new THREE.WebGLRenderer({ antialias: true });\n renderer.setSize(512, 512);\n renderer.setPixelRatio(window.devicePixelRatio);\n renderer.outputEncoding = THREE.sRGBEncoding;\n document.getElementById('container').appendChild(renderer.domElement);\n \n const ambientLight = new THREE.AmbientLight(0xffffff, 1.5);\n scene.add(ambientLight);\n \n const directionalLight = new THREE.DirectionalLight(0xffffff, 2);\n directionalLight.position.set(1, 1, 1);\n scene.add(directionalLight);\n\n const backLight = new THREE.DirectionalLight(0x6441a5, 1);\n backLight.position.set(-1, 0.5, -1);\n scene.add(backLight);\n \n const morphMeshes = [];\n \n let currentExpressionState = { 1: 0, 2: 0, 3: 0 };\n \n const loader = new THREE.GLTFLoader();\n \n const dracoLoader = new THREE.DRACOLoader();\n dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');\n loader.setDRACOLoader(dracoLoader);\n \n // Improved mouse tracking for gaze following\n const mouse = {\n current: new THREE.Vector2(),\n target: new THREE.Vector2(),\n lerp: 0.1\n };\n \n document.addEventListener('mousemove', (event) => {\n const rect = renderer.domElement.getBoundingClientRect();\n // Convert mouse position to normalized device coordinates (-1 to +1)\n mouse.target.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n mouse.target.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n });\n\n function lerpAngle(start, end, t) {\n return start + t * (end - start);\n }\n\n function calculateGazeRotation(mouseX, mouseY) {\n // Update smoothed mouse position\n mouse.current.x = lerpAngle(mouse.current.x, mouse.target.x, mouse.lerp);\n mouse.current.y = lerpAngle(mouse.current.y, mouse.target.y, mouse.lerp);\n \n // Scale and adjust the rotation ranges\n const rotX = -mouse.current.y * 0.4; // Look up/down (inverted)\n const rotY = mouse.current.x * 0.6; // Look left/right\n \n return {\n x: THREE.MathUtils.clamp(rotX, -0.4, 0.4),\n y: THREE.MathUtils.clamp(rotY, -0.6, 0.6)\n };\n }\n \n loader.load(\n 'https://glifxyz--glif-eternum-web-server.modal.run/gliffy/glb',\n function (gltf) {\n document.getElementById('loading').style.display = 'none';\n \n const model = gltf.scene;\n scene.add(model);\n \n const box = new THREE.Box3().setFromObject(model);\n const center = box.getCenter(new THREE.Vector3());\n const size = box.getSize(new THREE.Vector3());\n \n model.position.x = -center.x;\n model.position.y = -center.y;\n model.position.z = -center.z;\n \n const maxDim = Math.max(size.x, size.y, size.z);\n camera.position.z = maxDim * 2;\n camera.far = maxDim * 10;\n camera.updateProjectionMatrix();\n \n model.traverse((node) => {\n if (node.isMesh && node.morphTargetInfluences && node.morphTargetInfluences.length > 0) {\n morphMeshes.push(node); \n for (let i = 0; i < node.morphTargetInfluences.length; i++) {\n node.morphTargetInfluences[i] = 0;\n }\n }\n });\n \n startExpressionPolling();\n \n addIdleAnimation(model);\n },\n function (xhr) {\n const loadingPercentage = Math.floor((xhr.loaded / xhr.total) * 100);\n document.getElementById('loading').textContent = 'Loading Gliffy... ' + loadingPercentage + '%';\n },\n function (error) {\n console.error('Error loading model:', error);\n document.getElementById('loading').textContent = 'Error loading model. Please try again.';\n }\n );\n \n function startExpressionPolling() {\n fetchExpressionState();\n \n setInterval(fetchExpressionState, 2000);\n }\n \n function fetchExpressionState() {\n fetch('https://glifxyz--glif-eternum-web-server.modal.run/gliffy/expression')\n .then(response => response.json())\n .then(data => {\n console.log(data);\n console.log(currentExpressionState);\n if (JSON.stringify(data) !== JSON.stringify(currentExpressionState)) {\n currentExpressionState = data;\n console.log(currentExpressionState);\n applyExpressionState(data); }\n })\n .catch(error => {\n console.error('Error fetching expression state:', error);\n });\n }\n \n function applyExpressionState(expressionState) {\n const targetState = {};\n for (const [key, value] of Object.entries(expressionState)) {\n targetState[parseInt(key) - 1] = value;\n }\n \n morphMeshes.forEach(mesh => {\n console.log(mesh);\n console.log(mesh.morphTargetInfluences);\n const currentValues = {};\n for (let i = 0; i < mesh.morphTargetInfluences.length; i++) {\n currentValues[i] = mesh.morphTargetInfluences[i];\n }\n \n new TWEEN.Tween(currentValues)\n .to(targetState, 1500)\n .easing(TWEEN.Easing.Quadratic.InOut)\n .onUpdate(() => {\n for (let i = 0; i < mesh.morphTargetInfluences.length; i++) {\n if (i in targetState) {\n mesh.morphTargetInfluences[i] = currentValues[i];\n }\n }\n })\n .start();\n });\n }\n \n function addIdleAnimation(model) {\n // Remove floating animation\n \n // Improved gaze following\n function updateGaze() {\n const targetRotation = calculateGazeRotation(mouse.current.x, mouse.current.y);\n \n // Smooth rotation interpolation\n model.rotation.x = lerpAngle(model.rotation.x, targetRotation.x, 0.05);\n model.rotation.y = lerpAngle(model.rotation.y, targetRotation.y, 0.05);\n }\n\n // Add updateGaze to the animation loop\n const originalAnimate = animate;\n animate = function(time) {\n updateGaze();\n originalAnimate(time);\n };\n }\n \n window.addEventListener('resize', () => {\n camera.aspect = 512 / 512;\n camera.updateProjectionMatrix();\n renderer.setSize(512, 512);\n });\n \n function animate(time) {\n requestAnimationFrame(animate);\n TWEEN.update(time);\n renderer.render(scene, camera);\n }\n \n animate();\n </script>\n</body>\n</html>" } }
0
0
likes
Insectagon
4 mo. ago
Open options
{ "type": "updateSimState", "args": { "key": "main", "mimeType": "text/html", "value": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Gliffy - 3D Character Viewer</title>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/DRACOLoader.js\"></script>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.min.js\"></script>\n <style>\n body {\n margin: 0;\n padding: 0;\n width: 100%;\n height: 100vh;\n overflow: hidden;\n }\n #container {\n width: 100%;\n height: 100%;\n }\n #loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-family: Arial, sans-serif;\n }\n </style>\n</head>\n<body>\n <div id=\"container\"></div>\n <div id=\"loading\">Loading Gliffy...</div>\n\n <script>\n const scene = new THREE.Scene();\n scene.background = new THREE.Color(244, 244, 246);\n \n const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000);\n camera.position.z = 5;\n \n const renderer = new THREE.WebGLRenderer({ antialias: true });\n renderer.setSize(512, 512);\n renderer.setPixelRatio(window.devicePixelRatio);\n renderer.outputEncoding = THREE.sRGBEncoding;\n document.getElementById('container').appendChild(renderer.domElement);\n \n const ambientLight = new THREE.AmbientLight(0xffffff, 1.5);\n scene.add(ambientLight);\n \n const directionalLight = new THREE.DirectionalLight(0xffffff, 2);\n directionalLight.position.set(1, 1, 1);\n scene.add(directionalLight);\n\n const backLight = new THREE.DirectionalLight(0x6441a5, 1);\n backLight.position.set(-1, 0.5, -1);\n scene.add(backLight);\n \n const morphMeshes = [];\n \n let currentExpressionState = { 1: 0, 2: 0, 3: 0 };\n \n const loader = new THREE.GLTFLoader();\n \n const dracoLoader = new THREE.DRACOLoader();\n dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');\n loader.setDRACOLoader(dracoLoader);\n \n // Improved mouse tracking for gaze following\n const mouse = {\n current: new THREE.Vector2(),\n target: new THREE.Vector2(),\n lerp: 0.1\n };\n \n document.addEventListener('mousemove', (event) => {\n const rect = renderer.domElement.getBoundingClientRect();\n // Convert mouse position to normalized device coordinates (-1 to +1)\n mouse.target.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n mouse.target.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n });\n\n function lerpAngle(start, end, t) {\n return start + t * (end - start);\n }\n\n function calculateGazeRotation(mouseX, mouseY) {\n // Update smoothed mouse position\n mouse.current.x = lerpAngle(mouse.current.x, mouse.target.x, mouse.lerp);\n mouse.current.y = lerpAngle(mouse.current.y, mouse.target.y, mouse.lerp);\n \n // Scale and adjust the rotation ranges\n const rotX = -mouse.current.y * 0.4; // Look up/down (inverted)\n const rotY = mouse.current.x * 0.6; // Look left/right\n \n return {\n x: THREE.MathUtils.clamp(rotX, -0.4, 0.4),\n y: THREE.MathUtils.clamp(rotY, -0.6, 0.6)\n };\n }\n \n loader.load(\n 'https://glifxyz--glif-eternum-web-server.modal.run/gliffy/glb',\n function (gltf) {\n document.getElementById('loading').style.display = 'none';\n \n const model = gltf.scene;\n scene.add(model);\n \n const box = new THREE.Box3().setFromObject(model);\n const center = box.getCenter(new THREE.Vector3());\n const size = box.getSize(new THREE.Vector3());\n \n model.position.x = -center.x;\n model.position.y = -center.y;\n model.position.z = -center.z;\n \n const maxDim = Math.max(size.x, size.y, size.z);\n camera.position.z = maxDim * 2;\n camera.far = maxDim * 10;\n camera.updateProjectionMatrix();\n \n model.traverse((node) => {\n if (node.isMesh && node.morphTargetInfluences && node.morphTargetInfluences.length > 0) {\n morphMeshes.push(node); \n for (let i = 0; i < node.morphTargetInfluences.length; i++) {\n node.morphTargetInfluences[i] = 0;\n }\n }\n });\n \n startExpressionPolling();\n \n addIdleAnimation(model);\n },\n function (xhr) {\n const loadingPercentage = Math.floor((xhr.loaded / xhr.total) * 100);\n document.getElementById('loading').textContent = 'Loading Gliffy... ' + loadingPercentage + '%';\n },\n function (error) {\n console.error('Error loading model:', error);\n document.getElementById('loading').textContent = 'Error loading model. Please try again.';\n }\n );\n \n function startExpressionPolling() {\n fetchExpressionState();\n \n setInterval(fetchExpressionState, 2000);\n }\n \n function fetchExpressionState() {\n fetch('https://glifxyz--glif-eternum-web-server.modal.run/gliffy/expression')\n .then(response => response.json())\n .then(data => {\n console.log(data);\n console.log(currentExpressionState);\n if (JSON.stringify(data) !== JSON.stringify(currentExpressionState)) {\n currentExpressionState = data;\n console.log(currentExpressionState);\n applyExpressionState(data); }\n })\n .catch(error => {\n console.error('Error fetching expression state:', error);\n });\n }\n \n function applyExpressionState(expressionState) {\n const targetState = {};\n for (const [key, value] of Object.entries(expressionState)) {\n targetState[parseInt(key) - 1] = value;\n }\n \n morphMeshes.forEach(mesh => {\n console.log(mesh);\n console.log(mesh.morphTargetInfluences);\n const currentValues = {};\n for (let i = 0; i < mesh.morphTargetInfluences.length; i++) {\n currentValues[i] = mesh.morphTargetInfluences[i];\n }\n \n new TWEEN.Tween(currentValues)\n .to(targetState, 1500)\n .easing(TWEEN.Easing.Quadratic.InOut)\n .onUpdate(() => {\n for (let i = 0; i < mesh.morphTargetInfluences.length; i++) {\n if (i in targetState) {\n mesh.morphTargetInfluences[i] = currentValues[i];\n }\n }\n })\n .start();\n });\n }\n \n function addIdleAnimation(model) {\n // Remove floating animation\n \n // Improved gaze following\n function updateGaze() {\n const targetRotation = calculateGazeRotation(mouse.current.x, mouse.current.y);\n \n // Smooth rotation interpolation\n model.rotation.x = lerpAngle(model.rotation.x, targetRotation.x, 0.05);\n model.rotation.y = lerpAngle(model.rotation.y, targetRotation.y, 0.05);\n }\n\n // Add updateGaze to the animation loop\n const originalAnimate = animate;\n animate = function(time) {\n updateGaze();\n originalAnimate(time);\n };\n }\n \n window.addEventListener('resize', () => {\n camera.aspect = 512 / 512;\n camera.updateProjectionMatrix();\n renderer.setSize(512, 512);\n });\n \n function animate(time) {\n requestAnimationFrame(animate);\n TWEEN.update(time);\n renderer.render(scene, camera);\n }\n \n animate();\n </script>\n</body>\n</html>" } }
0
0
likes
glif - [gliffy]-spawn (remix) by dham