mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2025-12-28 05:33:54 -08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90305de2de | ||
|
|
1a2451f4d3 | ||
|
|
f43c4273a1 | ||
|
|
b201fb69fc | ||
|
|
c2047278e4 | ||
|
|
a03fc7a947 | ||
|
|
db74147021 | ||
|
|
4e0c2e69c5 | ||
|
|
ed135d1306 | ||
|
|
b7642b4a81 | ||
|
|
6beca88785 | ||
|
|
3bd1d4c1f0 |
113
examples/al-vr.html
Normal file
113
examples/al-vr.html
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"three": "https://unpkg.com/three@0.157.0/build/three.module.js",
|
||||||
|
"three/addons/": "https://unpkg.com/three@0.157.0/examples/jsm/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import A from '../src/js/A.js';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
|
||||||
|
let aladin;
|
||||||
|
A.init.then(() => {
|
||||||
|
aladin = A.aladin(
|
||||||
|
'#aladin-lite-div',
|
||||||
|
{
|
||||||
|
survey: 'P/DSS2/color', // set a survey
|
||||||
|
projection: 'TAN', // set a projection
|
||||||
|
fov: 70, // initial field of view in degrees
|
||||||
|
target: '338.98958 33.96', // initial target
|
||||||
|
cooFrame: 'equatorial', // set galactic frame
|
||||||
|
showCooGrid: true, // set the grid
|
||||||
|
fullScreen: true,
|
||||||
|
vr: {animation: animate.bind(renderer)},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//aladin.setOverlayImageLayer("https://alasky.cds.unistra.fr/JWST/CDS_P_JWST_Stephans-Quintet_NIRCam+MIRI")
|
||||||
|
initScene(aladin.view.imageCanvas);
|
||||||
|
aladin.setRenderer(renderer);
|
||||||
|
});
|
||||||
|
|
||||||
|
let renderer = null;
|
||||||
|
let scene = null;
|
||||||
|
let camera = null;
|
||||||
|
let cubeMesh = null;
|
||||||
|
// let controls = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a 3D scene, camera, and renderer for virtual reality (VR).
|
||||||
|
*
|
||||||
|
* @param {HTMLCanvasElement} canvas - The HTML canvas element to render the
|
||||||
|
* 3D scene
|
||||||
|
*/
|
||||||
|
function initScene(canvas) {
|
||||||
|
scene = new THREE.Scene();
|
||||||
|
|
||||||
|
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||||
|
scene.add(camera);
|
||||||
|
|
||||||
|
renderer = new THREE.WebGLRenderer({canvas: canvas, context: canvas.getContext('webgl2', {xrCompatible: true})}); // NOTE Une différence ici
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
renderer.xr.enabled = true;
|
||||||
|
// renderer.xr.setReferenceSpaceType('local');
|
||||||
|
renderer.autoClear = false;
|
||||||
|
|
||||||
|
const light = new THREE.PointLight(0xffffff, 10);
|
||||||
|
light.position.set(0, 2, 1);
|
||||||
|
scene.add(light);
|
||||||
|
|
||||||
|
const planeGeometry = new THREE.PlaneGeometry(10, 10);
|
||||||
|
const planeMaterial = new THREE.MeshPhongMaterial({ color: 0xff00ff });
|
||||||
|
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
|
||||||
|
planeMesh.position.set(0, -1, 0);
|
||||||
|
planeMesh.rotation.x = -Math.PI / 2;
|
||||||
|
scene.add(planeMesh);
|
||||||
|
|
||||||
|
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
|
||||||
|
const cubeMaterial = new THREE.MeshPhongMaterial({ color: 0x00ff00 });
|
||||||
|
cubeMesh = new THREE.Mesh(cubeGeometry, cubeMaterial);
|
||||||
|
cubeMesh.position.set(0, 0, -2);
|
||||||
|
scene.add(cubeMesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to animate the 3D scene and rendering it.
|
||||||
|
*/
|
||||||
|
function animate() {
|
||||||
|
cubeMesh.rotation.x += 0.001;
|
||||||
|
cubeMesh.rotation.y += 0.001;
|
||||||
|
|
||||||
|
renderer.render( scene, camera );
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Initializes a WebGL2 context and handles potential errors.
|
||||||
|
// */
|
||||||
|
// function initWebGL2() {
|
||||||
|
// // canvas = aladin.view.imageCanvas;
|
||||||
|
// canvas = document.getElementById(aladin.view.imageCanvas);
|
||||||
|
// // gl = canvas.getContext("webgl2", { alpha: true });
|
||||||
|
// gl = canvas.getContext('webgl2');
|
||||||
|
// if (!gl) { // If the gl didn't create properly
|
||||||
|
// alert('This browser doesn\'t support WebGL2');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -9,8 +9,8 @@ pub mod vao {
|
|||||||
use crate::object::element_array_buffer::ElementArrayBuffer;
|
use crate::object::element_array_buffer::ElementArrayBuffer;
|
||||||
|
|
||||||
use crate::webgl_ctx::WebGlContext;
|
use crate::webgl_ctx::WebGlContext;
|
||||||
use std::collections::HashMap;
|
|
||||||
use crate::Abort;
|
use crate::Abort;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct VertexArrayObject {
|
pub struct VertexArrayObject {
|
||||||
array_buffer: HashMap<&'static str, ArrayBuffer>,
|
array_buffer: HashMap<&'static str, ArrayBuffer>,
|
||||||
@@ -88,7 +88,10 @@ pub mod vao {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
pub fn num_elements(&self) -> usize {
|
pub fn num_elements(&self) -> usize {
|
||||||
self.element_array_buffer.as_ref().unwrap_abort().num_elements()
|
self.element_array_buffer
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_abort()
|
||||||
|
.num_elements()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_instances(&self) -> i32 {
|
pub fn num_instances(&self) -> i32 {
|
||||||
@@ -155,6 +158,7 @@ pub mod vao {
|
|||||||
|
|
||||||
pub fn unbind(&self) {
|
pub fn unbind(&self) {
|
||||||
self.vao.gl.bind_vertex_array(None);
|
self.vao.gl.bind_vertex_array(None);
|
||||||
|
self._shader.unbind(&self.vao.gl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,8 +174,9 @@ pub mod vao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> ShaderVertexArrayObjectBoundRef<'a, 'b> {
|
impl<'a, 'b> ShaderVertexArrayObjectBoundRef<'a, 'b> {
|
||||||
pub fn draw_arrays(&self, mode: u32, byte_offset: i32, size: i32) {
|
pub fn draw_arrays(&self, mode: u32, byte_offset: i32, size: i32) -> &Self {
|
||||||
self.vao.gl.draw_arrays(mode, byte_offset, size);
|
self.vao.gl.draw_arrays(mode, byte_offset, size);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_elements_with_i32(
|
pub fn draw_elements_with_i32(
|
||||||
@@ -180,11 +185,12 @@ pub mod vao {
|
|||||||
num_elements: Option<i32>,
|
num_elements: Option<i32>,
|
||||||
type_: u32,
|
type_: u32,
|
||||||
byte_offset: i32,
|
byte_offset: i32,
|
||||||
) {
|
) -> &Self {
|
||||||
let num_elements = num_elements.unwrap_or(self.vao.num_elements() as i32);
|
let num_elements = num_elements.unwrap_or(self.vao.num_elements() as i32);
|
||||||
self.vao
|
self.vao
|
||||||
.gl
|
.gl
|
||||||
.draw_elements_with_i32(mode, num_elements, type_, byte_offset);
|
.draw_elements_with_i32(mode, num_elements, type_, byte_offset);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_elements_instanced_with_i32(
|
pub fn draw_elements_instanced_with_i32(
|
||||||
@@ -192,7 +198,7 @@ pub mod vao {
|
|||||||
mode: u32,
|
mode: u32,
|
||||||
offset_element_idx: i32,
|
offset_element_idx: i32,
|
||||||
num_instances: i32,
|
num_instances: i32,
|
||||||
) {
|
) -> &Self {
|
||||||
self.vao.gl.draw_elements_instanced_with_i32(
|
self.vao.gl.draw_elements_instanced_with_i32(
|
||||||
mode,
|
mode,
|
||||||
self.vao.num_elements() as i32,
|
self.vao.num_elements() as i32,
|
||||||
@@ -200,10 +206,12 @@ pub mod vao {
|
|||||||
offset_element_idx,
|
offset_element_idx,
|
||||||
num_instances,
|
num_instances,
|
||||||
);
|
);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbind(&self) {
|
pub fn unbind(&self) {
|
||||||
self.vao.gl.bind_vertex_array(None);
|
self.vao.gl.bind_vertex_array(None);
|
||||||
|
self._shader.unbind(&self.vao.gl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,7 +452,10 @@ pub mod vao {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
pub fn num_elements(&self) -> usize {
|
pub fn num_elements(&self) -> usize {
|
||||||
self.element_array_buffer.as_ref().unwrap_abort().num_elements()
|
self.element_array_buffer
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_abort()
|
||||||
|
.num_elements()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_instances(&self) -> i32 {
|
pub fn num_instances(&self) -> i32 {
|
||||||
@@ -511,7 +522,8 @@ pub mod vao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbind(&self) {
|
pub fn unbind(&self) {
|
||||||
//self.vao.gl.bind_vertex_array(None);
|
self.vao.gl.bind_vertex_array(None);
|
||||||
|
self._shader.unbind(&self.vao.gl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,13 +540,15 @@ pub mod vao {
|
|||||||
}
|
}
|
||||||
use crate::object::array_buffer::VertexBufferObject;
|
use crate::object::array_buffer::VertexBufferObject;
|
||||||
impl<'a, 'b> ShaderVertexArrayObjectBoundRef<'a, 'b> {
|
impl<'a, 'b> ShaderVertexArrayObjectBoundRef<'a, 'b> {
|
||||||
pub fn draw_arrays(&self, mode: u32, byte_offset: i32, size: i32) {
|
pub fn draw_arrays(&self, mode: u32, byte_offset: i32, size: i32) -> &Self {
|
||||||
for (attr, buf) in self.vao.array_buffer.iter() {
|
for (attr, buf) in self.vao.array_buffer.iter() {
|
||||||
buf.bind();
|
buf.bind();
|
||||||
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
|
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.vao.gl.draw_arrays(mode, byte_offset, size);
|
self.vao.gl.draw_arrays(mode, byte_offset, size);
|
||||||
|
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_elements_with_i32(
|
pub fn draw_elements_with_i32(
|
||||||
@@ -543,7 +557,7 @@ pub mod vao {
|
|||||||
num_elements: Option<i32>,
|
num_elements: Option<i32>,
|
||||||
type_: u32,
|
type_: u32,
|
||||||
byte_offset: i32,
|
byte_offset: i32,
|
||||||
) {
|
) -> &Self {
|
||||||
for (attr, buf) in self.vao.array_buffer.iter() {
|
for (attr, buf) in self.vao.array_buffer.iter() {
|
||||||
buf.bind();
|
buf.bind();
|
||||||
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
|
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
|
||||||
@@ -555,6 +569,7 @@ pub mod vao {
|
|||||||
self.vao
|
self.vao
|
||||||
.gl
|
.gl
|
||||||
.draw_elements_with_i32(mode, num_elements, type_, byte_offset);
|
.draw_elements_with_i32(mode, num_elements, type_, byte_offset);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_elements_instanced_with_i32(
|
pub fn draw_elements_instanced_with_i32(
|
||||||
@@ -562,7 +577,7 @@ pub mod vao {
|
|||||||
mode: u32,
|
mode: u32,
|
||||||
offset_element_idx: i32,
|
offset_element_idx: i32,
|
||||||
num_instances: i32,
|
num_instances: i32,
|
||||||
) {
|
) -> &Self {
|
||||||
for (attr, buf) in self.vao.array_buffer.iter() {
|
for (attr, buf) in self.vao.array_buffer.iter() {
|
||||||
buf.bind();
|
buf.bind();
|
||||||
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
|
buf.set_vertex_attrib_pointer_by_name::<f32>(self.shader, attr);
|
||||||
@@ -587,10 +602,12 @@ pub mod vao {
|
|||||||
offset_element_idx,
|
offset_element_idx,
|
||||||
num_instances,
|
num_instances,
|
||||||
);
|
);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbind(&self) {
|
pub fn unbind(&self) {
|
||||||
//self.vao.gl.bind_vertex_array(None);
|
self.vao.gl.bind_vertex_array(None);
|
||||||
|
self.shader.unbind(&self.vao.gl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -716,6 +733,9 @@ pub mod vao {
|
|||||||
|
|
||||||
pub fn unbind(&self) {
|
pub fn unbind(&self) {
|
||||||
//self.vao.gl.bind_vertex_array(None);
|
//self.vao.gl.bind_vertex_array(None);
|
||||||
|
|
||||||
|
self.vao.gl.bind_vertex_array(None);
|
||||||
|
self.shader.unbind(&self.vao.gl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -875,7 +875,7 @@ impl App {
|
|||||||
&self.colormaps,
|
&self.colormaps,
|
||||||
&self.projection,
|
&self.projection,
|
||||||
)?;
|
)?;
|
||||||
|
/*
|
||||||
// Draw the catalog
|
// Draw the catalog
|
||||||
//let fbo_view = &self.fbo_view;
|
//let fbo_view = &self.fbo_view;
|
||||||
//catalogs.draw(&gl, shaders, camera, colormaps, fbo_view)?;
|
//catalogs.draw(&gl, shaders, camera, colormaps, fbo_view)?;
|
||||||
@@ -903,7 +903,7 @@ impl App {
|
|||||||
self.line_renderer.draw(&self.camera)?;
|
self.line_renderer.draw(&self.camera)?;
|
||||||
//let dpi = self.camera.get_dpi();
|
//let dpi = self.camera.get_dpi();
|
||||||
//ui.draw(&gl, dpi)?;
|
//ui.draw(&gl, dpi)?;
|
||||||
|
*/
|
||||||
// Reset the flags about the user action
|
// Reset the flags about the user action
|
||||||
self.camera.reset();
|
self.camera.reset();
|
||||||
|
|
||||||
|
|||||||
@@ -1047,7 +1047,8 @@ impl HiPS {
|
|||||||
Some(self.num_idx as i32),
|
Some(self.num_idx as i32),
|
||||||
WebGl2RenderingContext::UNSIGNED_SHORT,
|
WebGl2RenderingContext::UNSIGNED_SHORT,
|
||||||
0,
|
0,
|
||||||
);
|
)
|
||||||
|
.unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -225,7 +225,8 @@ impl RayTracer {
|
|||||||
None,
|
None,
|
||||||
WebGl2RenderingContext::UNSIGNED_SHORT,
|
WebGl2RenderingContext::UNSIGNED_SHORT,
|
||||||
0,
|
0,
|
||||||
);
|
)
|
||||||
|
.unbind();
|
||||||
#[cfg(feature = "webgl2")]
|
#[cfg(feature = "webgl2")]
|
||||||
shader
|
shader
|
||||||
.attach_uniform("position_tex", &self.position_tex)
|
.attach_uniform("position_tex", &self.position_tex)
|
||||||
@@ -236,6 +237,7 @@ impl RayTracer {
|
|||||||
WebGl2RenderingContext::UNSIGNED_SHORT,
|
WebGl2RenderingContext::UNSIGNED_SHORT,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
.unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_rendering(&self, camera: &CameraViewPort) -> bool {
|
pub fn is_rendering(&self, camera: &CameraViewPort) -> bool {
|
||||||
|
|||||||
@@ -612,7 +612,8 @@ impl Image {
|
|||||||
Some(num_indices),
|
Some(num_indices),
|
||||||
WebGl2RenderingContext::UNSIGNED_SHORT,
|
WebGl2RenderingContext::UNSIGNED_SHORT,
|
||||||
((off_indices as usize) * std::mem::size_of::<u16>()) as i32,
|
((off_indices as usize) * std::mem::size_of::<u16>()) as i32,
|
||||||
);
|
)
|
||||||
|
.unbind();
|
||||||
|
|
||||||
off_indices += self.num_indices[idx];
|
off_indices += self.num_indices[idx];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,7 +259,8 @@ impl Layers {
|
|||||||
None,
|
None,
|
||||||
WebGl2RenderingContext::UNSIGNED_SHORT,
|
WebGl2RenderingContext::UNSIGNED_SHORT,
|
||||||
0,
|
0,
|
||||||
);
|
)
|
||||||
|
.unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first layer must be paint independently of its alpha channel
|
// The first layer must be paint independently of its alpha channel
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import { ContextMenu } from "./gui/ContextMenu.js";
|
|||||||
import { ALEvent } from "./events/ALEvent.js";
|
import { ALEvent } from "./events/ALEvent.js";
|
||||||
import { Color } from './Color.js';
|
import { Color } from './Color.js';
|
||||||
import { ImageFITS } from "./ImageFITS.js";
|
import { ImageFITS } from "./ImageFITS.js";
|
||||||
|
import { VRButton } from "./VRButton.js";
|
||||||
import { DefaultActionsForContextMenu } from "./DefaultActionsForContextMenu.js";
|
import { DefaultActionsForContextMenu } from "./DefaultActionsForContextMenu.js";
|
||||||
import A from "./A.js";
|
import A from "./A.js";
|
||||||
|
|
||||||
@@ -458,7 +459,8 @@ export let Aladin = (function () {
|
|||||||
//this.discoverytree = new DiscoveryTree(this);
|
//this.discoverytree = new DiscoveryTree(this);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
this.view.redraw();
|
// [ ] That might pose problems
|
||||||
|
//this.view.redraw();
|
||||||
|
|
||||||
// go to full screen ?
|
// go to full screen ?
|
||||||
if (options.fullScreen) {
|
if (options.fullScreen) {
|
||||||
@@ -471,6 +473,11 @@ export let Aladin = (function () {
|
|||||||
this.contextMenu = new ContextMenu(this);
|
this.contextMenu = new ContextMenu(this);
|
||||||
this.contextMenu.attachTo(this.view.catalogCanvas, DefaultActionsForContextMenu.getDefaultActions(this));
|
this.contextMenu.attachTo(this.view.catalogCanvas, DefaultActionsForContextMenu.getDefaultActions(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize the VR button
|
||||||
|
if (options.vr) {
|
||||||
|
this.aladinDiv.appendChild(VRButton.createButton(this.view));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**** CONSTANTS ****/
|
/**** CONSTANTS ****/
|
||||||
@@ -671,6 +678,11 @@ export let Aladin = (function () {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// @API
|
||||||
|
Aladin.prototype.setRenderer = function(renderer) {
|
||||||
|
this.options.vr.renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
Aladin.prototype.setFrame = function (frameName) {
|
Aladin.prototype.setFrame = function (frameName) {
|
||||||
if (!frameName) {
|
if (!frameName) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
252
src/js/VRButton.js
Normal file
252
src/js/VRButton.js
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
/**
|
||||||
|
* This is an adaptation of the original VRButton.
|
||||||
|
* Original at:
|
||||||
|
* https://github.com/mrdoob/three.js/blob/dev/examples/jsm/webxr/VRButton.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VRButton class that handles the creation of a VR session
|
||||||
|
*
|
||||||
|
* @class VRButton
|
||||||
|
*/
|
||||||
|
class VRButton {
|
||||||
|
/**
|
||||||
|
* Constructs a VRButton
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @param {View} view - The aladin view
|
||||||
|
* @return {HTMLButtonElement|HTMLAnchorElement} The VR mode button or an
|
||||||
|
* error message
|
||||||
|
*/
|
||||||
|
static createButton(view) {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for handling the process of entering VR mode.
|
||||||
|
*/
|
||||||
|
function showEnterVR(/* device*/) {
|
||||||
|
let currentSession = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to handle when the XR session is started
|
||||||
|
*
|
||||||
|
* @param {XRSession} session - The XR session that has been started
|
||||||
|
*/
|
||||||
|
async function onSessionStarted(session) {
|
||||||
|
session.addEventListener('end', onSessionEnded);
|
||||||
|
|
||||||
|
let gl = view.imageCanvas.getContext('webgl2');
|
||||||
|
await gl.makeXRCompatible();
|
||||||
|
|
||||||
|
session.updateRenderState({
|
||||||
|
baseLayer: new XRWebGLLayer(session, gl)
|
||||||
|
});
|
||||||
|
|
||||||
|
await view.options.vr.renderer.xr.setSession(session);
|
||||||
|
button.textContent = 'EXIT VR';
|
||||||
|
|
||||||
|
// view.options.vr.renderer.setAnimationLoop(view.redrawVR.bind(view));
|
||||||
|
|
||||||
|
session.requestReferenceSpace('local-floor').then((refSpace) => {
|
||||||
|
const xrRefSpace = refSpace;
|
||||||
|
session.requestAnimationFrame((t, frame) => {view.redrawVR(t, frame, xrRefSpace)});
|
||||||
|
});
|
||||||
|
|
||||||
|
currentSession = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to render the whole scene
|
||||||
|
*/
|
||||||
|
// NOTE A supprimer
|
||||||
|
function onXRAnimationFrame(t, xrFrame) {
|
||||||
|
currentSession.requestAnimationFrame(onXRAnimationFrame);
|
||||||
|
view.redrawVR();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to handle when the XR session ends
|
||||||
|
*/
|
||||||
|
function onSessionEnded(/* event*/) {
|
||||||
|
currentSession.removeEventListener('end', onSessionEnded);
|
||||||
|
|
||||||
|
button.textContent = 'ENTER VR';
|
||||||
|
|
||||||
|
currentSession = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
button.style.display = '';
|
||||||
|
|
||||||
|
button.style.cursor = 'pointer';
|
||||||
|
button.style.left = 'calc(50% - 50px)';
|
||||||
|
button.style.width = '100px';
|
||||||
|
|
||||||
|
button.textContent = 'ENTER VR';
|
||||||
|
|
||||||
|
button.onmouseenter = function() {
|
||||||
|
button.style.opacity = '1.0';
|
||||||
|
};
|
||||||
|
|
||||||
|
button.onmouseleave = function() {
|
||||||
|
button.style.opacity = '0.5';
|
||||||
|
};
|
||||||
|
|
||||||
|
button.onclick = function() {
|
||||||
|
if (currentSession === null) {
|
||||||
|
// WebXR's requestReferenceSpace only works if the corresponding
|
||||||
|
// feature was requested at session creation time. For simplicity,
|
||||||
|
// just ask for the interesting ones as optional features, but be
|
||||||
|
// aware that the requestReferenceSpace call will fail if it turns
|
||||||
|
// out to be unavailable.
|
||||||
|
// ('local' is always available for immersive sessions and doesn't
|
||||||
|
// need to be requested separately.)
|
||||||
|
|
||||||
|
const sessionInit = {optionalFeatures: ['local-floor']};
|
||||||
|
navigator.xr.requestSession(
|
||||||
|
'immersive-vr', sessionInit).then(onSessionStarted);
|
||||||
|
} else {
|
||||||
|
currentSession.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for disabling the VR mode button
|
||||||
|
*
|
||||||
|
* @param {HTMLButtonElement} button - The VR mode button element to
|
||||||
|
* be disabled
|
||||||
|
*/
|
||||||
|
function disableButton() {
|
||||||
|
button.style.display = '';
|
||||||
|
|
||||||
|
button.style.cursor = 'auto';
|
||||||
|
button.style.left = 'calc(50% - 75px)';
|
||||||
|
button.style.width = '150px';
|
||||||
|
|
||||||
|
button.onmouseenter = null;
|
||||||
|
button.onmouseleave = null;
|
||||||
|
|
||||||
|
button.onclick = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for handling the case where WebXR is not supported
|
||||||
|
*
|
||||||
|
* @description This function disables the VR mode button and displays a
|
||||||
|
* message indicating that VR is not supported
|
||||||
|
*
|
||||||
|
* @param {HTMLButtonElement} button - The VR mode button element to be
|
||||||
|
* disabled and updated with a message
|
||||||
|
*/
|
||||||
|
function showWebXRNotFound() {
|
||||||
|
disableButton();
|
||||||
|
|
||||||
|
button.textContent = 'VR NOT SUPPORTED';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for handling the case where VR is not allowed due to an
|
||||||
|
* exception
|
||||||
|
*
|
||||||
|
* @description This function disables the VR mode button, logs an
|
||||||
|
* exception to the console, and displays a message indicating that VR
|
||||||
|
* is not allowed
|
||||||
|
*
|
||||||
|
* @param {any} exception - The exception object or error that indicates
|
||||||
|
* why VR is not allowed
|
||||||
|
* @param {HTMLButtonElement} button - The VR mode button element to be
|
||||||
|
* disabled and updated with a message
|
||||||
|
*/
|
||||||
|
function showVRNotAllowed(exception) {
|
||||||
|
disableButton();
|
||||||
|
|
||||||
|
console.warn('Exception when trying to call xr.isSessionSupported',
|
||||||
|
exception);
|
||||||
|
|
||||||
|
button.textContent = 'VR NOT ALLOWED';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for styling an HTML element with specific CSS properties
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element - The HTML element to be styled
|
||||||
|
*/
|
||||||
|
function stylizeElement(element) {
|
||||||
|
element.style.position = 'absolute';
|
||||||
|
element.style.bottom = '20px';
|
||||||
|
element.style.padding = '12px 6px';
|
||||||
|
element.style.border = '1px solid #fff';
|
||||||
|
element.style.borderRadius = '4px';
|
||||||
|
element.style.background = 'rgba(0,0,0,0.1)';
|
||||||
|
element.style.color = '#fff';
|
||||||
|
element.style.font = 'normal 13px sans-serif';
|
||||||
|
element.style.textAlign = 'center';
|
||||||
|
element.style.opacity = '0.5';
|
||||||
|
element.style.outline = 'none';
|
||||||
|
element.style.zIndex = '999';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('xr' in navigator) {
|
||||||
|
button.id = 'VRButton';
|
||||||
|
button.style.display = 'none';
|
||||||
|
|
||||||
|
stylizeElement(button);
|
||||||
|
|
||||||
|
navigator.xr.isSessionSupported('immersive-vr').then(function(supported) {
|
||||||
|
supported ? showEnterVR() : showWebXRNotFound();
|
||||||
|
|
||||||
|
if (supported && VRButton.xrSessionIsGranted) {
|
||||||
|
button.click();
|
||||||
|
}
|
||||||
|
}).catch(showVRNotAllowed);
|
||||||
|
|
||||||
|
return button;
|
||||||
|
} else {
|
||||||
|
const message = document.createElement('a');
|
||||||
|
|
||||||
|
if (window.isSecureContext === false) {
|
||||||
|
message.href = document.location.href.replace(/^http:/, 'https:');
|
||||||
|
message.innerHTML = 'WEBXR NEEDS HTTPS';
|
||||||
|
} else {
|
||||||
|
message.href = 'https://immersiveweb.dev/';
|
||||||
|
message.innerHTML = 'WEBXR NOT AVAILABLE';
|
||||||
|
}
|
||||||
|
|
||||||
|
message.style.left = 'calc(50% - 90px)';
|
||||||
|
message.style.width = '180px';
|
||||||
|
message.style.textDecoration = 'none';
|
||||||
|
|
||||||
|
stylizeElement(message);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a listener for the "sessiongranted" event to track the XR
|
||||||
|
* session being granted.
|
||||||
|
*
|
||||||
|
* @description This method checks if the WebXR API is available and
|
||||||
|
* registers a listener for the "sessiongranted" event to track when an
|
||||||
|
* XR session is granted. It sets the `VRButton.xrSessionIsGranted`
|
||||||
|
* property to `true` when the event is triggered.
|
||||||
|
*/
|
||||||
|
static registerSessionGrantedListener() {
|
||||||
|
if ('xr' in navigator) {
|
||||||
|
// WebXRViewer (based on Firefox) has a bug where addEventListener
|
||||||
|
// throws a silent exception and aborts execution entirely.
|
||||||
|
if (/WebXRViewer\//i.test(navigator.userAgent)) return;
|
||||||
|
|
||||||
|
navigator.xr.addEventListener('sessiongranted', () => {
|
||||||
|
VRButton.xrSessionIsGranted = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VRButton.xrSessionIsGranted = false;
|
||||||
|
VRButton.registerSessionGrantedListener();
|
||||||
|
|
||||||
|
export {VRButton};
|
||||||
@@ -370,7 +370,7 @@ export let View = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.computeNorder();
|
this.computeNorder();
|
||||||
this.redraw();
|
//this.redraw();
|
||||||
};
|
};
|
||||||
|
|
||||||
var pixelateCanvasContext = function (ctx, pixelateFlag) {
|
var pixelateCanvasContext = function (ctx, pixelateFlag) {
|
||||||
@@ -1059,6 +1059,41 @@ export let View = (function () {
|
|||||||
|
|
||||||
View.FPS_INTERVAL = 1000 / 140;
|
View.FPS_INTERVAL = 1000 / 140;
|
||||||
|
|
||||||
|
|
||||||
|
View.prototype.redrawVR = function (t, frame, xrRefSpace) {
|
||||||
|
const session = frame.session;
|
||||||
|
session.requestAnimationFrame((t, frame) => {this.redrawVR(t, frame, xrRefSpace)});
|
||||||
|
|
||||||
|
let pose = frame.getViewerPose(xrRefSpace);
|
||||||
|
|
||||||
|
if (!pose) return;
|
||||||
|
|
||||||
|
// Elapsed time since last loop
|
||||||
|
const now = Date.now();
|
||||||
|
const elapsedTime = now - this.then;
|
||||||
|
|
||||||
|
// If enough time has elapsed, draw the next frame
|
||||||
|
//if (elapsedTime >= View.FPS_INTERVAL) {
|
||||||
|
// Get ready for next frame by setting then=now, but also adjust for your
|
||||||
|
// specified fpsInterval not being a multiple of RAF's interval (16.7ms)
|
||||||
|
|
||||||
|
// Drawing code
|
||||||
|
try {
|
||||||
|
this.moving = this.wasm.update(elapsedTime);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
////// 2. Draw catalogues////////
|
||||||
|
const isViewRendering = this.wasm.isRendering();
|
||||||
|
if (isViewRendering || this.needRedraw) {
|
||||||
|
this.drawAllOverlays();
|
||||||
|
}
|
||||||
|
this.needRedraw = false;
|
||||||
|
|
||||||
|
this.options.vr.animation();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* redraw the whole view
|
* redraw the whole view
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user