Refactoring: move out init, scene, utils
This commit is contained in:
parent
ce4ae9e43e
commit
f2057836eb
@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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 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>
|
||||
<style>
|
||||
@ -34,6 +35,7 @@
|
||||
<!-- 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">
|
||||
|
||||
<!-- 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">
|
||||
@ -44,36 +46,16 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
// --- 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;
|
||||
}
|
||||
|
||||
<!-- 3D Canvas will be injected here by Three.js -->
|
||||
<script>
|
||||
// --- Initialization ---
|
||||
function init() {
|
||||
// 1. Scene Setup (Dark, Ambient)
|
||||
|
||||
scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x000000);
|
||||
|
||||
@ -82,6 +64,7 @@
|
||||
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);
|
||||
@ -90,6 +73,7 @@
|
||||
renderer.shadowMap.enabled = true;
|
||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Softer shadows
|
||||
|
||||
|
||||
container.appendChild(renderer.domElement);
|
||||
|
||||
// 4. Lighting (Minimal and focused)
|
||||
@ -100,15 +84,18 @@
|
||||
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) {
|
||||
@ -119,6 +106,7 @@
|
||||
const lampHelperPoint = new THREE.PointLightHelper(lampLightPoint, 0.1, 0x00ff00); // Green for lamp
|
||||
scene.add(lampHelperPoint);
|
||||
}
|
||||
|
||||
|
||||
// 9. Event Listeners
|
||||
window.addEventListener('resize', onWindowResize, false);
|
||||
@ -135,100 +123,6 @@
|
||||
// Start the animation loop
|
||||
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) {
|
||||
seed = uniqueSeed; // Reset seed for this specific shelf instance
|
||||
@ -1099,17 +993,6 @@
|
||||
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() {
|
||||
return new THREE.Vector3(
|
||||
(Math.random() - 0.5) * (ROOM_SIZE - 1),
|
||||
|
||||
64
tv-player/src/init.js
Normal file
64
tv-player/src/init.js
Normal 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
76
tv-player/src/scene.js
Normal 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
34
tv-player/src/utils.js
Normal 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}`;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user