Broshy's picture
Add 2 files
25dbc47 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Interactive Cat</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.min.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
touch-action: none;
}
#container {
position: relative;
width: 100vw;
height: 100vh;
}
#loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
font-family: Arial, sans-serif;
z-index: 100;
}
.spinner {
width: 50px;
height: 50px;
border: 5px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
margin-bottom: 20px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
#instructions {
position: absolute;
bottom: 20px;
width: 100%;
text-align: center;
color: white;
font-family: Arial, sans-serif;
background: rgba(0,0,0,0.5);
padding: 10px;
box-sizing: border-box;
}
</style>
</head>
<body class="bg-gray-900">
<div id="container">
<div id="loading">
<div class="spinner"></div>
<h1 class="text-2xl font-bold mb-2">Loading 3D Cat...</h1>
<p class="text-gray-300">Please wait while we prepare your interactive feline friend</p>
</div>
<div id="instructions">
<p class="text-lg">Touch and drag to rotate the cat | Pinch to zoom</p>
<p class="text-sm text-gray-300 hidden md:block">On desktop: Click and drag to rotate | Scroll to zoom</p>
</div>
</div>
<script>
// Initialize Three.js scene
const container = document.getElementById('container');
const loadingScreen = document.getElementById('loading');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x333333);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
// Add lights
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
directionalLight.castShadow = true;
scene.add(directionalLight);
const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight2.position.set(-1, -1, -1);
scene.add(directionalLight2);
// Add controls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.maxPolarAngle = Math.PI;
controls.minPolarAngle = 0;
controls.minDistance = 2;
controls.maxDistance = 10;
// Create a simple cat model (since we don't have a GLTF model)
function createCat() {
const group = new THREE.Group();
// Body (ellipsoid)
const bodyGeometry = new THREE.SphereGeometry(1, 32, 32);
bodyGeometry.scale(1.5, 0.8, 0.6);
const bodyMaterial = new THREE.MeshStandardMaterial({
color: 0xaaaaaa,
roughness: 0.7,
metalness: 0.1
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.castShadow = true;
body.receiveShadow = true;
group.add(body);
// Head (sphere)
const headGeometry = new THREE.SphereGeometry(0.6, 32, 32);
const headMaterial = new THREE.MeshStandardMaterial({
color: 0xaaaaaa,
roughness: 0.7,
metalness: 0.1
});
const head = new THREE.Mesh(headGeometry, headMaterial);
head.position.set(1.2, 0.2, 0);
head.castShadow = true;
group.add(head);
// Ears
const earGeometry = new THREE.ConeGeometry(0.3, 0.5, 32);
earGeometry.rotateX(Math.PI / 2);
const leftEar = new THREE.Mesh(earGeometry, headMaterial);
leftEar.position.set(1.5, 0.5, 0.3);
leftEar.rotation.z = -0.5;
group.add(leftEar);
const rightEar = new THREE.Mesh(earGeometry, headMaterial);
rightEar.position.set(1.5, 0.5, -0.3);
rightEar.rotation.z = 0.5;
group.add(rightEar);
// Eyes
const eyeGeometry = new THREE.SphereGeometry(0.1, 16, 16);
const eyeMaterial = new THREE.MeshStandardMaterial({ color: 0x111111 });
const leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial);
leftEye.position.set(1.6, 0.2, 0.2);
group.add(leftEye);
const rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial);
rightEye.position.set(1.6, 0.2, -0.2);
group.add(rightEye);
// Nose
const noseGeometry = new THREE.ConeGeometry(0.08, 0.2, 32);
noseGeometry.rotateX(Math.PI / 2);
const noseMaterial = new THREE.MeshStandardMaterial({ color: 0xff9999 });
const nose = new THREE.Mesh(noseGeometry, noseMaterial);
nose.position.set(1.7, 0.1, 0);
group.add(nose);
// Whiskers
const whiskerMaterial = new THREE.LineBasicMaterial({ color: 0xffffff });
// Left whiskers
const leftWhisker1 = new THREE.Line(
new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(1.6, 0.1, 0.2),
new THREE.Vector3(1.8, 0.15, 0.4)
]),
whiskerMaterial
);
group.add(leftWhisker1);
const leftWhisker2 = new THREE.Line(
new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(1.6, 0.1, 0.2),
new THREE.Vector3(1.8, 0.1, 0.4)
]),
whiskerMaterial
);
group.add(leftWhisker2);
const leftWhisker3 = new THREE.Line(
new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(1.6, 0.1, 0.2),
new THREE.Vector3(1.8, 0.05, 0.4)
]),
whiskerMaterial
);
group.add(leftWhisker3);
// Right whiskers
const rightWhisker1 = new THREE.Line(
new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(1.6, 0.1, -0.2),
new THREE.Vector3(1.8, 0.15, -0.4)
]),
whiskerMaterial
);
group.add(rightWhisker1);
const rightWhisker2 = new THREE.Line(
new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(1.6, 0.1, -0.2),
new THREE.Vector3(1.8, 0.1, -0.4)
]),
whiskerMaterial
);
group.add(rightWhisker2);
const rightWhisker3 = new THREE.Line(
new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(1.6, 0.1, -0.2),
new THREE.Vector3(1.8, 0.05, -0.4)
]),
whiskerMaterial
);
group.add(rightWhisker3);
// Legs
const legGeometry = new THREE.CylinderGeometry(0.15, 0.15, 0.5, 32);
const legMaterial = new THREE.MeshStandardMaterial({ color: 0xaaaaaa });
// Front legs
const frontLeftLeg = new THREE.Mesh(legGeometry, legMaterial);
frontLeftLeg.position.set(0.5, -0.5, 0.4);
frontLeftLeg.rotation.x = Math.PI / 2;
group.add(frontLeftLeg);
const frontRightLeg = new THREE.Mesh(legGeometry, legMaterial);
frontRightLeg.position.set(0.5, -0.5, -0.4);
frontRightLeg.rotation.x = Math.PI / 2;
group.add(frontRightLeg);
// Back legs
const backLeftLeg = new THREE.Mesh(legGeometry, legMaterial);
backLeftLeg.position.set(-0.5, -0.5, 0.4);
backLeftLeg.rotation.x = Math.PI / 2;
group.add(backLeftLeg);
const backRightLeg = new THREE.Mesh(legGeometry, legMaterial);
backRightLeg.position.set(-0.5, -0.5, -0.4);
backRightLeg.rotation.x = Math.PI / 2;
group.add(backRightLeg);
// Tail
const tailCurve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-1.2, -0.3, 0),
new THREE.Vector3(-1.8, -0.2, 0),
new THREE.Vector3(-2.2, 0, 0),
new THREE.Vector3(-2.4, 0.3, 0)
]);
const tailGeometry = new THREE.TubeGeometry(tailCurve, 64, 0.1, 16, false);
const tail = new THREE.Mesh(tailGeometry, bodyMaterial);
group.add(tail);
return group;
}
// Create and add cat to scene
const cat = createCat();
scene.add(cat);
// Add floor
const floorGeometry = new THREE.PlaneGeometry(20, 20);
const floorMaterial = new THREE.MeshStandardMaterial({
color: 0x666666,
roughness: 0.8,
metalness: 0.2
});
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.position.y = -0.8;
floor.receiveShadow = true;
scene.add(floor);
// Hide loading screen after a short delay (simulating loading)
setTimeout(() => {
loadingScreen.style.opacity = 0;
setTimeout(() => {
loadingScreen.style.display = 'none';
}, 500);
}, 1500);
// Handle window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Animation loop
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Broshy/deepsite-3d-cat-touch-controls" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>