Compare commits
6 Commits
9f91635569
...
4784d5ee26
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4784d5ee26 | ||
|
|
c870b7e5f3 | ||
|
|
022e5633dd | ||
|
|
a7cd8f5546 | ||
|
|
6a505f0af1 | ||
|
|
dc864ca24e |
108
tv-player/base.html
Normal file
108
tv-player/base.html
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Retro TV Video Player (3D)</title>
|
||||||
|
<!-- Load Tailwind CSS for styling --><script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<!-- Load Three.js for 3D rendering --><script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||||
|
<style>
|
||||||
|
/* Dark room aesthetic */
|
||||||
|
body {
|
||||||
|
background-color: #0d0d10;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- 3D Canvas will be injected here by Three.js --><script>
|
||||||
|
// --- Global Variables ---
|
||||||
|
let scene, camera, renderer;
|
||||||
|
|
||||||
|
const container = document.body;
|
||||||
|
|
||||||
|
// --- Initialization ---
|
||||||
|
function init() {
|
||||||
|
// 1. Scene Setup (Dark, Ambient)
|
||||||
|
scene = new THREE.Scene();
|
||||||
|
scene.background = new THREE.Color(0x000000);
|
||||||
|
|
||||||
|
// 2. Camera Setup
|
||||||
|
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||||
|
camera.position.set(0, 1.5, 4);
|
||||||
|
|
||||||
|
// 3. Renderer Setup
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
// Enable shadows on the renderer
|
||||||
|
renderer.shadowMap.enabled = true;
|
||||||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Softer shadows
|
||||||
|
|
||||||
|
container.appendChild(renderer.domElement);
|
||||||
|
|
||||||
|
// 4. Lighting (Minimal and focused)
|
||||||
|
const ambientLight = new THREE.AmbientLight(0x444444);
|
||||||
|
scene.add(ambientLight);
|
||||||
|
|
||||||
|
// 5. Build the entire scene
|
||||||
|
createSceneObjects();
|
||||||
|
|
||||||
|
// 6. Event Listeners
|
||||||
|
window.addEventListener('resize', onWindowResize, false);
|
||||||
|
|
||||||
|
// Start the animation loop
|
||||||
|
animate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Scene Modeling Function (omitted for brevity, assume content is stable) ---
|
||||||
|
function createSceneObjects() {
|
||||||
|
// 1. Floor
|
||||||
|
const floorGeometry = new THREE.PlaneGeometry(20, 20);
|
||||||
|
const floorMaterial = new THREE.MeshPhongMaterial({ color: 0x1a1a1a, shininess: 5 });
|
||||||
|
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
|
||||||
|
floor.rotation.x = -Math.PI / 2;
|
||||||
|
floor.position.y = 0;
|
||||||
|
floor.receiveShadow = true;
|
||||||
|
scene.add(floor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Animation Loop ---
|
||||||
|
function animate() {
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
|
||||||
|
// 1. Camera movement (omitted for brevity)
|
||||||
|
const globalTime = Date.now() * 0.00005;
|
||||||
|
const lookAtTime = Date.now() * 0.00003;
|
||||||
|
const camAmplitude = 0.6; const lookAmplitude = 0.05;
|
||||||
|
const baseX = 0; const baseY = 1.5; const baseZ = 4;
|
||||||
|
const baseTargetX = 0; const baseTargetY = 1.7; const baseTargetZ = 0.96;
|
||||||
|
const camOffsetX = Math.sin(globalTime * 3.1) * camAmplitude;
|
||||||
|
const camOffsetY = Math.cos(globalTime * 2.5) * camAmplitude * 0.4;
|
||||||
|
camera.position.x = baseX + camOffsetX;
|
||||||
|
camera.position.y = baseY + camOffsetY;
|
||||||
|
camera.position.z = baseZ;
|
||||||
|
const lookOffsetX = Math.sin(lookAtTime * 1.5) * lookAmplitude;
|
||||||
|
const lookOffsetY = Math.cos(lookAtTime * 1.2) * lookAmplitude;
|
||||||
|
camera.lookAt(baseTargetX + lookOffsetX, baseTargetY + lookOffsetY, baseTargetZ);
|
||||||
|
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Window Resize Handler ---
|
||||||
|
function onWindowResize() {
|
||||||
|
camera.aspect = window.innerWidth / window.innerHeight;
|
||||||
|
camera.updateProjectionMatrix();
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start everything on window load
|
||||||
|
window.onload = init;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
423
tv-player/bookshelves.html
Normal file
423
tv-player/bookshelves.html
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Fully Furnished Bookshelf Room (3D)</title>
|
||||||
|
<!-- Load Tailwind CSS for styling --><script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<!-- Load Three.js for 3D rendering --><script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||||
|
<style>
|
||||||
|
/* Light room aesthetic */
|
||||||
|
body {
|
||||||
|
background-color: #f0f0f5; /* Light grey background */
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- 3D Canvas will be injected here by Three.js --><script>
|
||||||
|
// --- Global Variables ---
|
||||||
|
let scene, camera, renderer;
|
||||||
|
let fanPropeller; // Reference for the rotating fan blades
|
||||||
|
|
||||||
|
// --- Seedable Random Number Generator (Mulberry32) ---
|
||||||
|
let seed = 12345; // Default seed, will be overridden per shelf
|
||||||
|
function seededRandom() {
|
||||||
|
let t = seed += 0x6D2B79F5;
|
||||||
|
t = Math.imul(t ^ t >>> 15, t | 1);
|
||||||
|
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
||||||
|
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = document.body;
|
||||||
|
|
||||||
|
// --- Initialization ---
|
||||||
|
function init() {
|
||||||
|
// 1. Scene Setup (White Room)
|
||||||
|
scene = new THREE.Scene();
|
||||||
|
scene.background = new THREE.Color(0xf0f0f0);
|
||||||
|
|
||||||
|
// 2. Camera Setup
|
||||||
|
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||||
|
camera.position.set(0, 1.7, 5);
|
||||||
|
|
||||||
|
// 3. Renderer Setup
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
renderer.shadowMap.enabled = true;
|
||||||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||||
|
|
||||||
|
container.appendChild(renderer.domElement);
|
||||||
|
|
||||||
|
// 4. Lighting
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xaaaaaa, 0.8);
|
||||||
|
scene.add(ambientLight);
|
||||||
|
|
||||||
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.7);
|
||||||
|
directionalLight.position.set(10, 15, 10);
|
||||||
|
directionalLight.castShadow = true;
|
||||||
|
directionalLight.shadow.mapSize.width = 2048; // Increased shadow map for sharper book shadows
|
||||||
|
directionalLight.shadow.mapSize.height = 2048;
|
||||||
|
scene.add(directionalLight);
|
||||||
|
|
||||||
|
// 5. Build scene
|
||||||
|
createSceneObjects();
|
||||||
|
|
||||||
|
// 6. Events
|
||||||
|
window.addEventListener('resize', onWindowResize, false);
|
||||||
|
animate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Utility: Random Color (seeded) ---
|
||||||
|
function getRandomColor() {
|
||||||
|
const hue = seededRandom();
|
||||||
|
const saturation = 0.6 + seededRandom() * 0.4;
|
||||||
|
const lightness = 0.3 + seededRandom() * 0.4;
|
||||||
|
return new THREE.Color().setHSL(hue, saturation, lightness).getHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- REVISED Procedural Bookshelf Generator ---
|
||||||
|
function createBookshelf(x, z, rotationY, uniqueSeed) {
|
||||||
|
seed = uniqueSeed; // Reset seed for this specific shelf instance
|
||||||
|
|
||||||
|
const shelfHeight = 2.2;
|
||||||
|
const shelfDepth = 0.35;
|
||||||
|
const shelfWidth = 1.2;
|
||||||
|
const numShelves = 6;
|
||||||
|
const woodThickness = 0.04;
|
||||||
|
const woodColor = 0x5c4033; // Darker, richer wood
|
||||||
|
|
||||||
|
const shelfGroup = new THREE.Group();
|
||||||
|
shelfGroup.position.set(x, 0, z);
|
||||||
|
shelfGroup.rotation.y = rotationY;
|
||||||
|
|
||||||
|
const woodMaterial = new THREE.MeshPhongMaterial({ color: woodColor, shininess: 30 });
|
||||||
|
|
||||||
|
// 1. Build Frame (Hollow box)
|
||||||
|
// Back Panel
|
||||||
|
const backGeo = new THREE.BoxGeometry(shelfWidth, shelfHeight, woodThickness);
|
||||||
|
const backPanel = new THREE.Mesh(backGeo, woodMaterial);
|
||||||
|
backPanel.position.set(0, shelfHeight / 2, -shelfDepth / 2 + woodThickness / 2);
|
||||||
|
backPanel.castShadow = true;
|
||||||
|
backPanel.receiveShadow = true;
|
||||||
|
shelfGroup.add(backPanel);
|
||||||
|
|
||||||
|
// Side Panels (Left & Right)
|
||||||
|
const sideGeo = new THREE.BoxGeometry(woodThickness, shelfHeight, shelfDepth);
|
||||||
|
const leftSide = new THREE.Mesh(sideGeo, woodMaterial);
|
||||||
|
leftSide.position.set(-shelfWidth / 2 + woodThickness / 2, shelfHeight / 2, 0);
|
||||||
|
leftSide.castShadow = true;
|
||||||
|
leftSide.receiveShadow = true;
|
||||||
|
shelfGroup.add(leftSide);
|
||||||
|
|
||||||
|
const rightSide = new THREE.Mesh(sideGeo, woodMaterial);
|
||||||
|
rightSide.position.set(shelfWidth / 2 - woodThickness / 2, shelfHeight / 2, 0);
|
||||||
|
rightSide.castShadow = true;
|
||||||
|
rightSide.receiveShadow = true;
|
||||||
|
shelfGroup.add(rightSide);
|
||||||
|
|
||||||
|
// Top & Bottom Panels
|
||||||
|
const topBottomGeo = new THREE.BoxGeometry(shelfWidth, woodThickness, shelfDepth);
|
||||||
|
const bottomPanel = new THREE.Mesh(topBottomGeo, woodMaterial);
|
||||||
|
bottomPanel.position.set(0, woodThickness / 2, 0);
|
||||||
|
bottomPanel.receiveShadow = true;
|
||||||
|
shelfGroup.add(bottomPanel);
|
||||||
|
|
||||||
|
const topPanel = new THREE.Mesh(topBottomGeo, woodMaterial);
|
||||||
|
topPanel.position.set(0, shelfHeight - woodThickness / 2, 0);
|
||||||
|
topPanel.castShadow = true;
|
||||||
|
shelfGroup.add(topPanel);
|
||||||
|
|
||||||
|
// 2. Individual Shelves & Books
|
||||||
|
const internalHeight = shelfHeight - (2 * woodThickness);
|
||||||
|
const shelfSpacing = internalHeight / numShelves;
|
||||||
|
const internalWidth = shelfWidth - (2 * woodThickness);
|
||||||
|
|
||||||
|
for (let i = 0; i < numShelves; i++) {
|
||||||
|
const currentShelfY = woodThickness + (i * shelfSpacing);
|
||||||
|
|
||||||
|
// Shelf board (skip for the very bottom one as we have a bottom panel)
|
||||||
|
if (i > 0) {
|
||||||
|
const shelfBoard = new THREE.Mesh(
|
||||||
|
new THREE.BoxGeometry(internalWidth, woodThickness, shelfDepth - woodThickness), // Slightly shallower to fit inside back panel
|
||||||
|
woodMaterial
|
||||||
|
);
|
||||||
|
shelfBoard.position.set(0, currentShelfY, woodThickness / 2); // Offset forward slightly
|
||||||
|
shelfBoard.castShadow = true;
|
||||||
|
shelfBoard.receiveShadow = true;
|
||||||
|
shelfGroup.add(shelfBoard);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Procedural Books
|
||||||
|
let currentBookX = -internalWidth / 2 + 0.01; // Start at left inside edge
|
||||||
|
const shelfSurfaceY = currentShelfY + woodThickness / 2;
|
||||||
|
|
||||||
|
while (currentBookX < internalWidth / 2 - 0.05) {
|
||||||
|
const bookWidth = 0.02 + seededRandom() * 0.05; // 2cm to 7cm wide
|
||||||
|
const bookHeight = (shelfSpacing * 0.6) + seededRandom() * (shelfSpacing * 0.3); // Vary height within shelf limits
|
||||||
|
const bookDepth = 0.15 + seededRandom() * 0.1; // Vary depth
|
||||||
|
|
||||||
|
if (currentBookX + bookWidth > internalWidth / 2) break;
|
||||||
|
|
||||||
|
const bookColor = getRandomColor();
|
||||||
|
const bookMat = new THREE.MeshPhongMaterial({ color: bookColor, shininess: 60 });
|
||||||
|
const bookGeo = new THREE.BoxGeometry(bookWidth, bookHeight, bookDepth);
|
||||||
|
const book = new THREE.Mesh(bookGeo, bookMat);
|
||||||
|
|
||||||
|
// Position: Resting on shelf, pushed towards the back with slight random variation
|
||||||
|
const depthVariation = seededRandom() * 0.05;
|
||||||
|
book.position.set(
|
||||||
|
currentBookX + bookWidth / 2,
|
||||||
|
shelfSurfaceY + bookHeight / 2,
|
||||||
|
-shelfDepth / 2 + woodThickness + bookDepth / 2 + depthVariation
|
||||||
|
);
|
||||||
|
|
||||||
|
book.castShadow = true;
|
||||||
|
book.receiveShadow = true;
|
||||||
|
shelfGroup.add(book);
|
||||||
|
|
||||||
|
currentBookX += bookWidth + 0.002; // Tiny gap between books
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.add(shelfGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDoor(x, z) {
|
||||||
|
const doorGroup = new THREE.Group();
|
||||||
|
doorGroup.position.set(x, 1.1, z); // Centered vertically for a 2.2m door
|
||||||
|
|
||||||
|
// Door Frame
|
||||||
|
const frameMaterial = new THREE.MeshPhongMaterial({ color: 0x473e3a }); // Dark wood for frame
|
||||||
|
const frameTop = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.1, 0.15), frameMaterial);
|
||||||
|
frameTop.position.set(0, 1.15, 0);
|
||||||
|
frameTop.castShadow = true;
|
||||||
|
doorGroup.add(frameTop);
|
||||||
|
|
||||||
|
const frameLeft = new THREE.Mesh(new THREE.BoxGeometry(0.1, 2.3, 0.15), frameMaterial);
|
||||||
|
frameLeft.position.set(-0.55, 0.05, 0);
|
||||||
|
frameLeft.castShadow = true;
|
||||||
|
doorGroup.add(frameLeft);
|
||||||
|
|
||||||
|
const frameRight = new THREE.Mesh(new THREE.BoxGeometry(0.1, 2.3, 0.15), frameMaterial);
|
||||||
|
frameRight.position.set(0.55, 0.05, 0);
|
||||||
|
frameRight.castShadow = true;
|
||||||
|
doorGroup.add(frameRight);
|
||||||
|
|
||||||
|
// Main Door Panel
|
||||||
|
const doorMaterial = new THREE.MeshPhongMaterial({ color: 0x8b5a2b, shininess: 10 }); // Lighter wood for door
|
||||||
|
const door = new THREE.Mesh(new THREE.BoxGeometry(1.0, 2.2, 0.08), doorMaterial);
|
||||||
|
door.castShadow = true;
|
||||||
|
door.receiveShadow = true;
|
||||||
|
doorGroup.add(door);
|
||||||
|
|
||||||
|
// Door Knob
|
||||||
|
const knobMaterial = new THREE.MeshPhongMaterial({ color: 0xd4af37, shininess: 100 }); // Gold/Brass
|
||||||
|
const knob = new THREE.Mesh(new THREE.SphereGeometry(0.05, 16, 16), knobMaterial);
|
||||||
|
knob.position.set(0.4, 0, 0.06); // Position on the right side of the door
|
||||||
|
knob.castShadow = true;
|
||||||
|
doorGroup.add(knob);
|
||||||
|
|
||||||
|
scene.add(doorGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDeskAndChair(x, z, rotationY) {
|
||||||
|
const group = new THREE.Group();
|
||||||
|
const woodMaterial = new THREE.MeshPhongMaterial({ color: 0x8b4513, shininess: 20 });
|
||||||
|
const metalMaterial = new THREE.MeshPhongMaterial({ color: 0x777777, shininess: 50 });
|
||||||
|
const fabricMaterial = new THREE.MeshPhongMaterial({ color: 0x2c3e50, shininess: 10 });
|
||||||
|
|
||||||
|
// Desk
|
||||||
|
const deskTop = new THREE.Mesh(new THREE.BoxGeometry(1.5, 0.05, 0.6), woodMaterial);
|
||||||
|
deskTop.position.set(0, 0.75, 0);
|
||||||
|
deskTop.castShadow = true;
|
||||||
|
group.add(deskTop);
|
||||||
|
|
||||||
|
const legGeo = new THREE.BoxGeometry(0.05, 0.75, 0.05);
|
||||||
|
const leg1 = new THREE.Mesh(legGeo, metalMaterial); leg1.position.set(0.7, 0.375, 0.25); leg1.castShadow = true; group.add(leg1);
|
||||||
|
const leg2 = new THREE.Mesh(legGeo, metalMaterial); leg2.position.set(-0.7, 0.375, 0.25); leg2.castShadow = true; group.add(leg2);
|
||||||
|
|
||||||
|
// Chair
|
||||||
|
const chairSeat = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.05, 0.5), fabricMaterial);
|
||||||
|
chairSeat.position.set(0, 0.45, 1.0); chairSeat.castShadow = true; group.add(chairSeat);
|
||||||
|
const chairBack = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.5, 0.05), fabricMaterial);
|
||||||
|
chairBack.position.set(0, 0.7, 0.75); chairBack.castShadow = true; group.add(chairBack);
|
||||||
|
|
||||||
|
const cLegGeo = new THREE.CylinderGeometry(0.02, 0.02, 0.45, 8);
|
||||||
|
const cLeg1 = new THREE.Mesh(cLegGeo, metalMaterial); cLeg1.position.set(0.2, 0.225, 1.2); cLeg1.castShadow = true; group.add(cLeg1);
|
||||||
|
const cLeg2 = new THREE.Mesh(cLegGeo, metalMaterial); cLeg2.position.set(-0.2, 0.225, 1.2); cLeg2.castShadow = true; group.add(cLeg2);
|
||||||
|
|
||||||
|
group.position.set(x, 0, z);
|
||||||
|
group.rotation.y = rotationY;
|
||||||
|
scene.add(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWallDecor() {
|
||||||
|
// 1. Purple Image on Left Wall
|
||||||
|
const imagePlane = new THREE.Mesh(new THREE.PlaneGeometry(1.0, 0.7), new THREE.MeshPhongMaterial({ color: 0x9c27b0 }));
|
||||||
|
imagePlane.rotation.y = Math.PI / 2;
|
||||||
|
imagePlane.position.set(-4.95, 2.0, -3.0);
|
||||||
|
scene.add(imagePlane);
|
||||||
|
|
||||||
|
const frame = new THREE.Mesh(new THREE.BoxGeometry(1.05, 0.75, 0.05), new THREE.MeshPhongMaterial({ color: 0x444444 }));
|
||||||
|
frame.rotation.y = Math.PI / 2;
|
||||||
|
frame.position.set(-4.98, 2.0, -3.0);
|
||||||
|
scene.add(frame);
|
||||||
|
|
||||||
|
// 2. Calendar (Generated via Canvas Texture)
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 512;
|
||||||
|
canvas.height = 768;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Paper background
|
||||||
|
ctx.fillStyle = '#fdfdfd';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Red Header
|
||||||
|
ctx.fillStyle = '#d32f2f';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, 150);
|
||||||
|
|
||||||
|
// Month Text
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = 'bold 80px Inter, sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillText('JANUARY', canvas.width / 2, 75);
|
||||||
|
|
||||||
|
// YEAR
|
||||||
|
ctx.fillStyle = '#222222';
|
||||||
|
ctx.font = 'bold 160px Inter, sans-serif';
|
||||||
|
ctx.fillText('1987', canvas.width / 2, 280);
|
||||||
|
|
||||||
|
// Grid (simple representation of days)
|
||||||
|
ctx.strokeStyle = '#999999';
|
||||||
|
ctx.lineWidth = 4;
|
||||||
|
const startY = 400;
|
||||||
|
const endY = 720;
|
||||||
|
const startX = 40;
|
||||||
|
const endX = 472;
|
||||||
|
const rows = 5;
|
||||||
|
const cols = 7;
|
||||||
|
|
||||||
|
// Horizontal lines
|
||||||
|
for (let i = 0; i <= rows; i++) {
|
||||||
|
const y = startY + (i * (endY - startY) / rows);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(startX, y);
|
||||||
|
ctx.lineTo(endX, y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
// Vertical lines
|
||||||
|
for (let i = 0; i <= cols; i++) {
|
||||||
|
const x = startX + (i * (endX - startX) / cols);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, startY);
|
||||||
|
ctx.lineTo(x, endY);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
const calendarTexture = new THREE.CanvasTexture(canvas);
|
||||||
|
// Use MeshBasicMaterial so it's bright and legible regardless of lighting angles,
|
||||||
|
// or MeshPhongMaterial with low shininess for a matte paper look.
|
||||||
|
const calendarMat = new THREE.MeshPhongMaterial({
|
||||||
|
map: calendarTexture,
|
||||||
|
shininess: 5,
|
||||||
|
color: 0xffffff // Ensure white tint doesn't darken texture
|
||||||
|
});
|
||||||
|
|
||||||
|
const calendarGeo = new THREE.PlaneGeometry(1.5, 2.25); // Large size
|
||||||
|
const calendar = new THREE.Mesh(calendarGeo, calendarMat);
|
||||||
|
calendar.rotation.y = Math.PI / 2;
|
||||||
|
// Positioned on the left wall, further forward so it's visible
|
||||||
|
calendar.position.set(-4.95, 2.5, 2.0);
|
||||||
|
scene.add(calendar);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCeilingFan() {
|
||||||
|
const fanGroup = new THREE.Group();
|
||||||
|
const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.5, 8), new THREE.MeshPhongMaterial({ color: 0xaaaaaa }));
|
||||||
|
pole.position.set(0, 4.75, 0); fanGroup.add(pole);
|
||||||
|
|
||||||
|
fanPropeller = new THREE.Group();
|
||||||
|
const bladeGeo = new THREE.BoxGeometry(0.8, 0.02, 0.15);
|
||||||
|
const bladeMat = new THREE.MeshPhongMaterial({ color: 0x555555 });
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const blade = new THREE.Mesh(bladeGeo, bladeMat);
|
||||||
|
blade.position.set(0.4, 0, 0); blade.castShadow = true;
|
||||||
|
const pivot = new THREE.Group(); pivot.add(blade); pivot.rotation.y = (i * Math.PI * 2) / 3;
|
||||||
|
fanPropeller.add(pivot);
|
||||||
|
}
|
||||||
|
fanPropeller.position.set(0, 4.5, 0); fanGroup.add(fanPropeller);
|
||||||
|
scene.add(fanGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Main Scene ---
|
||||||
|
function createSceneObjects() {
|
||||||
|
// Room shell
|
||||||
|
const wallMat = new THREE.MeshPhongMaterial({ color: 0xfcfcfc, side: THREE.DoubleSide });
|
||||||
|
const floorMat = new THREE.MeshPhongMaterial({ color: 0xe0e0e0 });
|
||||||
|
|
||||||
|
const floor = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), floorMat);
|
||||||
|
floor.rotation.x = -Math.PI / 2; floor.receiveShadow = true; scene.add(floor);
|
||||||
|
|
||||||
|
const backWall = new THREE.Mesh(new THREE.PlaneGeometry(10, 5), wallMat);
|
||||||
|
backWall.position.set(0, 2.5, -5); backWall.receiveShadow = true; scene.add(backWall);
|
||||||
|
|
||||||
|
const rightWall = new THREE.Mesh(new THREE.PlaneGeometry(10, 5), wallMat);
|
||||||
|
rightWall.rotation.y = -Math.PI / 2; rightWall.position.set(5, 2.5, 0); rightWall.receiveShadow = true; scene.add(rightWall);
|
||||||
|
|
||||||
|
const leftWall = new THREE.Mesh(new THREE.PlaneGeometry(10, 5), wallMat);
|
||||||
|
leftWall.rotation.y = Math.PI / 2; leftWall.position.set(-5, 2.5, 0); leftWall.receiveShadow = true; scene.add(leftWall);
|
||||||
|
|
||||||
|
const ceiling = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), wallMat);
|
||||||
|
ceiling.rotation.x = Math.PI / 2; ceiling.position.set(0, 5, 0); scene.add(ceiling);
|
||||||
|
|
||||||
|
// Placed Objects
|
||||||
|
// Back Wall: Door on the left, shelves shifted right
|
||||||
|
createDoor(-3.0, -4.95); // New door position
|
||||||
|
|
||||||
|
// Adjusted shelf positions to make room for the door
|
||||||
|
createBookshelf(-1.2, -4.8, 0, 101);
|
||||||
|
createBookshelf(0.6, -4.8, 0, 102);
|
||||||
|
createBookshelf(2.4, -4.8, 0, 103);
|
||||||
|
|
||||||
|
// Right wall shelves (Seeds: 201, 202, 203)
|
||||||
|
createBookshelf(4.8, -2.0, -Math.PI / 2, 201);
|
||||||
|
createBookshelf(4.8, 0, -Math.PI / 2, 202);
|
||||||
|
createBookshelf(4.8, 2.0, -Math.PI / 2, 203);
|
||||||
|
|
||||||
|
createDeskAndChair(-3.0, 2.5, -Math.PI * 0.15);
|
||||||
|
createWallDecor();
|
||||||
|
createCeilingFan();
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
if (fanPropeller) fanPropeller.rotation.y += 0.05;
|
||||||
|
|
||||||
|
// Camera sway
|
||||||
|
const time = Date.now() * 0.0005;
|
||||||
|
camera.position.x = Math.sin(time * 0.3) * 0.2;
|
||||||
|
camera.position.y = 1.7 + Math.cos(time * 0.2) * 0.1;
|
||||||
|
camera.lookAt(0, 1.7, 0);
|
||||||
|
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWindowResize() {
|
||||||
|
camera.aspect = window.innerWidth / window.innerHeight;
|
||||||
|
camera.updateProjectionMatrix();
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = init;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1080
tv-player/index.html
1080
tv-player/index.html
File diff suppressed because it is too large
Load Diff
BIN
tv-player/textures/floor.jpg
Normal file
BIN
tv-player/textures/floor.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 MiB |
Loading…
Reference in New Issue
Block a user