forked from EXT/VR180-Web-Player
more refactoer
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import * as THREE from 'https://unpkg.com/three/build/three.module.js';
|
||||
import {
|
||||
DEFAULT_PROJECTION,
|
||||
PLANE_2D_DISTANCE,
|
||||
@@ -17,6 +16,7 @@ import {
|
||||
} from './projection.js';
|
||||
import { createVideoTexture as createVideoTextureCore } from './three-utils.js';
|
||||
import { FallbackCameraControls } from './fallback-camera-controls.js';
|
||||
import { MediaController } from './media-controller.js';
|
||||
import {
|
||||
createVrController,
|
||||
handleVrControllerSelect
|
||||
@@ -30,7 +30,11 @@ import {
|
||||
updateVrVolumeButtonIcon
|
||||
} from './vr-control-panel.js';
|
||||
import { VrPanelVisibility } from './vr-panel-visibility.js';
|
||||
import { TwoDControlPanel } from './two-d-control-panel.js';
|
||||
import { TwoDMode } from './two-d-mode.js';
|
||||
import {
|
||||
createPlayerRenderer,
|
||||
resizePlayerRenderer
|
||||
} from './renderer-lifecycle.js';
|
||||
|
||||
const _playerBase = new URL('.', import.meta.url).href;
|
||||
|
||||
@@ -43,10 +47,10 @@ let videoElement, playBtn;
|
||||
let frameCounter = 0;
|
||||
|
||||
let isXrLoopActive = false;
|
||||
let is2DMode = false;
|
||||
let vrControlPanel;
|
||||
let mediaController: MediaController | undefined;
|
||||
let vrPanel: VrControlPanel | undefined;
|
||||
let twoDControls: TwoDControlPanel | undefined;
|
||||
let twoDMode: TwoDMode | undefined;
|
||||
const vrPanelVisibility = new VrPanelVisibility();
|
||||
|
||||
// 2D Camera Controls
|
||||
@@ -130,7 +134,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
|
||||
function applySbsTextureWindow(renderingRenderer, activeCamera, material) {
|
||||
applySbsTextureWindowCore(renderingRenderer, activeCamera, material, is2DMode);
|
||||
applySbsTextureWindowCore(renderingRenderer, activeCamera, material, is2DModeActive());
|
||||
}
|
||||
|
||||
function hideContentMeshes() {
|
||||
@@ -151,6 +155,34 @@ function createVideoTexture() {
|
||||
return videoTexture;
|
||||
}
|
||||
|
||||
function is2DModeActive() {
|
||||
return twoDMode?.isActive ?? false;
|
||||
}
|
||||
|
||||
function closeActiveXrSessionAfterContextLoss() {
|
||||
if (!xrSession) return;
|
||||
|
||||
const sessionToClose = xrSession;
|
||||
xrSession = null;
|
||||
sessionToClose.removeEventListener('end', onVRSessionEnd);
|
||||
sessionToClose.end().catch(e => {
|
||||
console.error("Error ending session on context lost:", e);
|
||||
}).finally(() => {
|
||||
onVRSessionEnd({ session: sessionToClose });
|
||||
});
|
||||
}
|
||||
|
||||
function restoreVideoTextureAfterContextRestored() {
|
||||
if (video && sphereMaterial && activeContentMesh && activeContentMesh.visible && renderer.xr.isPresenting && xrSession) {
|
||||
videoTexture = createVideoTexture();
|
||||
sphereMaterial.map = videoTexture;
|
||||
sphereMaterial.needsUpdate = true;
|
||||
updateVRPlayPauseButtonIcon();
|
||||
updateVRVolumeButtonIcon();
|
||||
console.log("Re-initialized video texture after context restoration during VR.");
|
||||
}
|
||||
}
|
||||
|
||||
function getVideoTitle() {
|
||||
return videoElement.getAttribute('title') ||
|
||||
videoElement.querySelector('source')?.src.split('/').pop()?.split('.')[0].replace(/-/g, ' ') ||
|
||||
@@ -160,53 +192,22 @@ function getVideoTitle() {
|
||||
|
||||
function init() {
|
||||
try {
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
camera.position.set(0, 1.6, 0.1);
|
||||
scene.add(camera);
|
||||
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
if (!renderer || !renderer.isWebGLRenderer) {
|
||||
throw new Error("Failed to create WebGLRenderer or it's not a valid Three.js renderer type.");
|
||||
}
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.xr.enabled = true;
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
playerContainer.appendChild(renderer.domElement);
|
||||
|
||||
if (renderer.domElement) {
|
||||
renderer.domElement.style.display = 'none';
|
||||
}
|
||||
|
||||
const gl = renderer.getContext();
|
||||
if (!gl) {
|
||||
throw new Error("Failed to get WebGL context from renderer.");
|
||||
}
|
||||
gl.canvas.addEventListener('webglcontextlost', (event) => {
|
||||
event.preventDefault();
|
||||
console.error("CONTEXT_EVENT: WebGL Context Lost! xrSession active?", !!xrSession, event);
|
||||
if (xrSession) {
|
||||
const sessionToClose = xrSession;
|
||||
xrSession = null; // Nullify global ref immediately
|
||||
sessionToClose.removeEventListener('end', onVRSessionEnd); // Try to remove listener
|
||||
sessionToClose.end().catch(e => { console.error("Error ending session on context lost:", e); }).finally(() => {
|
||||
onVRSessionEnd({ session: sessionToClose });
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
gl.canvas.addEventListener('webglcontextrestored', (event) => {
|
||||
console.log("CONTEXT_EVENT: WebGL Context Restored.");
|
||||
if (video && sphereMaterial && activeContentMesh && activeContentMesh.visible && renderer.xr.isPresenting && xrSession) {
|
||||
videoTexture = createVideoTexture();
|
||||
sphereMaterial.map = videoTexture;
|
||||
sphereMaterial.needsUpdate = true;
|
||||
updateVRPlayPauseButtonIcon();
|
||||
updateVRVolumeButtonIcon();
|
||||
console.log("Re-initialized video texture after context restoration during VR.");
|
||||
}
|
||||
}, false);
|
||||
const playerRenderer = createPlayerRenderer(playerContainer, {
|
||||
closeActiveXrSession: closeActiveXrSessionAfterContextLoss,
|
||||
hasActiveXrSession: () => !!xrSession,
|
||||
restoreAfterContextRestored: restoreVideoTextureAfterContextRestored
|
||||
});
|
||||
scene = playerRenderer.scene;
|
||||
camera = playerRenderer.camera;
|
||||
renderer = playerRenderer.renderer;
|
||||
|
||||
video = videoElement;
|
||||
mediaController = new MediaController({
|
||||
is2DModeActive,
|
||||
on2DPlaybackResume: show2DControlPanel,
|
||||
playButton: playBtn,
|
||||
video
|
||||
});
|
||||
const contentScene = createContentScene(scene, projectionMode, (renderer, scene, activeCamera, geometry, material, group) => {
|
||||
applySbsTextureWindow(renderer, activeCamera, material);
|
||||
});
|
||||
@@ -219,9 +220,32 @@ function init() {
|
||||
camera2D = contentScene.fallbackCamera;
|
||||
fallbackCameraControls = new FallbackCameraControls(camera2D, {
|
||||
hideControls: hide2DControlPanel,
|
||||
isEnabled: () => is2DMode,
|
||||
isEnabled: is2DModeActive,
|
||||
showControls: show2DControlPanel
|
||||
});
|
||||
twoDMode = new TwoDMode({
|
||||
callbacks: {
|
||||
createMediaTexture: createVideoTexture,
|
||||
forward: () => mediaController?.forward(),
|
||||
positionPlaneForPresentation,
|
||||
rewind: () => mediaController?.rewind(),
|
||||
seekToProgress: (progress) => mediaController?.seekToProgress(progress),
|
||||
showActiveContentMesh,
|
||||
toggleMute: () => mediaController?.toggleMute(),
|
||||
togglePlayPause: () => mediaController?.togglePlayPause()
|
||||
},
|
||||
fullscreenTarget: playerContainer,
|
||||
getActiveContentMesh: () => activeContentMesh,
|
||||
getCamera: () => camera2D,
|
||||
getCameraControls: () => fallbackCameraControls,
|
||||
getMaterial: () => sphereMaterial,
|
||||
getRenderer: () => renderer,
|
||||
getScene: () => scene,
|
||||
getVideo: () => video,
|
||||
playerContainer,
|
||||
projectionMode,
|
||||
title: getVideoTitle()
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("INIT_ERROR (Phase 1 - Core Setup):", e);
|
||||
renderer = null;
|
||||
@@ -264,9 +288,6 @@ function init() {
|
||||
video
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize 2D control panel
|
||||
init2DControlPanel();
|
||||
} catch (e) {
|
||||
console.error("INIT_ERROR (Phase 3 - Event Listeners):", e);
|
||||
}
|
||||
@@ -308,248 +329,81 @@ function hidePanel() {
|
||||
|
||||
function onWindowResize() {
|
||||
if (!renderer) return;
|
||||
if (renderer.xr && renderer.xr.isPresenting) return;
|
||||
|
||||
if (is2DMode) {
|
||||
// In 2D mode, calculate canvas size based on container dimensions
|
||||
const container = playerContainer;
|
||||
if (container) {
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const containerWidth = containerRect.width;
|
||||
|
||||
// Calculate height based on 16:9 aspect ratio
|
||||
const aspectRatio = 16 / 9;
|
||||
const calculatedHeight = containerWidth / aspectRatio;
|
||||
|
||||
// Ensure minimum dimensions to prevent zero-sized canvas
|
||||
const canvasWidth = Math.max(containerWidth, 320);
|
||||
const canvasHeight = Math.max(calculatedHeight, 180);
|
||||
|
||||
renderer.setSize(canvasWidth, canvasHeight);
|
||||
camera2D.aspect = canvasWidth / canvasHeight;
|
||||
camera2D.updateProjectionMatrix();
|
||||
|
||||
// Update canvas styling to maintain proper positioning
|
||||
const canvas = renderer.domElement;
|
||||
canvas.style.width = '100%';
|
||||
canvas.style.height = 'auto';
|
||||
canvas.style.aspectRatio = '16/9';
|
||||
|
||||
// Reposition control panel after resize
|
||||
position2DControlPanel();
|
||||
}
|
||||
} else {
|
||||
// Normal VR/window mode
|
||||
if (camera && renderer.domElement.style.display !== 'none') {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
} else if (camera) {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
// Update 2D camera aspect ratio for potential future use
|
||||
if (camera2D) {
|
||||
camera2D.aspect = window.innerWidth / window.innerHeight;
|
||||
camera2D.updateProjectionMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2D Render Loop
|
||||
function render2D() {
|
||||
if (!is2DMode) return;
|
||||
|
||||
if (projectionMode === 'vr180') {
|
||||
fallbackCameraControls?.updateCameraRotation();
|
||||
} else if (camera2D) {
|
||||
camera2D.rotation.set(0, 0, 0);
|
||||
}
|
||||
|
||||
if (renderer && camera2D && scene) {
|
||||
renderer.render(scene, camera2D);
|
||||
}
|
||||
|
||||
requestAnimationFrame(render2D);
|
||||
}
|
||||
if (twoDMode?.resize()) return;
|
||||
|
||||
// 2D Control Panel Functions
|
||||
function init2DControlPanel() {
|
||||
twoDControls = new TwoDControlPanel({
|
||||
callbacks: {
|
||||
onForward: () => {
|
||||
if (video && isFinite(video.duration)) {
|
||||
video.currentTime = Math.min(video.duration, video.currentTime + 15);
|
||||
}
|
||||
},
|
||||
onMute: () => {
|
||||
if (video) {
|
||||
video.muted = !video.muted;
|
||||
}
|
||||
},
|
||||
onPlayPause: togglePlayPause,
|
||||
onRewind: () => {
|
||||
if (video) {
|
||||
video.currentTime = Math.max(0, video.currentTime - 15);
|
||||
}
|
||||
},
|
||||
onSeek: (progress) => {
|
||||
if (video && isFinite(video.duration)) {
|
||||
video.currentTime = progress * video.duration;
|
||||
}
|
||||
}
|
||||
},
|
||||
fullscreenTarget: playerContainer,
|
||||
getIsActive: () => is2DMode,
|
||||
resizePlayerRenderer({
|
||||
camera,
|
||||
camera2D,
|
||||
is2DMode: false,
|
||||
onFallbackResize: () => {},
|
||||
playerContainer,
|
||||
title: getVideoTitle()
|
||||
renderer
|
||||
});
|
||||
}
|
||||
|
||||
function show2DControlPanel() {
|
||||
twoDControls?.show();
|
||||
twoDMode?.showControls();
|
||||
}
|
||||
|
||||
function hide2DControlPanel() {
|
||||
twoDControls?.hide();
|
||||
twoDMode?.hideControls();
|
||||
}
|
||||
|
||||
function update2DControlPanel() {
|
||||
if (!is2DMode || !video) return;
|
||||
|
||||
twoDControls?.updateTime(video.currentTime, video.duration);
|
||||
twoDMode?.updateTimeline();
|
||||
}
|
||||
|
||||
function update2DPlayPauseButton() {
|
||||
if (!is2DMode || !video) return;
|
||||
|
||||
twoDControls?.updatePlaybackButton(video.paused || video.ended);
|
||||
twoDMode?.updatePlaybackButton();
|
||||
}
|
||||
|
||||
function update2DMuteButton() {
|
||||
if (!is2DMode || !video) return;
|
||||
|
||||
twoDControls?.updateMuteButton(video.muted);
|
||||
twoDMode?.updateMuteButton();
|
||||
}
|
||||
|
||||
function handle2DVideoEnd() {
|
||||
if (!is2DMode || !video) return;
|
||||
|
||||
twoDControls?.showPersistent();
|
||||
update2DPlayPauseButton();
|
||||
}
|
||||
|
||||
function position2DControlPanel() {
|
||||
if (!renderer) return;
|
||||
|
||||
twoDControls?.position(renderer.domElement);
|
||||
}
|
||||
|
||||
function hidePlayButton() {
|
||||
if (playBtn) {
|
||||
playBtn.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function enableNativeControls() {
|
||||
if (video) {
|
||||
video.controls = true;
|
||||
}
|
||||
}
|
||||
|
||||
function togglePlayPause() {
|
||||
if (!video || !video.currentSrc) return;
|
||||
if (video.paused || video.ended) {
|
||||
// If video has ended in 2D mode, restart from beginning
|
||||
if (video.ended && is2DMode) {
|
||||
video.currentTime = 0;
|
||||
}
|
||||
|
||||
if (video.readyState >= video.HAVE_ENOUGH_DATA || video.currentSrc) {
|
||||
const playPromise = video.play();
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.then(() => {
|
||||
// Resume normal control panel auto-hide behavior after restart
|
||||
if (is2DMode && video.ended === false) {
|
||||
show2DControlPanel();
|
||||
}
|
||||
}).catch(err => console.error("Error during video.play():", err));
|
||||
} else {
|
||||
console.error("video.play() did not return a promise.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
video.pause();
|
||||
}
|
||||
twoDMode?.handleVideoEnd();
|
||||
}
|
||||
|
||||
function resetToOriginalState() {
|
||||
// Reset video to show poster frame
|
||||
if (video) {
|
||||
video.pause();
|
||||
video.currentTime = 0;
|
||||
video.controls = false; // Disable native controls
|
||||
|
||||
// Force video back to poster state by reloading
|
||||
video.load();
|
||||
}
|
||||
|
||||
// Show the play button in center position
|
||||
if (playBtn) {
|
||||
if (mediaController) {
|
||||
mediaController.resetToOriginalState();
|
||||
} else if (playBtn) {
|
||||
playBtn.classList.remove('hidden');
|
||||
playBtn.disabled = false;
|
||||
}
|
||||
|
||||
// Reset 2D mode if it was active
|
||||
if (is2DMode) {
|
||||
is2DMode = false;
|
||||
remove2DEventListeners();
|
||||
|
||||
// Hide 2D control panel
|
||||
hide2DControlPanel();
|
||||
|
||||
// Reset camera rotation
|
||||
fallbackCameraControls?.reset();
|
||||
positionPlaneForPresentation(false);
|
||||
|
||||
// Hide WebGL canvas and show video element
|
||||
if (renderer && renderer.domElement) {
|
||||
renderer.domElement.style.display = 'none';
|
||||
}
|
||||
if (video) {
|
||||
video.style.display = '';
|
||||
}
|
||||
|
||||
// Reset renderer size
|
||||
if (twoDMode?.isActive) {
|
||||
twoDMode.stop();
|
||||
onWindowResize();
|
||||
}
|
||||
}
|
||||
|
||||
function onVideoEnded() {
|
||||
if (video && !video.paused) video.pause();
|
||||
|
||||
if (xrSession && renderer && renderer.xr.isPresenting) {
|
||||
// VR mode - exit VR and reset to original state
|
||||
actualSessionToggle().catch(err => {
|
||||
console.error("Error during automatic VR exit on video end:", err);
|
||||
// Fallback cleanup if actualSessionToggle fails or doesn't fully clean up
|
||||
if(xrSession) { // Check if session still exists
|
||||
const sessionToClean = xrSession;
|
||||
xrSession = null; // Nullify global ref
|
||||
sessionToClean.removeEventListener('end', onVRSessionEnd);
|
||||
sessionToClean.end().catch(e => {}).finally(() => onVRSessionEnd({session: sessionToClean}));
|
||||
} else {
|
||||
onVRSessionEnd({session: null}); // Call with null session if already gone
|
||||
}
|
||||
});
|
||||
} else if (is2DMode) {
|
||||
// 2D mode - stay on last frame with controls visible
|
||||
handle2DVideoEnd();
|
||||
} else {
|
||||
// Regular mode - reset to original state
|
||||
if (!mediaController) {
|
||||
resetToOriginalState();
|
||||
return;
|
||||
}
|
||||
|
||||
mediaController.handleEnded({
|
||||
cleanupFailedVrExit,
|
||||
exitVr: actualSessionToggle,
|
||||
isIn2DMode: is2DModeActive,
|
||||
isInVr: () => Boolean(xrSession && renderer && renderer.xr.isPresenting),
|
||||
on2DEnded: handle2DVideoEnd,
|
||||
resetToOriginalState
|
||||
});
|
||||
}
|
||||
|
||||
function cleanupFailedVrExit() {
|
||||
if (xrSession) {
|
||||
const sessionToClean = xrSession;
|
||||
xrSession = null;
|
||||
sessionToClean.removeEventListener('end', onVRSessionEnd);
|
||||
sessionToClean.end().catch(e => {}).finally(() => onVRSessionEnd({session: sessionToClean}));
|
||||
} else {
|
||||
onVRSessionEnd({session: null});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,31 +413,27 @@ function onSelectStartVR(event) {
|
||||
if (xrSession) actualSessionToggle();
|
||||
},
|
||||
forward: () => {
|
||||
if (video && isFinite(video.duration)) {
|
||||
video.currentTime = Math.min(video.duration, video.currentTime + 15);
|
||||
updateSeekBarAppearance();
|
||||
}
|
||||
mediaController?.forward();
|
||||
updateSeekBarAppearance();
|
||||
},
|
||||
hidePanel,
|
||||
isPanelVisible: () => vrPanelVisibility.isVisible,
|
||||
raycaster,
|
||||
rewind: () => {
|
||||
if (video) {
|
||||
video.currentTime = Math.max(0, video.currentTime - 15);
|
||||
updateSeekBarAppearance();
|
||||
}
|
||||
mediaController?.rewind();
|
||||
updateSeekBarAppearance();
|
||||
},
|
||||
seek: (progress) => {
|
||||
if (video && isFinite(video.duration)) {
|
||||
video.currentTime = progress * video.duration;
|
||||
updateSeekBarAppearance();
|
||||
}
|
||||
mediaController?.seekToProgress(progress);
|
||||
updateSeekBarAppearance();
|
||||
},
|
||||
showPanel,
|
||||
toggleMute: () => {
|
||||
if (video) video.muted = !video.muted;
|
||||
mediaController?.toggleMute();
|
||||
},
|
||||
togglePlayPause: () => {
|
||||
mediaController?.togglePlayPause();
|
||||
},
|
||||
togglePlayPause,
|
||||
uiElements,
|
||||
vrPanel
|
||||
});
|
||||
@@ -596,7 +446,7 @@ async function handleEnterVRButtonClick() {
|
||||
}
|
||||
|
||||
// Hide the play button after click
|
||||
hidePlayButton();
|
||||
mediaController?.hidePlayButton();
|
||||
|
||||
// Check if VR is supported
|
||||
if (playBtn.dataset.xrSupported === "true") {
|
||||
@@ -604,110 +454,10 @@ async function handleEnterVRButtonClick() {
|
||||
await actualSessionToggle();
|
||||
} else {
|
||||
// VR is not supported - start 2D rectilinear mode
|
||||
start2DMode();
|
||||
twoDMode?.start();
|
||||
}
|
||||
}
|
||||
|
||||
function start2DMode() {
|
||||
if (!video || !renderer || !camera2D) {
|
||||
console.error("Required components not available for 2D mode");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set 2D mode flag
|
||||
is2DMode = true;
|
||||
|
||||
// Calculate canvas size based on container dimensions (same logic as onWindowResize)
|
||||
const container = playerContainer;
|
||||
if (container) {
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const containerWidth = containerRect.width;
|
||||
|
||||
// Calculate height based on 16:9 aspect ratio
|
||||
const aspectRatio = 16 / 9;
|
||||
const calculatedHeight = containerWidth / aspectRatio;
|
||||
|
||||
// Ensure minimum dimensions to prevent zero-sized canvas
|
||||
const canvasWidth = Math.max(containerWidth, 320);
|
||||
const canvasHeight = Math.max(calculatedHeight, 180);
|
||||
|
||||
// Resize renderer with calculated dimensions
|
||||
renderer.setSize(canvasWidth, canvasHeight);
|
||||
|
||||
// Update 2D camera aspect ratio
|
||||
camera2D.aspect = canvasWidth / canvasHeight;
|
||||
camera2D.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
// Position the canvas to match the video element
|
||||
const canvas = renderer.domElement;
|
||||
canvas.style.position = 'relative';
|
||||
canvas.style.width = '100%';
|
||||
canvas.style.height = 'auto';
|
||||
canvas.style.aspectRatio = '16/9';
|
||||
|
||||
// Hide HTML video element and show WebGL canvas
|
||||
video.style.display = 'none';
|
||||
canvas.style.display = '';
|
||||
|
||||
// Create video texture if not exists
|
||||
videoTexture = createVideoTexture();
|
||||
positionPlaneForPresentation(projectionMode === 'plane');
|
||||
|
||||
// Apply texture to the selected projection mesh and make it visible
|
||||
if (sphereMaterial && activeContentMesh) {
|
||||
sphereMaterial.map = videoTexture;
|
||||
sphereMaterial.needsUpdate = true;
|
||||
showActiveContentMesh();
|
||||
}
|
||||
|
||||
// Start video playback
|
||||
togglePlayPause();
|
||||
|
||||
// Add event listeners for 2D controls
|
||||
add2DEventListeners();
|
||||
|
||||
// Add fullscreen event listeners to handle resize properly
|
||||
document.addEventListener('fullscreenchange', onFullscreenChange);
|
||||
document.addEventListener('webkitfullscreenchange', onFullscreenChange);
|
||||
document.addEventListener('mozfullscreenchange', onFullscreenChange);
|
||||
document.addEventListener('MSFullscreenChange', onFullscreenChange);
|
||||
|
||||
// Show 2D control panel
|
||||
show2DControlPanel();
|
||||
|
||||
// Position control panel relative to canvas
|
||||
position2DControlPanel();
|
||||
|
||||
// Start 2D render loop
|
||||
render2D();
|
||||
}
|
||||
|
||||
function add2DEventListeners() {
|
||||
fallbackCameraControls?.addEventListeners(renderer.domElement, projectionMode);
|
||||
}
|
||||
|
||||
function remove2DEventListeners() {
|
||||
if (!renderer || !renderer.domElement) return;
|
||||
|
||||
fallbackCameraControls?.removeEventListeners(renderer.domElement);
|
||||
|
||||
// Fullscreen events
|
||||
document.removeEventListener('fullscreenchange', onFullscreenChange);
|
||||
document.removeEventListener('webkitfullscreenchange', onFullscreenChange);
|
||||
document.removeEventListener('mozfullscreenchange', onFullscreenChange);
|
||||
document.removeEventListener('MSFullscreenChange', onFullscreenChange);
|
||||
}
|
||||
|
||||
function onFullscreenChange() {
|
||||
if (!is2DMode) return;
|
||||
|
||||
// Trigger resize handling when fullscreen state changes
|
||||
setTimeout(() => {
|
||||
onWindowResize();
|
||||
}, 100); // Small delay to ensure fullscreen transition is complete
|
||||
}
|
||||
|
||||
async function actualSessionToggle() {
|
||||
if (!renderer || !renderer.isWebGLRenderer) {
|
||||
console.error("CRITICAL_ERROR: actualSessionToggle: renderer is NOT a WebGLRenderer or is null!", renderer);
|
||||
@@ -741,9 +491,9 @@ async function actualSessionToggle() {
|
||||
video.style.display = 'none';
|
||||
}
|
||||
|
||||
if (video && (video.paused || video.ended)) {
|
||||
if (mediaController && video && (video.paused || video.ended)) {
|
||||
try {
|
||||
await video.play();
|
||||
await mediaController.play();
|
||||
} catch (playError) {
|
||||
console.error("Failed to play video after obtaining XR session:", playError);
|
||||
}
|
||||
@@ -818,9 +568,7 @@ function onVRSessionEnd(event) {
|
||||
video.style.display = '';
|
||||
}
|
||||
|
||||
if (video && !video.paused) {
|
||||
video.pause();
|
||||
}
|
||||
mediaController?.pauseIfPlaying();
|
||||
|
||||
if (sphereMaterial && sphereMaterial.map) {
|
||||
sphereMaterial.map.dispose();
|
||||
|
||||
Reference in New Issue
Block a user