janusweb
Version:
Web client for JanusVR worlds
404 lines (380 loc) • 13.1 kB
JavaScript
janus.registerElement('xrmenu', {
buttonwidth: .1,
buttonheight: .1,
buttonmargin: 1.2,
create() {
/*
this.backing = this.createObject('object', {
id: 'plane',
col: '#111',
scale: V(0, 0, 1),
pos: V(0, 0, 0),
renderorder: 9,
});
*/
console.log("creating xrmenu")
this.worldpos = V();
this.worlddir = V();
this.playervec = V();
this.panels = {};
let xrmenu = janus.ui.apps.default.apps.xrmenu;
if (!xrmenu) return;
let asseturl = xrmenu.resolveFullURL('./xrmenu-assets.json');
fetch(asseturl).then(res => res.json()).then(assetlist => {
this.assetpack = elation.engine.assets.loadJSON(assetlist, xrmenu.resolveFullURL('./'));
/*
* Navigation
* Home
* Respawn
* Exit VR
* Refresh
* Sound
* Output
* Output Select
* Volume Levels
* Environment
* Media
* Voices
* Input
* Input Select
* Volume Levels
* Settings
*/
this.buttons = {
home: this.createObject('xrmenu-button', {
pos: V(.08, 0, 0),
label: 'Home',
image_id: 'xrmenu-button-home',
//onactivate: (ev) => janus.navigateHome(),
}),
back: this.createObject('xrmenu-button', {
pos: V(.20, 0, 0),
label: 'Back',
image_id: 'xrmenu-button-back',
//onactivate: (ev) => janus.navigateBack(),
}),
reset: this.createObject('xrmenu-button', {
pos: V(.32, 0, 0),
label: 'Reset',
image_id: 'xrmenu-button-reset',
//onactivate: (ev) => player.reset_position(),
}),
reload: this.createObject('xrmenu-button', {
pos: V(.44, 0, 0),
label: 'Reload',
image_id: 'xrmenu-button-reload',
//onactivate: (ev) => location.reload(),
}),
exitvr: this.createObject('xrmenu-button', {
pos: V(.56, 0, 0),
label: 'Exit VR',
//image_id: 'xrmenu-button-exitvr',
//onactivate: (ev) => this.engine.client.stopXR(),
}),
sound: this.createObject('xrmenu-button', {
pos: V(.56, 0, 0),
label: 'Exit VR',
image_id: 'xrmenu-button-sound',
//onactivate: (ev) => this.engine.client.stopXR(),
}),
teleport: this.createObject('xrmenu-button', {
pos: V(.68, 0, 0),
label: 'Teleport',
image_id: 'xrmenu-button-teleport',
toggle: true,
enabled: room.teleport,
//onactivate: (ev) => this.engine.client.stopXR(),
}),
};
this.buttons.home.addEventListener('activate', ev => janus.navigateHome());
this.buttons.back.addEventListener('activate', ev => janus.navigateBack());
this.buttons.reset.addEventListener('activate', ev => player.reset_position());
this.buttons.reload.addEventListener('activate', ev => location.reload());
this.buttons.exitvr.addEventListener('activate', ev => this.engine.client.stopXR());
this.buttons.sound.addEventListener('activate', ev => this.toggleSound());
this.buttons.teleport.addEventListener('activate', ev => this.toggleTeleport());
this.reflow();
});
},
reflow() {
let buttonnames = Object.keys(this.buttons);
let width = this.buttonwidth * buttonnames.length * this.buttonmargin,
height = this.buttonheight * this.buttonmargin;
//this.backing.scale.x = width;
//this.backing.scale.y = height;
for (let i = 0; i < buttonnames.length; i++) {
let button = this.buttons[buttonnames[i]];
button.pos.x = (-width / 2) + (this.buttonwidth * this.buttonmargin * (i + .5)) ;
}
},
toggleSound(){
if (!this.panels['sound']) {
this.panels['sound'] = this.buttons.sound.createObject('xrmenu-popup', {
content: 'janus-voip-picker'
});
} else {
if (this.panels['sound'].parent === this.buttons.sound) {
this.buttons.sound.removeChild(this.panels['sound']);
} else {
this.buttons.sound.appendChild(this.panels['sound']);
}
}
},
toggleTeleport(){
room.teleport = !room.teleport
this.buttons.teleport.state(room.teleport)
},
/*
update() {
let worldpos = this.localToWorld(this.worldpos.set(0,0,0));
let worlddir = this.localToWorld(this.worlddir.set(0,0,1)).sub(worldpos).normalize();
let playervec = player.head.localToWorld(this.playervec.set(0,0,0)).sub(worldpos).normalize();
let dot = playervec.dot(worlddir);
let opacity = Math.max(0, dot);
for (let k in this.buttons) {
if (this.buttons[k].button) {
this.buttons[k].button.opacity = opacity;
}
}
//this.backing.opacity = opacity;
},
*/
});
janus.registerElement('xrmenu-button', {
onactivate: null,
image_id: '',
toggle: false,
enabled: true,
create() {
this.button = this.createObject('object', {
id: 'plane',
scale: V(.1, .1, .02),
col: '#fff',
pos: V(0, 0, this.enabled ? 0.01 : 0 ),
collision_id: 'cube',
image_id: this.image_id,
renderorder: 10,
emissive: this.enabled ? (this.toggle ? '#FFF' : '#595')
: '#555',
});
this.button.addEventListener('mouseover', ev => this.handleMouseOver(ev));
this.button.addEventListener('mouseout', ev => this.handleMouseOut(ev));
this.button.addEventListener('mousedown', ev => this.handleMouseDown(ev));
this.button.addEventListener('mouseup', ev => this.handleMouseUp(ev));
this.button.addEventListener('click', ev => this.handleClick(ev));
},
handleMouseOver(ev) {
if( this.toggle ) return
this.button.emissive = '#595';
},
handleMouseOut(ev) {
if( this.toggle ) return
this.button.emissive = '#555';
},
handleMouseDown(ev) {
this.button.pos.z = 0;
},
handleMouseUp(ev) {
this.button.pos.z = .01;
},
state(enabled){
this.button.emissive = (this.enabled = enabled) ? '#FFF' : '#555'
this.button.pos.z = enabled ? .01 : 0
},
handleClick(ev) {
this.dispatchEvent({type: 'activate'});
},
});
janus.registerElement('xrmenu-popup', {
content: 'ui-content',
element: null,
width: 512,
height: 512,
depth_test: true,
renderorder: 0,
create() {
let element = elation.elements.create(this.content, { deferred: false });
//document.body.appendChild(element);
//this.shadowdom.appendChild(element);
let container = document.createElement('div');
this.shadowdom = container.attachShadow({mode: 'open'});
//container.appendChild(element);
setTimeout(() => {
this.initShadowStylesheets();
}, 0);
//this.shadowdom.appendChild(element);
let a = document.createElement('html');
let b = document.createElement('body');
b.className = 'dark janusweb';
a.appendChild(b);
this.shadowdom.appendChild(a);
b.appendChild(element);
document.body.appendChild(container);
elation.events.add(element, 'styleupdate', ev => {
this.initShadowStylesheets();
});
container.style.position = 'absolute';
container.style.top = '0';
container.style.left = '0';
container.style.zIndex = -1000;
container.style.width = this.width + 'px';
container.style.height = this.height + 'px';
container.style.overflow = 'hidden';
container.style.opacity = 0;
//container.style.transform = 'translateX(-40000px)';
//container.style.border = '1px solid red';
this.elementcontainer = container;
this.initElementCanvas();
this.plane = this.createObject('object', {
id: 'plane',
collision_id: 'cube',
collision_scale: V(1, 1, .0001),
lighting: false,
//image_id: 'xrmenu-element-canvas',
scale: V(1, this.height / this.width, 1),
depth_test: this.depth_test,
renderorder: this.renderorder,
});
this.element = element;
this.plane.addEventListener('mousemove', ev => this.handleMouse(ev));
this.plane.addEventListener('mousedown', ev => this.handleMouse(ev));
this.plane.addEventListener('wheel', ev => this.handleMouse(ev));
this.plane.addEventListener('mouseup', ev => this.handleMouse(ev));
this.plane.addEventListener('click', ev => this.handleMouse(ev));
this.plane.addEventListener('mouseover', ev => this.handleMouseOver(ev));
this.plane.addEventListener('mouseout', ev => this.handleMouseOut(ev));
},
initElementCanvas() {
let element = this.element;
if (!(element && element.isConnected && element.toCanvas)) {
setTimeout(() => this.initElementCanvas(), 100);
return;
}
console.log(element.isConnected, element);
let canvas = element.toCanvas(this.width, this.height, 1);
/*
document.body.appendChild(canvas);
canvas.style.position = 'absolute';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.zIndex = 1000;
canvas.style.border = '1px solid red';
*/
this.loadNewAsset('image', {
id: 'xrmenu-element-canvas',
canvas: canvas,
transparent: true,
});
this.plane.image_id = 'xrmenu-element-canvas';
this.canvas = canvas;
elation.events.add(canvas, 'asset_update', ev => this.refresh());
element.updateStylesheets(elation.engine.assets.corsproxy).then(d => {
element.updateCanvas();
this.refresh();
});
},
initShadowStylesheets() {
let promises = [];
for (let i = 0; i < document.styleSheets.length; i++) {
//promises.push(new Promise(accept => {
if (document.styleSheets[i].href ) {
// external stylesheet
let styleel = document.createElement('link');
styleel.rel = 'stylesheet';
styleel.href = elation.engine.assets.corsproxy + document.styleSheets[i].href;
this.shadowdom.appendChild(styleel);
} else if (document.styleSheets[i].cssRules.length > 0) {
// inline <style> definition
let txt = '';
let sheet = document.styleSheets[i];
for (let i = 0; i < sheet.cssRules.length; i++) {
txt += sheet.cssRules[i].cssText + '\n';
}
let styleel = document.createElement('style');
styleel.appendChild(document.createTextNode(txt));
this.shadowdom.appendChild(styleel);
}
//}));
}
/*
Promise.all(promises).then(() => {
console.log('stylesheets loaded!');
setTimeout(() => {
this.element.updateCanvas().then(() => this.refresh());
}, 400);
});
*/
},
handleMouse(ev) {
if (this.element && this.canvas) {
let mousexy = [ev.data.uv.x * this.canvas.width, (1 - ev.data.uv.y) * this.canvas.height];
let EventClass = (ev.type == 'wheel' ? WheelEvent : MouseEvent);
let fakeev = new EventClass(ev.type, {
bubbles: true,
cancelable: true,
screenX: mousexy[0],
screenY: mousexy[1],
clientX: mousexy[0],
clientY: mousexy[1],
view: window,
});
//let target = document.elementFromPoint(mousexy[0], mousexy[1]) || this.element;
let target = this.shadowdom.elementFromPoint(mousexy[0], mousexy[1]) || this.element;
//let target = document.elementFromPoint(mousexy[0], mousexy[1]) || this.element;
//console.log(mousexy, fakeev, ev, target);
target.dispatchEvent(fakeev);
if (ev.type == 'mousemove') {
if (target !== this.currenttarget) {
if (this.currenttarget) {
let mouseout = new EventClass('mouseout', {
bubbles: true,
cancelable: true,
screenX: mousexy[0],
screenY: mousexy[1],
clientX: mousexy[0],
clientY: mousexy[1],
view: window,
});
this.currenttarget.dispatchEvent(mouseout);
}
this.currenttarget = target;
let mousemove = new EventClass('mousemove', {
bubbles: true,
cancelable: true,
screenX: mousexy[0],
screenY: mousexy[1],
clientX: mousexy[0],
clientY: mousexy[1],
view: window,
});
this.currenttarget.dispatchEvent(mousemove);
//let mouseover = elation.events.clone(fakeev, { type: 'mouseover' });
let mouseover = new EventClass('mouseover', {
bubbles: true,
cancelable: true,
screenX: mousexy[0],
screenY: mousexy[1],
clientX: mousexy[0],
clientY: mousexy[1],
view: window,
});
target.dispatchEvent(mouseover);
}
}
//this.element.updateCanvas();
//setTimeout(() => this.element.updateCanvas(), 10);
}
},
handleMouseOver(ev) {
this.elementcontainer.style.zIndex = 1000;
if (!this.elementcontainer.parentNode) {
document.body.appendChild(this.elementcontainer);
}
},
handleMouseOut(ev) {
this.elementcontainer.style.zIndex = -1000;
if (this.elementcontainer.parentNode) {
this.elementcontainer.parentNode.removeChild(this.elementcontainer);
}
},
});