Refactoring: move out init, scene, utils

This commit is contained in:
Dejvino 2025-11-13 19:59:38 +01:00
parent ce4ae9e43e
commit f2057836eb
4 changed files with 188 additions and 131 deletions

View File

@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Retro TV Player</title> <title>Retro TV Player</title>
<!-- Load Tailwind CSS for styling --><script src="./vendor/tailwind-3.4.17.js" x-src="https://cdn.tailwindcss.com"></script> <!-- Load Tailwind CSS for styling --><script src="./vendor/tailwind-3.4.17.js" x-src="https://cdn.tailwindcss.com"></script>
<!-- Load Three.js for 3D rendering --><script src="./vendor/three.min.js" x-src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <!-- Load Three.js for 3D rendering --><script src="./vendor/three.min.js" x-src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="./src/tailwind-config.js"></script> <script src="./src/tailwind-config.js"></script>
<style> <style>
@ -34,6 +35,7 @@
<!-- Hidden Video Element --><video id="video" playsinline muted class="hidden"></video> <!-- Hidden Video Element --><video id="video" playsinline muted class="hidden"></video>
<!-- Controls for loading video --><div id="controls" class="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-20 flex flex-col items-center space-y-2"> <!-- Controls for loading video --><div id="controls" class="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-20 flex flex-col items-center space-y-2">
<!-- Hidden File Input that will be triggered by the button --><input type="file" id="fileInput" accept="video/mp4" class="hidden" multiple> <!-- Hidden File Input that will be triggered by the button --><input type="file" id="fileInput" accept="video/mp4" class="hidden" multiple>
<div class="flex space-x-4"> <div class="flex space-x-4">
@ -44,36 +46,16 @@
</div> </div>
<script src="./src/global-variables.js"></script> <script src="./src/global-variables.js"></script>
<script src="./src/utils.js"></script>
<script src="./src/scene.js"></script>
<script src="./src/init.js"></script>
<!-- 3D Canvas will be injected here by Three.js --><script> <!-- 3D Canvas will be injected here by Three.js -->
// --- Utility: Random Color (seeded) --- <script>
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();
}
/**
* Converts degrees to radians.
* @param {number} degrees
* @returns {number}
*/
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
// --- Seedable Random Number Generator (Mulberry32) ---
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;
}
// --- Initialization --- // --- Initialization ---
function init() { function init() {
// 1. Scene Setup (Dark, Ambient) // 1. Scene Setup (Dark, Ambient)
scene = new THREE.Scene(); scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000); scene.background = new THREE.Color(0x000000);
@ -82,6 +64,7 @@
camera = new THREE.PerspectiveCamera(FOV, window.innerWidth / window.innerHeight, 0.1, 1000); camera = new THREE.PerspectiveCamera(FOV, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 1.5, 4); camera.position.set(0, 1.5, 4);
// 3. Renderer Setup // 3. Renderer Setup
renderer = new THREE.WebGLRenderer({ antialias: true }); renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight); renderer.setSize(window.innerWidth, window.innerHeight);
@ -90,6 +73,7 @@
renderer.shadowMap.enabled = true; renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Softer shadows renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Softer shadows
container.appendChild(renderer.domElement); container.appendChild(renderer.domElement);
// 4. Lighting (Minimal and focused) // 4. Lighting (Minimal and focused)
@ -100,15 +84,18 @@
roomLight.position.set(0, 1.8, 0); roomLight.position.set(0, 1.8, 0);
scene.add(roomLight); scene.add(roomLight);
// 5. Build the entire scene with TV and surrounding objects // 5. Build the entire scene with TV and surrounding objects
createSceneObjects(); createSceneObjects();
// 6. Create the Dust Particle System // 6. Create the Dust Particle System
createDust(); createDust();
// 7. Create the Room Walls and Ceiling // 7. Create the Room Walls and Ceiling
createRoomWalls(); createRoomWalls();
// --- 8. Debug Visualization Helpers --- // --- 8. Debug Visualization Helpers ---
// Visual aids for the light source positions // Visual aids for the light source positions
if (debugLight && THREE.PointLightHelper) { if (debugLight && THREE.PointLightHelper) {
@ -119,6 +106,7 @@
const lampHelperPoint = new THREE.PointLightHelper(lampLightPoint, 0.1, 0x00ff00); // Green for lamp const lampHelperPoint = new THREE.PointLightHelper(lampLightPoint, 0.1, 0x00ff00); // Green for lamp
scene.add(lampHelperPoint); scene.add(lampHelperPoint);
} }
// 9. Event Listeners // 9. Event Listeners
window.addEventListener('resize', onWindowResize, false); window.addEventListener('resize', onWindowResize, false);
@ -135,100 +123,6 @@
// Start the animation loop // Start the animation loop
animate(); animate();
} }
// --- Room Walls Function ---
function createRoomWalls() {
const wallTexture = loader.load('./textures/wall.jpg');
wallTexture.wrapS = THREE.RepeatWrapping;
wallTexture.wrapT = THREE.RepeatWrapping;
// USING MeshPhongMaterial for specular highlights on walls
const wallMaterial = new THREE.MeshPhongMaterial({
map: wallTexture,
side: THREE.FrontSide,
shininess: 5,
specular: 0x111111 // Subtle reflection
});
// 1. Back Wall (behind the TV)
const backWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
backWall.position.set(0, roomHeight / 2, -roomSize / 2);
backWall.receiveShadow = true;
scene.add(backWall);
// 2. Front Wall (behind the camera)
const frontWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
frontWall.position.set(0, roomHeight / 2, roomSize / 2);
frontWall.rotation.y = Math.PI;
frontWall.receiveShadow = true;
scene.add(frontWall);
// 3. Left Wall
const leftWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
leftWall.rotation.y = Math.PI / 2;
leftWall.position.set(-roomSize / 2, roomHeight / 2, 0);
leftWall.receiveShadow = true;
scene.add(leftWall);
// 4. Right Wall
const rightWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
rightWall.rotation.y = -Math.PI / 2;
rightWall.position.set(roomSize / 2, roomHeight / 2, 0);
rightWall.receiveShadow = true;
scene.add(rightWall);
// 5. Ceiling
const ceilingGeometry = new THREE.PlaneGeometry(roomSize, roomSize);
const ceilingTexture = wallTexture;
ceilingTexture.repeat.set(4, 4);
// USING MeshPhongMaterial
const ceilingMaterial = new THREE.MeshPhongMaterial({
map: ceilingTexture,
side: THREE.FrontSide,
shininess: 5,
specular: 0x111111
});
const ceiling = new THREE.Mesh(ceilingGeometry, ceilingMaterial);
ceiling.rotation.x = Math.PI / 2;
ceiling.position.set(0, roomHeight, 0);
ceiling.receiveShadow = true;
scene.add(ceiling);
// --- 6. Add a Window to the Back Wall ---
const windowWidth = 1.5;
const windowHeight = 1.2;
const windowGeometry = new THREE.PlaneGeometry(windowWidth, windowHeight);
const nightSkyMaterial = new THREE.MeshBasicMaterial({
color: 0x0a1a3a,
emissive: 0x0a1a3a,
emissiveIntensity: 0.5,
side: THREE.FrontSide
});
const windowPane = new THREE.Mesh(windowGeometry, nightSkyMaterial);
const windowZ = -roomSize / 2 + 0.001;
windowPane.position.set(-3.5, roomHeight * 0.5 + 1.5, windowZ);
scene.add(windowPane);
// USING MeshPhongMaterial for the frame
const frameMaterial = new THREE.MeshPhongMaterial({ color: 0x4d3934, shininess: 10 });
// Horizontal bar
const hBarGeometry = new THREE.BoxGeometry(windowWidth + 0.1, 0.05, 0.05);
const hBar = new THREE.Mesh(hBarGeometry, frameMaterial);
hBar.position.set(windowPane.position.x, windowPane.position.y, windowZ);
hBar.castShadow = true;
scene.add(hBar);
// Vertical bar
const vBarGeometry = new THREE.BoxGeometry(0.05, windowHeight + 0.1, 0.05);
const vBar = new THREE.Mesh(vBarGeometry, frameMaterial);
vBar.position.set(windowPane.position.x, windowPane.position.y, windowZ);
vBar.castShadow = true;
scene.add(vBar);
}
function createBookshelf(x, z, rotationY, uniqueSeed) { function createBookshelf(x, z, rotationY, uniqueSeed) {
seed = uniqueSeed; // Reset seed for this specific shelf instance seed = uniqueSeed; // Reset seed for this specific shelf instance
@ -1099,17 +993,6 @@
return vcrGroup; return vcrGroup;
} }
// --- Helper function to format seconds into MM:SS ---
function formatTime(seconds) {
if (isNaN(seconds) || seconds < 0) return '00:00';
const totalSeconds = Math.floor(seconds);
const minutes = Math.floor(totalSeconds / 60);
const remainingSeconds = totalSeconds % 60;
const paddedMinutes = String(minutes).padStart(2, '0');
const paddedSeconds = String(remainingSeconds).padStart(2, '0');
return `${paddedMinutes}:${paddedSeconds}`;
}
function randomFlyTarget() { function randomFlyTarget() {
return new THREE.Vector3( return new THREE.Vector3(
(Math.random() - 0.5) * (ROOM_SIZE - 1), (Math.random() - 0.5) * (ROOM_SIZE - 1),

64
tv-player/src/init.js Normal file
View File

@ -0,0 +1,64 @@
// --- Initialization ---
function init() {
// 1. Scene Setup (Dark, Ambient)
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
// 2. Camera Setup
const FOV = 65;
camera = new THREE.PerspectiveCamera(FOV, 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(0x111111);
scene.add(ambientLight);
const roomLight = new THREE.PointLight(0xffaa55, 0.05, roomSize);
roomLight.position.set(0, 1.8, 0);
scene.add(roomLight);
// 5. Build the entire scene with TV and surrounding objects
createSceneObjects();
// 6. Create the Dust Particle System
createDust();
// 7. Create the Room Walls and Ceiling
createRoomWalls();
// --- 8. Debug Visualization Helpers ---
// Visual aids for the light source positions
if (debugLight && THREE.PointLightHelper) {
const screenHelper = new THREE.PointLightHelper(screenLight, 0.1, 0xff0000); // Red for screen
scene.add(screenHelper);
// Lamp Helper will now work since lampLight is added to the scene
const lampHelperPoint = new THREE.PointLightHelper(lampLightPoint, 0.1, 0x00ff00); // Green for lamp
scene.add(lampHelperPoint);
}
// 9. Event Listeners
window.addEventListener('resize', onWindowResize, false);
fileInput.addEventListener('change', loadVideoFile);
// Button logic
loadTapeButton.addEventListener('click', () => {
fileInput.click();
});
// Auto-advance to the next video when the current one finishes.
videoElement.addEventListener('ended', playNextVideo);
// Start the animation loop
animate();
}

76
tv-player/src/scene.js Normal file
View File

@ -0,0 +1,76 @@
// --- Room Walls Function ---
function createRoomWalls() {
const wallTexture = loader.load('./textures/wall.jpg');
wallTexture.wrapS = THREE.RepeatWrapping;
wallTexture.wrapT = THREE.RepeatWrapping;
// USING MeshPhongMaterial for specular highlights on walls
const wallMaterial = new THREE.MeshPhongMaterial({
map: wallTexture,
side: THREE.FrontSide,
shininess: 5,
specular: 0x111111 // Subtle reflection
});
// 1. Back Wall (behind the TV)
const backWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
backWall.position.set(0, roomHeight / 2, -roomSize / 2);
backWall.receiveShadow = true;
scene.add(backWall);
// 2. Front Wall (behind the camera)
const frontWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
frontWall.position.set(0, roomHeight / 2, roomSize / 2);
frontWall.rotation.y = Math.PI;
frontWall.receiveShadow = true;
scene.add(frontWall);
// 3. Left Wall
const leftWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
leftWall.rotation.y = Math.PI / 2;
leftWall.position.set(-roomSize / 2, roomHeight / 2, 0);
leftWall.receiveShadow = true;
scene.add(leftWall);
// 4. Right Wall
const rightWall = new THREE.Mesh(new THREE.PlaneGeometry(roomSize, roomHeight), wallMaterial);
rightWall.rotation.y = -Math.PI / 2;
rightWall.position.set(roomSize / 2, roomHeight / 2, 0);
rightWall.receiveShadow = true;
scene.add(rightWall);
// 5. Ceiling
const ceilingGeometry = new THREE.PlaneGeometry(roomSize, roomSize);
const ceilingTexture = wallTexture;
ceilingTexture.repeat.set(4, 4);
// USING MeshPhongMaterial
const ceilingMaterial = new THREE.MeshPhongMaterial({
map: ceilingTexture,
side: THREE.FrontSide,
shininess: 5,
specular: 0x111111
});
const ceiling = new THREE.Mesh(ceilingGeometry, ceilingMaterial);
ceiling.rotation.x = Math.PI / 2;
ceiling.position.set(0, roomHeight, 0);
ceiling.receiveShadow = true;
scene.add(ceiling);
// --- 6. Add a Window to the Back Wall ---
const windowWidth = 1.5;
const windowHeight = 1.2;
const windowGeometry = new THREE.PlaneGeometry(windowWidth, windowHeight);
const nightSkyMaterial = new THREE.MeshBasicMaterial({
color: 0x0a1a3a,
emissive: 0x0a1a3a,
emissiveIntensity: 0.5,
side: THREE.FrontSide
});
const windowPane = new THREE.Mesh(windowGeometry, nightSkyMaterial);
const windowZ = -roomSize / 2 + 0.001;
windowPane.position.set(-3.5, roomHeight * 0.5 + 1.5, windowZ);
scene.add(windowPane);
}

34
tv-player/src/utils.js Normal file
View File

@ -0,0 +1,34 @@
// --- 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();
}
/**
* Converts degrees to radians.
* @param {number} degrees
* @returns {number}
*/
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
// --- Seedable Random Number Generator (Mulberry32) ---
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;
}
// --- Helper function to format seconds into MM:SS ---
function formatTime(seconds) {
if (isNaN(seconds) || seconds === Infinity || seconds < 0) return '--:--';
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
const paddedMinutes = String(minutes).padStart(2, '0');
const paddedSeconds = String(remainingSeconds).padStart(2, '0');
return `${paddedMinutes}:${paddedSeconds}`;
}