Jean-Yves Didier

developped a first toy case to display webxr

......@@ -40,16 +40,16 @@ Animator = ARCS.Component.create(
*/
this.stop = function () {
paused = true;
};
/**
* Signals that an animation frame is ready.
* @signal
* @function Animator#onAnimationFrame
*/
};
},
['start','stop'],
'onAnimationFrame'
);
/**
* Signals that an animation frame is ready.
* @signal
* @function Animator#onAnimationFrame
*/
export default {Animator: Animator};
......
......@@ -6,9 +6,11 @@ import FrustumCamera from '../deps/three.js/frustumcamera.js';
var ARViewer;
/**
* @class ARViewer
* @classdesc Simple compositing viewer for augmented reality
*/
* @class ARViewer
* @classdesc Simple compositing viewer for augmented reality
* This class, while being useful is now superceded by the XRViewer component.
* @deprecated Use componenent XRViewer instead
*/
ARViewer = ARCS.Component.create(
/** @lends ARViewer.prototype */
function () {
......
......@@ -2,6 +2,15 @@ import * as ARCS from '../build/arcs.js';
let Filter;
/**
* SlotConfig
* @typedef {object} SlotConfig
* @property slot {string} slot name.
* @property func {string} a string enclosing javascript code acting as the body
* of the slot function. <b>self</b> can be used inside of it in order to emit
* signals.
*/
/**
* @class Filter
* @classdesc Creates a filter in between a signal and a slot
......@@ -10,7 +19,11 @@ let Filter;
* so that data is adapted, on the fly, by the component.
* In a way, it is similar to filters in pipe|filter architectures.
*
* @param config {FilterConfig} configuration object to initialize filter
* @param config {Object} configuration object to initialize filter
* @param config.signals {Array.string} the list signals potentially emitted by
* the filter.
* @param config.slots {Array.SlotConfig} the list of slots declared for this
* component.
*/
Filter = ARCS.Component.create(
/** @lends Filter.prototype */
......
......@@ -2,30 +2,67 @@ import ARCS from '../build/arcs.js';
let UIMapper ;
/**
* UIMapperConfigItem
* An item describing a slot for an UIMapper component
* @typedef {Object} UIMapperConfigItem
* @property param {string} a string representing a parameter. It can reach
* subcomponents of an object by using the notation X.subcomp1.subcomp2 ...
* where X value is the position index of the parameter
* @property field {string} a string representing a reference to an HTML element
* and its subcomponents in the same way as the param property. However, the X
* component relates to a CSS selector (without CSS classes)
* @property format {string} a string representing code for a callback function.
* The callback should only expect one parameter, which is the value given by
* the parameter param
* @property value a value that should be injected by the slot to the HTML field.
* It can be used instead of param.
*/
/**
* UIMapperConfig
* An UIMapperConfig is a javascript object. Each property is describing a slot.
* Every property will be of type UIMapperConfigItem
* @typedef {Object} UIMapperConfig
*/
/**
* @class UIMapper
* @classdesc Component that maps slot input to an HTML element
* UIMapper is using metaprogramming, that is to say the configuration object
* will determine what are the needed slots for the component instance as well
* as the way they will tie to any HTML element.
* @param obj {UIMapperConfig} configuration object to initialize the component.
*/
UIMapper = ARCS.Component.create(
function(obj) {
let self = this;
// returns a map function
const mapFunction = function(param, field, format) {
let fields = field.split('.');
const mapFunction = function(param, field, format, val) {
let params = param.split('.');
let eltField = document.querySelector(fields[0]);
for(let i=1; i < fields.length-1; i++) {
eltField = eltField[fields[i]];
}
let func = null;
if (format !== undefined) {
func = new Function(`return ${format}`)();
}
let fields = field.split('.');
let eltField = document.querySelector(fields[0]);
for(let i=1; i < fields.length-1; i++) {
eltField = eltField[fields[i]];
}
return function() {
let value = arguments[params[0]];
for (let i=1; i < params.length; i++) {
value = value[params[i]];
}
let value ;
if (val === undefined) {
value = arguments[params[0]];
for (let i=1; i < params.length; i++) {
value = value[params[i]];
}
} else {
value = val;
}
eltField[fields[fields.length-1]] = (func)?((typeof func === 'function')?func(value):func):value;
}
};
......@@ -36,7 +73,7 @@ UIMapper = ARCS.Component.create(
let slotFunction = function(pobj) {
return function() {
pobj.map( elt => {
mapFunction(elt.param, elt.field,elt.format).apply(null, arguments);
mapFunction(elt.param, elt.field,elt.format, elt.value).apply(null, arguments);
});
};
}
......
......@@ -9,21 +9,15 @@ import ARCS from '../build/arcs.js';
var LiveSource, VideoSource;
/**
* VideoConfig
* @typedef {Object} VideoConfig
* @property {number} width - preferred image width in pixels (optional, default: 320)
* @property {number} height - preferred image height in pixels (optional, default: 240)
* @property {number} facingMode - camera facingMode as listed in {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/facingMode}
* @property {HTMLVideoElement} video - css selector for the video element (optional, default: newly created shadow video element)
* @property {HTMLCanvasElement} canvas - css selector for the canvas element (optinal, default: newly created shadow canvas element)
*/
/**
* @class LiveSource
* @classdesc Starts the acquisition of a live video stream provided by a webcam
* @param config {VideoConfig} configuration object to initialize the component.
* @param config {object} configuration object to initialize the component.
* @param [config.width=320] {number} - preferred image width in pixels.
* @param [config.height=240] {number} - preferred image height in pixels.
* @param [config.facingMode] {string} - camera facingMode as listed in [MDN]{@link https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/facingMode}
* @param [config.video] {HTMLVideoElement} - css selector for the video element (default: newly created shadow video element)
* @param [config.canvas] {HTMLCanvasElement} - css selector for the canvas element (default: newly created shadow canvas element)
*/
LiveSource = ARCS.Component.create(
/** @lends LiveSource.prototype */
......
import ARCS from '../build/arcs.js';
import * as THREE from '../deps/three.js/index.js';
import ARButton from '../deps/three.js/ARButton.js';
import {ARButton} from '../deps/three.js/ARButton.js';
var XRViewer;
/**
* @class XRViewer
* @classdesc Simple compositing viewer for augmented reality using WebXR
* @param [config] {object} configuration object for the viewer
* @param [config.rootElement="document.body"] {string} element on which to attach renderer
* @param [config.sessionConfig] {object} an object to configure an immersive
* session as formulated in WebXR specification.
*/
XRViewer = ARCS.Component.create(
/** @lends XRViewer.prototype */
......@@ -15,6 +19,7 @@ XRViewer = ARCS.Component.create(
let renderer, scene, camera;
let sceneId;
let container;
let self = this;
let _config = config || {};
......@@ -22,6 +27,13 @@ XRViewer = ARCS.Component.create(
document.querySelector(_config.rootElement):document.body;
container = container || document.body;
if (_config.sessionConfig && _config.sessionConfig.domOverlay
&& _config.sessionConfig.domOverlay && _config.sessionConfig.domOverlay.root) {
let rootOverlay = document.querySelector(_config.sessionConfig.domOverlay.root);
_config.sessionConfig.domOverlay.root = rootOverlay || document.body;
}
// scenegraph initializations
scene = new THREE.Scene();
......@@ -31,7 +43,7 @@ XRViewer = ARCS.Component.create(
renderer.setPixelRatio(window.devicePixelRatio);
renderer.xr.enabled = true;
container.appendChild(renderer.domElement);
container.appendChild(ARButton.createButton(renderer));
container.appendChild(ARButton.createButton(renderer,_config.sessionConfig));
var theta = 45;
camera = new THREE.PerspectiveCamera(theta, container.width / container.height, 0.01, 10);
......@@ -61,24 +73,27 @@ XRViewer = ARCS.Component.create(
scene.remove(subScene);
};
/**
* Starts the animation loop.
* Each time an animation frame is obtained, preRender and postRender
* signals are triggered
* @slot
* @function XRViewer#start
* @triggers preRender
* @triggers postRender
*/
this.start = function() {
renderer.setAnimationLoop(render);
};
/**
* Triggers the rendering of the composite scene
* @function XRViewer#render
* @slot
*/
let render = function (time, frame) {
this.emit("preRender",time, frame);
renderer.render(scene3d, camera3d);
this.emit("postRender",time, frame);
self.emit("preRender",time,frame);
renderer.render(scene, camera);
self.emit("postRender",time,camera);
}
},
/** @lends ARViewer.slots */
/** @lends XRViewer.slots */
[
'setWidgets','addScene',
'removeScene','render',
......@@ -87,5 +102,23 @@ XRViewer = ARCS.Component.create(
['preRender','postRender']
);
/**
* emitted when a frame is obtained before rendering of the scene.
* Use this if you want to update the scenegraph.
* @function XRViewer#preRender
* @signal
* @param time {number} time elapsed since the beginning of the loop in seconds
* @param frame {object} an XRFrame as specified by WebXR
*/
/**
* emitted when a frame is obtained after rendering of the scene.
* @function XRViewer#postRender
* @signal
* @param time {number} time elapsed since the beginning of the loop in seconds
* @param frame {object} an XRFrame as specified by WebXR
*/
export default {XRViewer: XRViewer};
......
@viewport {
width: device-width;
zoom: 1;
}
body {
font-family: sans-serif;
padding: 0px;
margin: 0px;
}
.info {
background: navy;
opacity: 0.6;
font-weight: bold;
font-size: 120%;
color: white;
}
.info p {
margin:0;
}
#coords {
position: absolute;
padding: 10px;
left: 10px;
top: 10px;
}
#container {
left:0px;
top:0px;
width: 100vw;
height: 100vh;
}
#logo {
position: absolute;
margin:0px;
padding:0px;
right: 0px;
bottom:0px;
}
{
"context" : {
"libraries" : [ "../components/xrviewer.js", "../components/uimapper.js"],
"components" : {
"viewer" : { "type": "XRViewer", "value": {
"sessionConfig": {
"optionalFeatures": [ "dom-overlay" ],
"domOverlay" : { "root": "body"}
}
}
},
"display" : { "type": "UIMapper", "value": {
"updatePosition" : [
{ "param" : "1.position.x", "field" : "#x.innerHTML", "format": "e=>e.toFixed(2)" },
{ "param" : "1.position.y", "field" : "#y.innerHTML", "format": "e=>e.toFixed(2)" },
{ "param" : "1.position.z", "field" : "#z.innerHTML", "format": "e=>e.toFixed(2)" }
]
}
},
"statemachine" : {
"type": "StateMachine",
"value" : {
"initial": "start",
"final": "end",
"transitions" : {
"start" : { "end" : "end"}
}
}
}
}
},
"controller" : "statemachine",
"sheets" : {
"start" : {
"preconnections" : [],
"postconnections" : [
{ "destination": "viewer", "slot": "start", "value": []}
],
"connections" : [
{ "source": "viewer", "signal": "postRender", "destination": "display", "slot": "updatePosition"}
],
"cleanups" : []
},
"end" : {
"preconnections" : [],
"postconnections" : [],
"connections" : [],
"cleanups" : []
}
}
}
<html>
<head>
<title>ARCS: WebXR example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link type="text/css" rel="stylesheet" href="arcs.css">
<script type="module"
data-base-url="../.."
data-arcsapp="arcsapp.json"
src="../../build/arcs_browser.js">
</script>
</head>
<body>
<div id="container">
<div class="info" id="coords">
<p>X <span id="x"></span></p>
<p>Y <span id="y"></span></p>
<p>Z <span id="z"></span></p>
</div>
<div id="contents">
<a id="logo" href="http://arcs.ibisc.univ-evry.fr"><img src="../../docs/arcs_logo.png" alt="ARCS"/></a>
</div>
</div>
</body>
</html>