-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy paththree-scene.js
More file actions
172 lines (141 loc) · 5.65 KB
/
three-scene.js
File metadata and controls
172 lines (141 loc) · 5.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// Create scene
const scene = new THREE.Scene();
scene.background = null; // Transparent background
// Create camera
const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 25;
// Create renderer
const container = document.getElementById('iphone-model-container');
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
// Call resize immediately to fix initial perspective
onWindowResize();
// Create video texture
const video = document.getElementById( 'video' );
video.src = 'videos/demo.mp4';
video.loop = true;
video.muted = true;
video.playsInline = true;
video.preload = 'auto'; // Enable preloading for streaming
// Start playing as soon as enough data is buffered (streaming)
video.addEventListener('canplay', () => {
if (video.paused) {
video.play().catch(e => console.log('Auto-play prevented:', e));
}
}, { once: true });
// Fallback: if canplay doesn't fire, try loadeddata
video.addEventListener('loadeddata', () => {
if (video.paused) {
video.play().catch(e => console.log('Auto-play prevented:', e));
}
}, { once: true });
// Optional: Add progress tracking for debugging
// video.addEventListener('progress', () => {
// if (video.buffered.length > 0) {
// const bufferedEnd = video.buffered.end(video.buffered.length - 1);
// const duration = video.duration;
// if (duration > 0) {
// const bufferedPercent = (bufferedEnd / duration) * 100;
// console.log(`Buffered: ${bufferedPercent.toFixed(1)}%`);
// }
// }
// });
const videoTexture = new THREE.VideoTexture(video);
videoTexture.colorSpace = THREE.SRGBColorSpace;
// videoTexture.minFilter = THREE.LinearFilter;
// videoTexture.magFilter = THREE.LinearFilter;
// videoTexture.format = THREE.RGBAFormat;
// Fix rotation and aspect ratio
videoTexture.rotation = Math.PI;
videoTexture.center = new THREE.Vector2(0.5, 0.5);
videoTexture.repeat.set(-2.1, 1); // Make repeat.x negative to flip horizontally
videoTexture.offset.set(2.1 * 0.25, 0); // Adjust offset to compensate for negative repeat
// Add light
const ambientLight = new THREE.AmbientLight(0xFFFFFF, 3.0);
scene.add(ambientLight);
// const directionalLight = new THREE.DirectionalLight(0xffffff, 0.2);
// directionalLight.position.set(5, 5, 5);
// scene.add(directionalLight);
// Add OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.enableZoom = false;
// Disable controls on touch devices to prevent scroll conflicts
controls.enablePan = false;
if ('ontouchstart' in window || navigator.maxTouchPoints > 0) {
controls.enableRotate = false;
}
// Load the model
const loader = new GLTFLoader();
let iphoneModel = null; // Store reference to the model for animation
let baseRotation = Math.PI * 0.1;
loader.load(
'models/iphone.glb',
function (gltf) {
const model = gltf.scene;
iphoneModel = model; // Store reference
scene.add(model);
// Find the screen object and apply video texture
model.traverse((node) => {
if (node.name === 'screen') {
if (node.material) {
node.material = new THREE.MeshStandardMaterial({
map: videoTexture,
flatShading: true,
metalness: 0,
roughness: 0,
});
// Update texture properties
node.material.map.rotation = videoTexture.rotation;
node.material.map.center = videoTexture.center;
node.material.map.repeat = videoTexture.repeat;
node.material.map.offset = videoTexture.offset;
}
}
});
// Auto-rotate the model
model.rotation.y = baseRotation;
// Center the model
const box = new THREE.Box3().setFromObject(model);
const center = box.getCenter(new THREE.Vector3());
model.position.sub(center);
// Scale the model appropriately
const scale = 1;
model.scale.set(scale, scale, scale);
onWindowResize();
},
undefined,
function (error) {
console.error('An error occurred loading the model:', error);
}
);
// Handle window resize
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
}
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Gentle floating animation
if (iphoneModel) {
const time = Date.now() * 0.001; // Convert to seconds
// Gentle vertical floating (±0.3 units)
iphoneModel.position.y = Math.sin(time * 0.8) * 0.3;
// Gentle rotation around Y axis (±5 degrees)
iphoneModel.rotation.y = baseRotation + Math.sin(time * 0.6) * (Math.PI / 36); // ±5 degrees
// Very subtle tilt on X axis (±2 degrees)
iphoneModel.rotation.x = Math.sin(time * 0.5) * (Math.PI / 90); // ±2 degrees
}
controls.update();
renderer.render(scene, camera);
}
animate();