accessibility command plugins
This commit is contained in:
parent
4917791455
commit
0d73a822e8
|
@ -5,7 +5,8 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
|
||||
<script src="./../../../dist/aframe.min.js"></script> <!-- v1.5.0 -->
|
||||
<!-- AFRAME v1.5.0 + extra THREE.js extra loaders -->
|
||||
<script src="./../../../dist/aframe.min.js"></script>
|
||||
<script src="./../../../dist/xrfragment.aframe.js"></script>
|
||||
|
||||
<!-- important: allow touchevents in AR -->
|
||||
|
|
Binary file not shown.
2
make
2
make
|
@ -105,7 +105,7 @@ build(){
|
|||
cp src/3rd/js/plugin/frontend/\$editor.js dist/xrfragment.plugin.editor.js
|
||||
|
||||
cp src/3rd/js/plugin/frontend/css.js dist/xrfragment.plugin.frontend.css.js
|
||||
jscat src/3rd/js/plugin/frontend/{snackbar,accessibility,\$menu,frontend}.js > dist/xrfragment.plugin.frontend.js
|
||||
jscat src/3rd/js/plugin/frontend/{snackbar,accessibility,\$menu,frontend,chatcommand/*}.js > dist/xrfragment.plugin.frontend.js
|
||||
|
||||
jscat src/3rd/js/plugin/matrix/{matrix-crdt,matrix}.js > dist/xrfragment.plugin.matrix.js
|
||||
jscat src/3rd/js/plugin/p2p/{trystero-torrent.min,trystero}.js > dist/xrfragment.plugin.p2p.js
|
||||
|
|
|
@ -53,10 +53,16 @@ window.AFRAME.registerComponent('xrf', {
|
|||
VRbutton = document.querySelector('.a-enter-vr-button')
|
||||
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#-VR' ) )
|
||||
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
|
||||
//if( AFRAME.utils.device.checkARSupport() && VRbutton ){
|
||||
// VRbutton.style.display = 'none'
|
||||
// ARbutton.parentNode.style.right = '20px'
|
||||
//}
|
||||
})
|
||||
|
||||
// (de)active look-controls because of 'rot=' XR Fragment
|
||||
aScene.addEventListener('loaded', () => {
|
||||
// this is just for convenience (not part of spec): enforce AR + hide/show stuff based on VR tags in 3D model
|
||||
aScene.canvas.addEventListener('mousedown', () => xrf.camera.el.setAttribute("look-controls","") )
|
||||
})
|
||||
XRF.addEventListener('rot',(e) => {
|
||||
let lookcontrols = document.querySelector('[look-controls]')
|
||||
if( lookcontrols ) lookcontrols.removeAttribute("look-controls")
|
||||
})
|
||||
|
||||
let repositionUser = (scale) => () => {
|
||||
|
|
|
@ -28,6 +28,7 @@ chatComponent = {
|
|||
$messages: el.querySelector("#messages"),
|
||||
$chatline: el.querySelector("#chatline"),
|
||||
$chatbar: el.querySelector("#chatbar"),
|
||||
$chatsend: el.querySelector("#chatsend"),
|
||||
|
||||
install(opts){
|
||||
this.opts = opts
|
||||
|
@ -40,6 +41,19 @@ chatComponent = {
|
|||
this.send({message:`Welcome to <b>${document.location.search.substr(1)}</b>, a 3D scene(file) which simply links to other ones.<br>You can start a solo offline exploration in XR right away.<br>Type /help below, or use the arrow- or WASD-keys on your keyboard, and mouse-drag to rotate.<br>`, class: ["info","guide","multiline"] })
|
||||
},
|
||||
|
||||
sendInput(value){
|
||||
if( value[0] == '#' ) return xrf.navigator.to(value)
|
||||
let event = value.match(/^[!\/]/) ? "chat.command" : "network.send"
|
||||
let message = value.replace(/^[!\/]/,'')
|
||||
let raw = {detail:{message:value, halt:false}}
|
||||
document.dispatchEvent( new CustomEvent( event, {detail: {message}} ) )
|
||||
document.dispatchEvent( new CustomEvent( "chat.input", raw ) )
|
||||
if( event == "network.send" && !raw.detail.halt ) this.send({message: value })
|
||||
this.$chatline.lastValue = value
|
||||
this.$chatline.value = ''
|
||||
if( window.innerHeight < 600 ) this.$chatline.blur()
|
||||
},
|
||||
|
||||
initListeners(){
|
||||
let {$chatline} = this
|
||||
|
||||
|
@ -47,12 +61,10 @@ chatComponent = {
|
|||
|
||||
$chatline.addEventListener('keydown', (e) => {
|
||||
if (e.key == 'Enter' ){
|
||||
let event = $chatline.value.match(/^[!\/]/) ? "chat.command" : "network.send"
|
||||
let message = $chatline.value.replace(/^[!\/]/,'')
|
||||
document.dispatchEvent( new CustomEvent( event, {detail: {message}} ) )
|
||||
if( event == "network.send" ) this.send({message: $chatline.value })
|
||||
$chatline.value = ''
|
||||
if( window.innerHeight < 600 ) $chatline.blur()
|
||||
this.sendInput($chatline.value)
|
||||
}
|
||||
if (e.key == 'ArrowUp' ){
|
||||
$chatline.value = $chatline.lastValue || ''
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -76,11 +88,15 @@ chatComponent = {
|
|||
}
|
||||
})
|
||||
|
||||
this.$chatsend.addEventListener('click', (e) => {
|
||||
this.sendInput($chatline.value)
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
inform(){
|
||||
if( !this.inform.informed && (this.inform.informed = true) ){
|
||||
window.notify("Connected via P2P. You can now type message which will be visible to others.")
|
||||
window.notify("You can now type messages in the textfield below.")
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -263,6 +279,7 @@ chatComponent.css = `
|
|||
max-width: 500px;
|
||||
*/
|
||||
width:100%;
|
||||
box-sizing:border-box;
|
||||
align-items: flex-start;
|
||||
position: absolute;
|
||||
transition:1s;
|
||||
|
@ -271,7 +288,7 @@ chatComponent.css = `
|
|||
bottom: 49px;
|
||||
padding: 20px;
|
||||
overflow:hidden;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
pointer-events:none;
|
||||
transition:1s;
|
||||
z-index: 100;
|
||||
|
@ -283,11 +300,14 @@ chatComponent.css = `
|
|||
pointer-events:all;
|
||||
}
|
||||
#messages *{
|
||||
box-sizing:border-box;
|
||||
/*
|
||||
pointer-events:none;
|
||||
-webkit-user-select:none;
|
||||
-moz-user-select:-moz-none;
|
||||
-ms-user-select:none;
|
||||
user-select:none;
|
||||
*/
|
||||
}
|
||||
#messages .msg{
|
||||
transition:all 1s ease;
|
||||
|
@ -320,9 +340,9 @@ chatComponent.css = `
|
|||
color:#FFF;
|
||||
}
|
||||
#messages .msg.info{
|
||||
background: #473f7f;
|
||||
background: var(--xrf-white);
|
||||
border-radius: 20px;
|
||||
color: #FFF;
|
||||
color: var(--xrf-dark-gray);
|
||||
text-align: left;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
@ -411,7 +431,8 @@ chatComponent.css = `
|
|||
|
||||
.envelope{
|
||||
margin-right:15px;
|
||||
max-width:80%;
|
||||
width:50%;
|
||||
max-width:700px;
|
||||
}
|
||||
|
||||
.envelope,
|
||||
|
|
|
@ -4,14 +4,15 @@ window.accessibility = (opts) => new Proxy({
|
|||
enabled: false,
|
||||
|
||||
// features
|
||||
speak_movements: true,
|
||||
speak_keyboard: true,
|
||||
speak_teleports: true,
|
||||
speak_keyboard: false,
|
||||
|
||||
// audio settings
|
||||
speak_rate: 1,
|
||||
speak_pitch: 1,
|
||||
speak_volume: 1,
|
||||
speak_voice: -1,
|
||||
speak_voices: 0,
|
||||
|
||||
toggle(){ this.enabled = !this.enabled },
|
||||
|
||||
|
@ -33,8 +34,10 @@ window.accessibility = (opts) => new Proxy({
|
|||
}
|
||||
let speech = window.speechSynthesis
|
||||
let utterance = new SpeechSynthesisUtterance( str )
|
||||
if( this.speak_voice != -1) utterance.voice = speech.getVoices()[ this.speak_voice ];
|
||||
else{
|
||||
this.speak_voices = speech.getVoices().length
|
||||
if( this.speak_voice != -1 && this.speak_voice < this.speak_voices ){
|
||||
utterance.voice = speech.getVoices()[ this.speak_voice ];
|
||||
}else{
|
||||
let voices = speech.getVoices()
|
||||
for(let i = 0; i < voices.length; i++ ){
|
||||
if( voices[i].lang == navigator.lang ) this.speak_voice = i;
|
||||
|
@ -78,6 +81,9 @@ window.accessibility = (opts) => new Proxy({
|
|||
this.speak( lines.join("."), {override:true,speaksigns:false} )
|
||||
}
|
||||
})
|
||||
document.addEventListener('$chat.send', (opts) => {
|
||||
if( opts.detail.message ) this.speak( opts.detail.message)
|
||||
})
|
||||
})
|
||||
|
||||
document.addEventListener('network.send', (e) => {
|
||||
|
@ -87,8 +93,7 @@ window.accessibility = (opts) => new Proxy({
|
|||
})
|
||||
|
||||
opts.xrf.addEventListener('pos', (opts) => {
|
||||
if( this.enabled ){
|
||||
$chat.send({message: this.posToMessage(opts) })
|
||||
if( this.enabled && this.speak_teleports ){
|
||||
network.send({message: this.posToMessage(opts), class:["info","guide"]})
|
||||
}
|
||||
if( opts.frag.pos.string.match(/,/) ){
|
||||
|
@ -100,7 +105,7 @@ window.accessibility = (opts) => new Proxy({
|
|||
|
||||
setTimeout( () => this.initCommands(), 200 )
|
||||
// auto-enable if previously enabled
|
||||
if( window.localStorage.getItem("accessibility") ){
|
||||
if( window.localStorage.getItem("accessibility") === 'true' ){
|
||||
setTimeout( () => {
|
||||
this.enabled = true
|
||||
this.setFontSize()
|
||||
|
@ -111,7 +116,7 @@ window.accessibility = (opts) => new Proxy({
|
|||
initCommands(){
|
||||
|
||||
document.addEventListener('chat.command.help', (e) => {
|
||||
e.detail.message += `<br><b class="badge">/fontsize <number></b> set fontsize (default=14) `
|
||||
e.detail.message += `<br><b class="badge">/fontsize <number></b> set fontsize (default=14) `
|
||||
})
|
||||
|
||||
document.addEventListener('chat.command', (e) => {
|
||||
|
@ -179,10 +184,11 @@ window.accessibility = (opts) => new Proxy({
|
|||
data[k] = v
|
||||
switch( k ){
|
||||
case "enabled": {
|
||||
let message = "accessibility has been"+(v?"boosted":"lowered")
|
||||
let message = "accessibility mode has been "+(v?"activated":"disabled")+".<br>Type /help for help."
|
||||
if( v ) message = "<img src='https://i.imgur.com/wedtUSs.png' style='width:100%;border-radius:6px'/><br>" + message
|
||||
$('#accessibility.btn').style.filter= v ? 'brightness(1.0)' : 'brightness(0.5)'
|
||||
if( v ) $chat.visible = true
|
||||
$chat.send({message,class:['info','guide']})
|
||||
$chat.send({message,class:['info']})
|
||||
data.enabled = true
|
||||
data.speak(message)
|
||||
data.enabled = v
|
||||
|
@ -212,6 +218,10 @@ document.querySelector('head').innerHTML += `
|
|||
font-size:24px !important;
|
||||
line-height:40px;
|
||||
}
|
||||
.accessibility #messages .msg.self {
|
||||
background:var(--xrf-gray);
|
||||
color:#FFF;
|
||||
}
|
||||
.accessibility #messages .msg.info,
|
||||
.accessibility #messages .msg.self {
|
||||
line-height:unset;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// this allows surfing to a href by typing its node-name
|
||||
|
||||
// help screen
|
||||
document.addEventListener('chat.command.help', (e) => {
|
||||
e.detail.message += `
|
||||
<br><b class="badge"><destinationname></b> surf to a destination
|
||||
`
|
||||
})
|
||||
|
||||
document.addEventListener('chat.input', (e) => {
|
||||
|
||||
let name = e.detail.message.trim()
|
||||
xrf.scene.traverse( (n) => {
|
||||
if( n.userData && n.userData.href && n.userData.href.match(/pos=/) && n.name == name ){
|
||||
$chat.send({message:'<b class="badge">activating</b> '+n.name, class:['self','info']})
|
||||
xrf.navigator.to( n.userData.href )
|
||||
}
|
||||
})
|
||||
|
||||
})
|
|
@ -0,0 +1,144 @@
|
|||
// this allows a more-or-less MUD type interface
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
// help screen
|
||||
document.addEventListener('chat.command.help', (e) => {
|
||||
e.detail.message += `
|
||||
<br><b class="badge">?</b> help screen
|
||||
<br><b class="badge">look</b> view scene and destinations
|
||||
<br><b class="badge">go [left|right|forward|destination]</b> surf [to destination]
|
||||
<br><b class="badge">do [action]</b> list [or perform] action(s)
|
||||
<br><b class="badge">rotate <left|right|up|down></b> rotate camera
|
||||
<br><b class="badge">back</b> go to previous portal/link
|
||||
<br><b class="badge">forward</b> go to previous portal/link
|
||||
<br><b class="badge">#....</b> execute XR Fragments
|
||||
<hr/>
|
||||
`
|
||||
})
|
||||
|
||||
const listExits = (scene) => {
|
||||
let message = ''
|
||||
let destinations = {}
|
||||
scene.traverse( (n) => {
|
||||
if( n.userData && n.userData.href && n.userData.href.match(/pos=/) ){
|
||||
destinations[n.name] = n.userData['aria-label'] || n.userData.href
|
||||
}
|
||||
})
|
||||
for( let destination in destinations ){
|
||||
message += `<br><b class="badge">${destination}</b> ${destinations[destination]}`
|
||||
}
|
||||
if( !message ) message += '<br>type <b class="badge">back</b> to go back'
|
||||
return message
|
||||
}
|
||||
|
||||
const listActions = (scene) => {
|
||||
let message = ''
|
||||
let destinations = {}
|
||||
scene.traverse( (n) => {
|
||||
if( n.userData && n.userData.href && !n.userData.href.match(/pos=/) ){
|
||||
destinations[n.name] = n.userData['aria-description'] || n.userData['aria-label'] || n.userData.href
|
||||
}
|
||||
})
|
||||
for( let destination in destinations ){
|
||||
message += `<br><b class="badge">${destination}</b> ${destinations[destination]}`
|
||||
}
|
||||
if( !message ) message += '<br>no actions found'
|
||||
return message
|
||||
}
|
||||
|
||||
document.addEventListener('chat.input', (e) => {
|
||||
|
||||
if( e.detail.message.trim() == '?' ){
|
||||
document.dispatchEvent( new CustomEvent( 'chat.command', {detail:{message:"help"}} ) )
|
||||
e.detail.halt = true // don't send to other peers
|
||||
}
|
||||
|
||||
if( e.detail.message.trim() == 'look' ){
|
||||
let scene = xrf.frag.pos.last ? xrf.scene.getObjectByName(xrf.frag.pos.last) : xrf.scene
|
||||
let message = `<div class="transcript">${xrf.sceneToTranscript(scene)}</div><br>possible destinations in this area:${listExits(scene)}`
|
||||
e.detail.halt = true // dont print command to screen
|
||||
$chat.send({message})
|
||||
}
|
||||
|
||||
if( e.detail.message.match(/^go($| )/) ){
|
||||
if( e.detail.message.trim() == 'go' ){
|
||||
$chat.send({message: `all possible destinations:${listExits(xrf.scene)}`})
|
||||
}else{
|
||||
let destination = e.detail.message.replace(/^go /,'').trim()
|
||||
if( destination.match(/(left|right|forward|backward)/) ){
|
||||
let key = ''
|
||||
switch( destination){
|
||||
case "left": key = 'ArrowLeft'; break;
|
||||
case "right": key = 'ArrowRight'; break;
|
||||
case "forward": key = 'ArrowUp'; break;
|
||||
case "backward": key = 'ArrowDown'; break;
|
||||
}
|
||||
if( key ){
|
||||
let lookcontrols = document.querySelector('[look-controls]')
|
||||
if( lookcontrols ) lookcontrols.removeAttribute('look-controls') // workaround to unlock camera
|
||||
|
||||
var wasd = document.querySelector('[wasd-controls]').components['wasd-controls']
|
||||
wasd.keys[ key ] = true
|
||||
wasd.velocity = new THREE.Vector3()
|
||||
setTimeout( () => delete wasd.keys[ key ], 100 )
|
||||
wasd.el.object3D.matrixAutoUpdate = true;
|
||||
wasd.el.object3D.updateMatrix()
|
||||
xrf.camera.getCam().updateMatrix()
|
||||
}
|
||||
|
||||
}else{
|
||||
let node
|
||||
xrf.scene.traverse( (n) => {
|
||||
if( n.userData && n.userData.href && n.name == destination ) node = n
|
||||
})
|
||||
if( node ) xrf.navigator.to( node.userData.href )
|
||||
else $chat.send({message:"type 'look' for possible destinations"})
|
||||
}
|
||||
}
|
||||
e.detail.halt = true // dont write input to chat
|
||||
}
|
||||
|
||||
if( e.detail.message.match(/^do($| )/) ){
|
||||
if( e.detail.message.trim() == 'do' ){
|
||||
$chat.send({message: `all possible actions:${listActions(xrf.scene)}`})
|
||||
}else{
|
||||
let action = e.detail.message.replace(/^do /,'').trim()
|
||||
xrf.scene.traverse( (n) => {
|
||||
if( n.userData && n.userData.href && n.name == action ){
|
||||
$chat.send({message:'<b class="badge">activating</b> '+n.name, class:['self','info']})
|
||||
xrf.navigator.to( n.userData.href )
|
||||
}
|
||||
})
|
||||
}
|
||||
e.detail.halt = true // dont write input to chat
|
||||
}
|
||||
|
||||
if( e.detail.message.match(/^rotate /) ){
|
||||
let dir = e.detail.message.replace(/^rotate /,'').trim()
|
||||
let y = 0;
|
||||
let x = 0;
|
||||
switch(dir){
|
||||
case "left": y = 0.3; break;
|
||||
case "right": y = -0.3; break;
|
||||
case "up": x = 0.3; break;
|
||||
case "down": x = -0.3; break;
|
||||
}
|
||||
let lookcontrols = document.querySelector('[look-controls]')
|
||||
if( lookcontrols ) lookcontrols.removeAttribute('look-controls') // workaround to unlock camera
|
||||
xrf.camera.rotation.y += y
|
||||
xrf.camera.rotation.x += x
|
||||
xrf.camera.matrixAutoUpdate = true
|
||||
e.detail.halt = true // dont write input to chat
|
||||
}
|
||||
|
||||
if( e.detail.message.trim() == 'back' ){
|
||||
window.history.back()
|
||||
}
|
||||
|
||||
if( e.detail.message.trim() == 'forward' ){
|
||||
window.history.forward()
|
||||
}
|
||||
|
||||
})
|
|
@ -0,0 +1,26 @@
|
|||
// this allows surfing to a href by typing its node-name
|
||||
|
||||
// help screen
|
||||
document.addEventListener('chat.command.help', (e) => {
|
||||
e.detail.message += `
|
||||
<br><b class="badge">/speak_keyboard <true|false></b> turn on/off keyboard input TTS
|
||||
<br><b class="badge">/speak_teleports <true|false></b> turn on/off TTS for teleports
|
||||
<br><b class="badge">/speak_rate <1></b> adjust TTS speed
|
||||
<br><b class="badge">/speak_pitch <1></b> adjust TTS pitch
|
||||
<br><b class="badge">/speak_volume <1></b> adjust TTS volume
|
||||
<br><b class="badge">/speak_voice <0></b> select voice (max: ${window.accessibility.speak_voices})
|
||||
`
|
||||
})
|
||||
|
||||
document.addEventListener('chat.command', (e) => {
|
||||
if( !e.detail.message.trim().match(/ /) ) return
|
||||
let action = e.detail.message.trim().split(" ")[0]
|
||||
let value = e.detail.message.trim().split(" ")[1]
|
||||
|
||||
if( window.accessibility[action] == undefined ) return
|
||||
|
||||
window.accessibility[action] = value
|
||||
window.localStorage.setItem(action, value )
|
||||
$chat.send({message: `${action} set to ${value}`, class:['info']})
|
||||
|
||||
})
|
|
@ -306,8 +306,8 @@ document.head.innerHTML += `
|
|||
}
|
||||
#messages .msg .badge{
|
||||
display:inline;
|
||||
background: var(--xrf-primary-fg);
|
||||
color: var(--xrf-dark-gray);
|
||||
color: var(--xrf-primary-fg);
|
||||
background: var(--xrf-dark-gray);
|
||||
}
|
||||
|
||||
.ruler{
|
||||
|
@ -369,7 +369,7 @@ document.head.innerHTML += `
|
|||
}
|
||||
|
||||
.transcript{
|
||||
max-height:105px;
|
||||
max-height:132px;
|
||||
width:100%;
|
||||
overflow-y:auto;
|
||||
border: 1px solid var(--xrf-gray);
|
||||
|
|
|
@ -94,9 +94,9 @@ window.frontend = (opts) => new Proxy({
|
|||
? "hold 2-3 fingers to move forward/backward"
|
||||
: "use WASD-keys and mouse-drag to move around"
|
||||
window.notify(instructions,{timeout:false})
|
||||
xrf.addEventListener('navigate', (opts) => {
|
||||
let pos = opts.url.replace( document.location.href.replace(/#.*/,''), '')
|
||||
window.notify('<b class="badge">teleporting</b> to <b>'+pos+"</b><br><br>use back/forward browserbutton to undo")
|
||||
xrf.addEventListener('pos', (opts) => {
|
||||
let pos = opts.frag.pos.string
|
||||
window.notify('<b class="badge">teleporting</b> to <b>'+pos+"</b><br>use back/forward (browserbutton) to undo")
|
||||
}) // close dialogs when url changes
|
||||
},2000 )
|
||||
|
||||
|
@ -119,13 +119,8 @@ window.frontend = (opts) => new Proxy({
|
|||
}
|
||||
}
|
||||
}
|
||||
let transcript = ''
|
||||
let root = data.mesh.portal ? data.mesh.portal.stencilObject : data.mesh
|
||||
root.traverse( (n) => {
|
||||
if( n.userData['aria-description'] && n.uuid != data.mesh.uuid ){
|
||||
transcript += `<b>#${n.name}</b> ${n.userData['aria-description']}. `
|
||||
}
|
||||
})
|
||||
let transcript = xrf.sceneToTranscript(root,data.mesh)
|
||||
if( transcript.length ) html += `<br><b>transcript:</b><br><div class="transcript">${transcript}</div>`
|
||||
|
||||
if (hasMeta && !data.mesh.portal ) html += `<br><br><a class="btn" style="float:right" onclick="xrf.navigator.to('${data.mesh.userData.href}')">Visit embedded scene</a>`
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
xrf.sceneToTranscript = (scene, ignoreMesh ) => {
|
||||
let transcript = ''
|
||||
scene.traverse( (n) => {
|
||||
let isSRC = false
|
||||
n.traverseAncestors( (m) => m.userData.src ? isSRC = true : false )
|
||||
if( !isSRC && n.userData['aria-description'] && (!ignoreMesh || n.uuid != ignoreMesh.uuid) ){
|
||||
transcript += `<b>#${n.name}</b> ${n.userData['aria-description']}. `
|
||||
}
|
||||
})
|
||||
return transcript
|
||||
}
|
|
@ -19,7 +19,7 @@ xrf.frag.pos = function(v, opts){
|
|||
|
||||
if( xrf.debug ) console.log(`#pos.js: setting camera to position ${pos.x},${pos.y},${pos.z}`)
|
||||
|
||||
xrf.frag.pos.last = pos // remember
|
||||
xrf.frag.pos.last = v.string // remember
|
||||
|
||||
camera.updateMatrixWorld()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue