Refactoring: move effects to a manager, chunk animate loop contents
This commit is contained in:
parent
043552f36c
commit
75c87c9d03
@ -51,6 +51,7 @@
|
|||||||
<script src="./src/video-player.js"></script>
|
<script src="./src/video-player.js"></script>
|
||||||
<script src="./src/effects_dust.js"></script>
|
<script src="./src/effects_dust.js"></script>
|
||||||
<script src="./src/effects_flies.js"></script>
|
<script src="./src/effects_flies.js"></script>
|
||||||
|
<script src="./src/EffectsManager.js"></script>
|
||||||
<script src="./src/vcr-display.js"></script>
|
<script src="./src/vcr-display.js"></script>
|
||||||
<script src="./src/animate.js"></script>
|
<script src="./src/animate.js"></script>
|
||||||
<script src="./src/init.js"></script>
|
<script src="./src/init.js"></script>
|
||||||
|
|||||||
21
tv-player/src/EffectsManager.js
Normal file
21
tv-player/src/EffectsManager.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
class EffectsManager {
|
||||||
|
constructor(scene) {
|
||||||
|
this.effects = [];
|
||||||
|
this._initializeEffects(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
_initializeEffects(scene) {
|
||||||
|
// Add all desired effects here.
|
||||||
|
// This is now the single place to manage which effects are active.
|
||||||
|
this.addEffect(new DustEffect(scene));
|
||||||
|
this.addEffect(new FliesEffect(scene));
|
||||||
|
}
|
||||||
|
|
||||||
|
addEffect(effect) {
|
||||||
|
this.effects.push(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.effects.forEach(effect => effect.update());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,20 +1,4 @@
|
|||||||
// --- Animation Loop ---
|
function updateCamera() {
|
||||||
function animate() {
|
|
||||||
requestAnimationFrame(animate);
|
|
||||||
|
|
||||||
// 1. Dust animation: slow downward drift
|
|
||||||
if (dust) {
|
|
||||||
const positions = dust.geometry.attributes.position.array;
|
|
||||||
for (let i = 1; i < positions.length; i += 3) {
|
|
||||||
positions[i] -= 0.001;
|
|
||||||
if (positions[i] < -2) {
|
|
||||||
positions[i] = 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dust.geometry.attributes.position.needsUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Camera movement (Gentle, random hovering)
|
|
||||||
const globalTime = Date.now() * 0.00005;
|
const globalTime = Date.now() * 0.00005;
|
||||||
const lookAtTime = Date.now() * 0.00003;
|
const lookAtTime = Date.now() * 0.00003;
|
||||||
|
|
||||||
@ -45,13 +29,10 @@ function animate() {
|
|||||||
const lookOffsetY = Math.cos(lookAtTime * 1.2) * lookAmplitude;
|
const lookOffsetY = Math.cos(lookAtTime * 1.2) * lookAmplitude;
|
||||||
|
|
||||||
// Apply lookAt to the subtly shifted target
|
// Apply lookAt to the subtly shifted target
|
||||||
camera.lookAt(
|
camera.lookAt(baseTargetX + lookOffsetX, baseTargetY + lookOffsetY, baseTargetZ);
|
||||||
baseTargetX + lookOffsetX,
|
}
|
||||||
baseTargetY + lookOffsetY,
|
|
||||||
baseTargetZ
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3. Lamp Flicker Effect
|
function updateLampFlicker() {
|
||||||
const flickerChance = 0.995;
|
const flickerChance = 0.995;
|
||||||
const restoreRate = 0.15;
|
const restoreRate = 0.15;
|
||||||
|
|
||||||
@ -66,55 +47,51 @@ function animate() {
|
|||||||
lampLightSpot.intensity = lampLightIntensity;
|
lampLightSpot.intensity = lampLightIntensity;
|
||||||
lampLightPoint.intensity = lampLightIntensity;
|
lampLightPoint.intensity = lampLightIntensity;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Screen Light Pulse and Movement Effect (Updated)
|
function updateScreenLight() {
|
||||||
if (isVideoLoaded && screenLight.intensity > 0) {
|
if (isVideoLoaded && screenLight.intensity > 0) {
|
||||||
// A. Pulse Effect (Intensity Fluctuation)
|
|
||||||
// Generate a small random fluctuation for the pulse (Range: 1.35 to 1.65 around base 1.5)
|
|
||||||
const pulseTarget = originalScreenIntensity + (Math.random() - 0.5) * screenIntensityPulse;
|
const pulseTarget = originalScreenIntensity + (Math.random() - 0.5) * screenIntensityPulse;
|
||||||
// Smoothly interpolate towards the new target fluctuation
|
|
||||||
screenLight.intensity = THREE.MathUtils.lerp(screenLight.intensity, pulseTarget, 0.1);
|
screenLight.intensity = THREE.MathUtils.lerp(screenLight.intensity, pulseTarget, 0.1);
|
||||||
|
|
||||||
// B. Movement Effect (Subtle circle around the screen center - circling the room area)
|
|
||||||
const lightTime = Date.now() * 0.0001;
|
const lightTime = Date.now() * 0.0001;
|
||||||
const radius = 0.01;
|
const radius = 0.01;
|
||||||
const centerX = 0;
|
const centerX = 0;
|
||||||
const centerY = 1.5;
|
const centerY = 1.5;
|
||||||
//const centerZ = 1.2; // Use the updated Z position of the light source
|
|
||||||
|
|
||||||
// Move the light in a subtle, erratic circle
|
|
||||||
screenLight.position.x = centerX + Math.cos(lightTime) * radius;
|
screenLight.position.x = centerX + Math.cos(lightTime) * radius;
|
||||||
screenLight.position.y = centerY + Math.sin(lightTime * 1.5) * radius * 0.5; // Slightly different freq for Y
|
screenLight.position.y = centerY + Math.sin(lightTime * 1.5) * radius * 0.5; // Slightly different freq for Y
|
||||||
//screenLight.position.z = centerZ; // Keep Z constant at the screen light plane
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 5. Update video texture (essential to grab the next frame)
|
function updateVideo() {
|
||||||
if (videoTexture) {
|
if (videoTexture) {
|
||||||
videoTexture.needsUpdate = true;
|
videoTexture.needsUpdate = true;
|
||||||
|
|
||||||
// Update time display in the animation loop
|
|
||||||
if (isVideoLoaded && videoElement.readyState >= 3) {
|
|
||||||
const currentTime = formatTime(videoElement.currentTime);
|
|
||||||
const duration = formatTime(videoElement.duration);
|
|
||||||
console.info(`Tape ${currentVideoIndex + 1} of ${videoUrls.length}. Time: ${currentTime} / ${duration}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateFlies();
|
function updateVcr() {
|
||||||
|
|
||||||
const currentTime = baseTime + videoElement.currentTime;
|
const currentTime = baseTime + videoElement.currentTime;
|
||||||
|
|
||||||
// Simulate playback time
|
|
||||||
if (Math.abs(currentTime - lastUpdateTime) > 0.1) {
|
if (Math.abs(currentTime - lastUpdateTime) > 0.1) {
|
||||||
updateVcrDisplay(currentTime);
|
updateVcrDisplay(currentTime);
|
||||||
lastUpdateTime = currentTime;
|
lastUpdateTime = currentTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blink the colon every second
|
|
||||||
if (currentTime - lastBlinkToggleTime > 0.5) { // Blink every 0.5 seconds
|
if (currentTime - lastBlinkToggleTime > 0.5) { // Blink every 0.5 seconds
|
||||||
blinkState = !blinkState;
|
blinkState = !blinkState;
|
||||||
lastBlinkToggleTime = currentTime;
|
lastBlinkToggleTime = currentTime;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Animation Loop ---
|
||||||
|
function animate() {
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
|
||||||
|
effectsManager.update();
|
||||||
|
updateCamera();
|
||||||
|
updateLampFlicker();
|
||||||
|
updateScreenLight();
|
||||||
|
updateVideo();
|
||||||
|
updateVcr();
|
||||||
|
|
||||||
// RENDER!
|
// RENDER!
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
|
|||||||
@ -1,28 +1,45 @@
|
|||||||
// --- Dust Particle System Function ---
|
class DustEffect {
|
||||||
function createDust() {
|
constructor(scene) {
|
||||||
const particleCount = 2000;
|
this.dust = null;
|
||||||
const particlesGeometry = new THREE.BufferGeometry();
|
this._create(scene);
|
||||||
const positions = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < particleCount; i++) {
|
|
||||||
positions.push(
|
|
||||||
(Math.random() - 0.5) * 15,
|
|
||||||
Math.random() * 10,
|
|
||||||
(Math.random() - 0.5) * 15
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// Use THREE.Float32BufferAttribute to correctly set the position attribute
|
|
||||||
particlesGeometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
||||||
|
|
||||||
const particleMaterial = new THREE.PointsMaterial({
|
_create(scene) {
|
||||||
color: 0xffffff,
|
const particleCount = 2000;
|
||||||
size: 0.015,
|
const particlesGeometry = new THREE.BufferGeometry();
|
||||||
transparent: true,
|
const positions = [];
|
||||||
opacity: 0.08,
|
|
||||||
blending: THREE.AdditiveBlending
|
|
||||||
});
|
|
||||||
|
|
||||||
dust = new THREE.Points(particlesGeometry, particleMaterial);
|
for (let i = 0; i < particleCount; i++) {
|
||||||
// Dust particles generally don't cast or receive shadows in this context
|
positions.push(
|
||||||
scene.add(dust);
|
(Math.random() - 0.5) * 15,
|
||||||
|
Math.random() * 10,
|
||||||
|
(Math.random() - 0.5) * 15
|
||||||
|
);
|
||||||
|
}
|
||||||
|
particlesGeometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
||||||
|
|
||||||
|
const particleMaterial = new THREE.PointsMaterial({
|
||||||
|
color: 0xffffff,
|
||||||
|
size: 0.015,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.08,
|
||||||
|
blending: THREE.AdditiveBlending
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dust = new THREE.Points(particlesGeometry, particleMaterial);
|
||||||
|
scene.add(this.dust);
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if (this.dust) {
|
||||||
|
const positions = this.dust.geometry.attributes.position.array;
|
||||||
|
for (let i = 1; i < positions.length; i += 3) {
|
||||||
|
positions[i] -= 0.001;
|
||||||
|
if (positions[i] < -2) {
|
||||||
|
positions[i] = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dust.geometry.attributes.position.needsUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,174 +1,129 @@
|
|||||||
function randomFlyTarget() {
|
const FLIES_COUNT = 2;
|
||||||
return new THREE.Vector3(
|
|
||||||
(Math.random() - 0.5) * (ROOM_SIZE - 1),
|
|
||||||
FLIGHT_HEIGHT_MIN + Math.random() * (FLIGHT_HEIGHT_MAX - FLIGHT_HEIGHT_MIN),
|
|
||||||
(Math.random() - 0.5) * (ROOM_SIZE - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
class FliesEffect {
|
||||||
* Creates a single fly mesh (small cone/tetrahedron).
|
constructor(scene) {
|
||||||
* @returns {THREE.Group}
|
this.flies = [];
|
||||||
*/
|
this._setupFlies(scene);
|
||||||
function createFlyMesh() {
|
|
||||||
const flyGroup = new THREE.Group();
|
|
||||||
|
|
||||||
const flyMaterial = new THREE.MeshPhongMaterial({
|
|
||||||
color: 0x111111, // Dark fly color
|
|
||||||
shininess: 50,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Small Cone/Tetrahedron for a simple shape
|
|
||||||
const bodyGeometry = new THREE.ConeGeometry(0.01, 0.02, 3);
|
|
||||||
const body = new THREE.Mesh(bodyGeometry, flyMaterial);
|
|
||||||
body.rotation.x = degToRad(90); // Point nose in Z direction
|
|
||||||
|
|
||||||
body.castShadow = true;
|
|
||||||
body.receiveShadow = true;
|
|
||||||
flyGroup.add(body);
|
|
||||||
|
|
||||||
// Initial state and parameters for the fly
|
|
||||||
flyGroup.userData = {
|
|
||||||
state: 'flying', // 'flying' or 'landed'
|
|
||||||
landTimer: 0,
|
|
||||||
t: 0, // Curve progression t parameter (0 to 1)
|
|
||||||
speed: FLY_FLIGHT_SPEED_FACTOR + Math.random() * 0.01,
|
|
||||||
curve: null,
|
|
||||||
landCheckTimer: 0,
|
|
||||||
oscillationTime: Math.random() * 100, // For smooth y-axis buzzing
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initial random position
|
|
||||||
flyGroup.position = randomFlyTarget();
|
|
||||||
|
|
||||||
return flyGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new Quadratic Bezier curve for a fly's flight path.
|
|
||||||
* @param {THREE.Group} fly - The fly mesh group.
|
|
||||||
* @param {THREE.Vector3} endPoint - The target position for the end of the curve.
|
|
||||||
*/
|
|
||||||
function createFlyCurve(fly, endPoint) {
|
|
||||||
const startPoint = fly.position.clone();
|
|
||||||
|
|
||||||
// Calculate the midpoint
|
|
||||||
const midPoint = new THREE.Vector3().lerpVectors(startPoint, endPoint, 0.5);
|
|
||||||
|
|
||||||
// Calculate a random offset for the control point to create curvature
|
|
||||||
const offsetMagnitude = startPoint.distanceTo(endPoint) * 0.5;
|
|
||||||
const offsetAngle = Math.random() * Math.PI * 2;
|
|
||||||
|
|
||||||
// Displace the control point randomly to create a swooping path.
|
|
||||||
// Control point y is usually higher than start/end for a nice arc.
|
|
||||||
const controlPoint = new THREE.Vector3(
|
|
||||||
midPoint.x + Math.cos(offsetAngle) * offsetMagnitude * 0.5,
|
|
||||||
midPoint.y + Math.random() * 0.5 + 0.5,
|
|
||||||
midPoint.z + Math.sin(offsetAngle) * offsetMagnitude * 0.5
|
|
||||||
);
|
|
||||||
|
|
||||||
fly.userData.curve = new THREE.QuadraticBezierCurve3(
|
|
||||||
startPoint,
|
|
||||||
controlPoint,
|
|
||||||
endPoint
|
|
||||||
);
|
|
||||||
fly.userData.t = 0; // Reset progression
|
|
||||||
fly.userData.landCheckTimer = 50 + Math.random() * 50; // New landing decision window
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and places the 'flies' meshes.
|
|
||||||
*/
|
|
||||||
function setupFlies() {
|
|
||||||
for (let i = 0; i < FLIES_COUNT; i++) {
|
|
||||||
const fly = createFlyMesh();
|
|
||||||
scene.add(fly);
|
|
||||||
flies.push(fly);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
_randomFlyTarget() {
|
||||||
* Updates the position and state of the flies using Bezier curves.
|
return new THREE.Vector3(
|
||||||
*/
|
(Math.random() - 0.5) * (ROOM_SIZE - 1),
|
||||||
function updateFlies() {
|
FLIGHT_HEIGHT_MIN + Math.random() * (FLIGHT_HEIGHT_MAX - FLIGHT_HEIGHT_MIN),
|
||||||
flies.forEach(fly => {
|
(Math.random() - 0.5) * (ROOM_SIZE - 1)
|
||||||
const data = fly.userData;
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (data.state === 'flying' || data.state === 'landing') {
|
_createFlyMesh() {
|
||||||
|
const flyGroup = new THREE.Group();
|
||||||
|
const flyMaterial = new THREE.MeshPhongMaterial({ color: 0x111111, shininess: 50 });
|
||||||
|
const bodyGeometry = new THREE.ConeGeometry(0.01, 0.02, 3);
|
||||||
|
const body = new THREE.Mesh(bodyGeometry, flyMaterial);
|
||||||
|
body.rotation.x = degToRad(90);
|
||||||
|
body.castShadow = true;
|
||||||
|
body.receiveShadow = true;
|
||||||
|
flyGroup.add(body);
|
||||||
|
|
||||||
if (!data.curve) {
|
flyGroup.userData = {
|
||||||
// Initialize the first curve
|
state: 'flying',
|
||||||
const newTargetPos = randomFlyTarget();
|
landTimer: 0,
|
||||||
createFlyCurve(fly, newTargetPos);
|
t: 0,
|
||||||
data.t = 0;
|
speed: FLY_FLIGHT_SPEED_FACTOR + Math.random() * 0.01,
|
||||||
}
|
curve: null,
|
||||||
|
landCheckTimer: 0,
|
||||||
|
oscillationTime: Math.random() * 100,
|
||||||
|
};
|
||||||
|
|
||||||
// Advance curve progression
|
flyGroup.position.copy(this._randomFlyTarget());
|
||||||
data.t += data.speed;
|
return flyGroup;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for landing readiness during the flight path
|
_createFlyCurve(fly, endPoint) {
|
||||||
data.landCheckTimer--;
|
const startPoint = fly.position.clone();
|
||||||
|
const midPoint = new THREE.Vector3().lerpVectors(startPoint, endPoint, 0.5);
|
||||||
|
const offsetMagnitude = startPoint.distanceTo(endPoint) * 0.5;
|
||||||
|
const offsetAngle = Math.random() * Math.PI * 2;
|
||||||
|
|
||||||
if (data.t >= 1) {
|
const controlPoint = new THREE.Vector3(
|
||||||
// Path finished
|
midPoint.x + Math.cos(offsetAngle) * offsetMagnitude * 0.5,
|
||||||
|
midPoint.y + Math.random() * 0.5 + 0.5,
|
||||||
|
midPoint.z + Math.sin(offsetAngle) * offsetMagnitude * 0.5
|
||||||
|
);
|
||||||
|
|
||||||
if (data.state === 'landing') {
|
fly.userData.curve = new THREE.QuadraticBezierCurve3(startPoint, controlPoint, endPoint);
|
||||||
data.state = 'landed';
|
fly.userData.t = 0;
|
||||||
data.landTimer = FLY_WAIT_BASE + Math.random() * 1000; // Land for a random duration
|
fly.userData.landCheckTimer = 50 + Math.random() * 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupFlies(scene) {
|
||||||
|
for (let i = 0; i < FLIES_COUNT; i++) {
|
||||||
|
const fly = this._createFlyMesh();
|
||||||
|
scene.add(fly);
|
||||||
|
this.flies.push(fly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.flies.forEach(fly => {
|
||||||
|
const data = fly.userData;
|
||||||
|
|
||||||
|
if (data.state === 'flying' || data.state === 'landing') {
|
||||||
|
if (!data.curve) {
|
||||||
|
const newTargetPos = this._randomFlyTarget();
|
||||||
|
this._createFlyCurve(fly, newTargetPos);
|
||||||
data.t = 0;
|
data.t = 0;
|
||||||
return; // Stop updates for this fly
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Check for landing decision
|
data.t += data.speed;
|
||||||
if (data.landCheckTimer <= 0 && Math.random() > FLY_LAND_CHANCE) {
|
data.landCheckTimer--;
|
||||||
|
|
||||||
// Raycast down from the current position to find a landing spot
|
if (data.t >= 1) {
|
||||||
raycaster.set(fly.position, new THREE.Vector3(0, -1, 0));
|
if (data.state === 'landing') {
|
||||||
const intersects = raycaster.intersectObjects(landingSurfaces, false);
|
data.state = 'landed';
|
||||||
|
data.landTimer = FLY_WAIT_BASE + Math.random() * 1000;
|
||||||
|
data.t = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (intersects.length > 0) {
|
if (data.landCheckTimer <= 0 && Math.random() > FLY_LAND_CHANCE) {
|
||||||
const intersect = intersects[0];
|
raycaster.set(fly.position, new THREE.Vector3(0, -1, 0));
|
||||||
data.state = 'landing';
|
const intersects = raycaster.intersectObjects(landingSurfaces, false);
|
||||||
// Land slightly above the surface
|
|
||||||
let newTargetPos = new THREE.Vector3(intersect.point.x,
|
if (intersects.length > 0) {
|
||||||
intersect.point.y + 0.05,
|
const intersect = intersects[0];
|
||||||
intersect.point.z);
|
data.state = 'landing';
|
||||||
// const newTargetPos = randomFlyTarget();
|
let newTargetPos = new THREE.Vector3(
|
||||||
createFlyCurve(fly, newTargetPos);
|
intersect.point.x,
|
||||||
|
intersect.point.y + 0.05,
|
||||||
|
intersect.point.z
|
||||||
|
);
|
||||||
|
this._createFlyCurve(fly, newTargetPos);
|
||||||
|
data.t = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.state !== 'landing') {
|
||||||
|
const newTargetPos = this._randomFlyTarget();
|
||||||
|
this._createFlyCurve(fly, newTargetPos);
|
||||||
data.t = 0;
|
data.t = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.state !== 'landing') {
|
fly.position.copy(data.curve.getPoint(Math.min(data.t, 1)));
|
||||||
// 2. If not landing, generate a new random flight path
|
const tangent = data.curve.getTangent(Math.min(data.t, 1)).normalize();
|
||||||
const newTargetPos = randomFlyTarget();
|
fly.rotation.y = Math.atan2(tangent.x, tangent.z);
|
||||||
createFlyCurve(fly, newTargetPos);
|
data.oscillationTime += 0.1;
|
||||||
data.t = 0; // Reset T for the new curve
|
fly.position.y += Math.sin(data.oscillationTime * 4) * 0.01;
|
||||||
|
|
||||||
|
} else if (data.state === 'landed') {
|
||||||
|
data.landTimer--;
|
||||||
|
if (data.landTimer <= 0) {
|
||||||
|
data.state = 'flying';
|
||||||
|
const newTargetPos = this._randomFlyTarget();
|
||||||
|
this._createFlyCurve(fly, newTargetPos);
|
||||||
|
data.t = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
// Set position along the curve
|
}
|
||||||
fly.position.copy(data.curve.getPoint(Math.min(data.t, 1)));
|
|
||||||
|
|
||||||
// Set rotation tangent to the curve
|
|
||||||
const tangent = data.curve.getTangent(Math.min(data.t, 1)).normalize();
|
|
||||||
fly.rotation.y = Math.atan2(tangent.x, tangent.z);
|
|
||||||
|
|
||||||
// Add slight Y oscillation for buzzing feel (on top of curve)
|
|
||||||
data.oscillationTime += 0.1;
|
|
||||||
fly.position.y += Math.sin(data.oscillationTime * 4) * 0.01;
|
|
||||||
|
|
||||||
} else if (data.state === 'landed') {
|
|
||||||
// --- Landed State ---
|
|
||||||
data.landTimer--;
|
|
||||||
if (data.landTimer <= 0) {
|
|
||||||
// Take off: Generate new flight curve from current landed position
|
|
||||||
data.state = 'flying';
|
|
||||||
|
|
||||||
const newTargetPos = randomFlyTarget();
|
|
||||||
createFlyCurve(fly, newTargetPos);
|
|
||||||
data.t = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
// --- Global Variables ---
|
// --- Global Variables ---
|
||||||
let scene, camera, renderer, tvScreen, videoTexture, dust, screenLight, lampLightPoint, lampLightSpot;
|
let scene, camera, renderer, tvScreen, videoTexture, screenLight, lampLightPoint, lampLightSpot, effectsManager;
|
||||||
|
|
||||||
// VCR Display related variables
|
// VCR Display related variables
|
||||||
let simulatedPlaybackTime = 0;
|
let simulatedPlaybackTime = 0;
|
||||||
@ -23,9 +23,6 @@ const loadTapeButton = document.getElementById('loadTapeButton');
|
|||||||
const loader = new THREE.TextureLoader();
|
const loader = new THREE.TextureLoader();
|
||||||
|
|
||||||
const debugLight = false;
|
const debugLight = false;
|
||||||
|
|
||||||
const FLIES_COUNT = 2; // Flies
|
|
||||||
const flies = [];
|
|
||||||
let landingSurfaces = []; // Array to hold floor and table for fly landings
|
let landingSurfaces = []; // Array to hold floor and table for fly landings
|
||||||
const raycaster = new THREE.Raycaster();
|
const raycaster = new THREE.Raycaster();
|
||||||
|
|
||||||
|
|||||||
@ -30,8 +30,8 @@ function init() {
|
|||||||
// 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. Initialize all visual effects via the manager
|
||||||
createDust();
|
effectsManager = new EffectsManager(scene);
|
||||||
|
|
||||||
// 7. Create the Room Walls and Ceiling
|
// 7. Create the Room Walls and Ceiling
|
||||||
createRoomWalls();
|
createRoomWalls();
|
||||||
|
|||||||
@ -567,5 +567,4 @@ function createSceneObjects() {
|
|||||||
createBookshelf(-roomSize/2 + 0.2, roomSize/2*0.7, Math.PI/2, 0);
|
createBookshelf(-roomSize/2 + 0.2, roomSize/2*0.7, Math.PI/2, 0);
|
||||||
createBookshelf(roomSize/2 * 0.7, -roomSize/2+0.3, 0, 1);
|
createBookshelf(roomSize/2 * 0.7, -roomSize/2+0.3, 0, 1);
|
||||||
|
|
||||||
setupFlies();
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user