work in progress [might break]

This commit is contained in:
Leon van Kammen 2024-03-12 11:32:59 +01:00
parent 7e0d7ca7c1
commit a456cd8264
9 changed files with 232 additions and 13 deletions

View File

@ -18,13 +18,13 @@
light="defaultLightsEnabled: false"> light="defaultLightsEnabled: false">
<a-entity id="player" movement-controls touch-controls wasd-controls="fly:false" look-controls> <a-entity id="player" movement-controls touch-controls wasd-controls="fly:false" look-controls>
<a-entity camera="fov:90" position="0 1.6 0" id="camera"></a-entity> <a-entity camera="fov:90" position="0 1.6 0" id="camera"></a-entity>
<a-entity id="left-hand" hand-tracking-controls="hand:left" laser-controls="hand: left" raycaster="objects:.ray" blink-controls="cameraRig:#player; teleportOrigin: #camera; collisionEntities: .floor"> <a-entity id="left-hand" hand-tracking-controls="hand:left;modelColor:#cccccc" laser-controls="hand: left" raycaster="objects:.ray" blink-controls="cameraRig:#player; teleportOrigin: #camera; collisionEntities: .floor">
<a-entity rotation="-35 0 0" position="0 0.1 0" id="navigator"> <a-entity rotation="-35 0 0" position="0 0.1 0" id="navigator">
<a-entity id="back" xrf-button="label: <; width:0.05; action: history.back()" position="-0.025 0 0" class="ray"></a-entity> <a-entity id="back" xrf-button="label: <; width:0.05; action: history.back()" position="-0.025 0 0" class="ray"></a-entity>
<a-entity id="next" xrf-button="label: >; width:0.05; action: history.forward()" position=" 0.025 0 0" class="ray"></a-entity> <a-entity id="next" xrf-button="label: >; width:0.05; action: history.forward()" position=" 0.025 0 0" class="ray"></a-entity>
</a-entity> </a-entity>
</a-entity> </a-entity>
<a-entity id="right-hand" hand-tracking-controls="hand:right" laser-controls="hand: right" raycaster="objects:.ray" blink-controls="cameraRig:#player; teleportOrigin: #camera; collisionEntities: .floor" xrf-pinchmove="rig: #player"></a-entity> <a-entity id="right-hand" hand-tracking-controls="hand:right;modelColor:#cccccc" laser-controls="hand: right" raycaster="objects:.ray" blink-controls="cameraRig:#player; teleportOrigin: #camera; collisionEntities: .floor" xrf-pinchmove="rig: #player"></a-entity>
</a-entity> </a-entity>
<a-entity id="home" xrf="index.glb" xrf-menu></a-entity> <a-entity id="home" xrf="index.glb" xrf-menu></a-entity>

View File

@ -10,7 +10,7 @@ import { ColladaLoader } from 'super-three/examples/jsm/loaders/ColladaLoader';
import { MTLLoader } from 'super-three/examples/jsm/loaders/MTLLoader'; import { MTLLoader } from 'super-three/examples/jsm/loaders/MTLLoader';
import * as BufferGeometryUtils from 'super-three/examples/jsm/utils/BufferGeometryUtils'; import * as BufferGeometryUtils from 'super-three/examples/jsm/utils/BufferGeometryUtils';
import { LightProbeGenerator } from 'super-three/examples/jsm/lights/LightProbeGenerator'; import { LightProbeGenerator } from 'super-three/examples/jsm/lights/LightProbeGenerator';
//import {Text} from 'troika-three-text' import { TransformControls } from 'super-three/examples/jsm/controls/TransformControls.js';
var THREE = window.THREE = SUPER_THREE; var THREE = window.THREE = SUPER_THREE;
@ -27,6 +27,7 @@ THREE.ColladaLoader = ColladaLoader;
THREE.OBB = OBB; THREE.OBB = OBB;
THREE.BufferGeometryUtils = BufferGeometryUtils; THREE.BufferGeometryUtils = BufferGeometryUtils;
THREE.LightProbeGenerator = LightProbeGenerator; THREE.LightProbeGenerator = LightProbeGenerator;
THREE.TransformControls = TransformControls;
//THREE.Text = Text //THREE.Text = Text
export default THREE; export default THREE;

View File

@ -100,7 +100,7 @@ window.AFRAME.registerComponent('xrf', {
} }
// give headset users way to debug without a cumbersome usb-tapdance // give headset users way to debug without a cumbersome usb-tapdance
if( xrf.debug || document.location.hostname.match(/^(localhost|[1-9])/) && !aScene.getAttribute("vconsole") ){ if( document.location.hostname.match(/^(localhost|[1-9])/) && !aScene.getAttribute("vconsole") ){
aScene.setAttribute('vconsole','') aScene.setAttribute('vconsole','')
} }
@ -146,7 +146,6 @@ window.AFRAME.registerComponent('xrf', {
el.addEventListener("click", clickHandler ) el.addEventListener("click", clickHandler )
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) ) el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) ) el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el) $('a-scene').appendChild(el)
} }
createEl(mesh) createEl(mesh)

View File

@ -45,7 +45,7 @@ AFRAME.registerComponent('pressable', {
handEl.object3D.getWorldPosition( this.fingerWorldPosition ) handEl.object3D.getWorldPosition( this.fingerWorldPosition )
this.fingerWorldPosition.add( indexTipPosition ) this.fingerWorldPosition.add( indexTipPosition )
this.raycaster.far = 0.05 this.raycaster.far = this.data.pressDistance
// Create a direction vector (doesnt matter because it is supershort for 'touch' purposes) // Create a direction vector (doesnt matter because it is supershort for 'touch' purposes)
const direction = new THREE.Vector3(1.0,0,0); const direction = new THREE.Vector3(1.0,0,0);
this.raycaster.set(this.fingerWorldPosition, direction) this.raycaster.set(this.fingerWorldPosition, direction)
@ -59,6 +59,7 @@ AFRAME.registerComponent('pressable', {
if (intersects.length ){ if (intersects.length ){
if( !this.pressed ){ if( !this.pressed ){
this.el.emit('pressedstarted'); this.el.emit('pressedstarted');
this.el.emit('click');
this.pressed = setTimeout( () => { this.pressed = setTimeout( () => {
this.el.emit('pressedended'); this.el.emit('pressedended');
this.pressed = null this.pressed = null

View File

@ -0,0 +1,126 @@
// reactive component for displaying the menu
$editor = (el,opts) => new Proxy({
html: `
<div style="position:absolute; width:100%; text-align:right; right:166px;">
<button class="btn edit-btn">
<i class="gg-pen"></i>
</button>
</div>
<style type="text/css">
.xrf button.edit-btn{
height: 32px;
width: 30px;
margin-top: 7px;
}
.edit-btn i.gg-pen{
margin-top: -26px;
margin-left: 4px;
width: 10px;
color: var(--xrf-white);
}
</style>
`,
enabled: false,
helper: null,
selected: null,
init(opts){
el.innerHTML = this.html
window.frontend.el.querySelector('#topbar').appendChild(el);
el.querySelector('.edit-btn').addEventListener('click', () => $editor.enabled = true )
return this
},
editNode(){
if( !this.enabled ) return console.log("not editing")
console.log("click!")
$editor.enabled = false // disable selections
this.enableHref(this.selected,true) // re-enable hrefs
notify(`${this.selected.name}<br>${this.getMetaData(this.selected)}`)
notify(`<b>XR Fragment:</b> #${this.selected.name}<br><br>${this.getMetaData(this.selected)}`)
},
initEdit(scene){
AFRAME.scenes[0].addEventListener('click', () => this.editNode() )
scene.traverse( (n) => {
let highlight = (n) => (e) => {
console.log(n.name)
if( this.selected ) this.enableHref(this.selected,true) // re-enable href of previous selection
if( this.helper){
if( this.helper.selected == n.uuid ) return // already selected
xrf.scene.remove(this.helper)
}
if( !this.enabled ) return // do nothing
this.selected = n
this.helper = new THREE.BoxHelper( n, 0xFF00FF )
this.helper.material.linewidth = 5
this.helper.material.color = xrf.focusLine.material.color
this.helper.selected = n.uuid
xrf.scene.add(this.helper)
let div = document.createElement('div')
notify(`<b>XR Fragment:</b> #${n.name}<br><br>${this.getMetaData(this.selected)}`)
this.enableHref(n,false) // prevent clicks from doing their usual teleporting/executions
}
if( n.geometry ) n.addEventListener('mousemove', n.highlightOnMouseMove = highlight(n) )
})
console.log("inited scene")
},
getMetaData(n){
return `href: ${n.userData.href}<br>src: ${n.userData.src}<br>tag: ${n.userData.tag}`
},
enableHref(n, state){
if( n.userData.XRF && n.userData.XRF.href && n.userData.XRF.href.exec ){
let exec = n.userData.XRF.href.exec
if( !state && !exec.bak ){
exec.bak = exec
n.userData.XRF.href.exec = function(){}
}
if( state && exec.bak ){
n.userData.XRF.href.exec = exec.bak
}
}
}
},
{
get(me,k,v){ return me[k] },
set(me,k,v){
me[k] = v
switch( k ){
case "enabled":{
if( v ){
notify("click an object to reveal XR Fragment metadata")
xrf.interactive.raycastAll = true
if( !xrf.scene.initEdit ) me.initEdit(xrf.scene)
}else{
console.log("idsabled")
xrf.scene.traverse( (n) => {
me.enableHref(n,true)
if( n.highlightOnMouseMove ){
n.removeEventListener( 'mousemove', n.highlightOnMouseMove )
}
})
me.helper.remove()
console.log("removed events")
}
break;
}
}
},
})
// reactify component!
document.addEventListener('frontend:ready', (e) => {
window.$editor = $editor( document.createElement('div') ).init(e.detail)
})

View File

@ -0,0 +1,40 @@
window.$editor = (opts) => new Proxy({
opts,
html: `
<div class="xrf footer">
<div class="menu">
<div id="buttons"></div>
<a class="btn" id="more" aria-title="menu button"><i id="icon" class="gg-menu"></i></a><br>
</div>
</div>
`,
enabled: false,
toggle(){ this.enabled = !this.enabled },
init(){
el.innerHTML = this.html
document.body.appendChild(el);
},
},
{
// auto-trigger events on changes
get(data,k,receiver){ return data[k] },
set(data,k,v){
data[k] = v
switch( k ){
case "enabled": {
data.enabled = v
}
}
})
document.addEventListener('$menu:ready', (e) => {
return
try{
$editor = $editor( document.createElement('div'), {} )
$editor.init()
}catch(e){console.error(e)}
})

View File

@ -781,5 +781,46 @@ document.head.innerHTML += `
bottom: 8px; bottom: 8px;
background: currentColor background: currentColor
} }
.gg-pen {
box-sizing: border-box;
position: relative;
display: block;
transform: rotate(-45deg) scale(var(--ggs,1));
width: 14px;
height: 4px;
border-right: 2px solid transparent;
box-shadow:
0 0 0 2px,
inset -2px 0 0;
border-top-right-radius: 1px;
border-bottom-right-radius: 1px;
margin-right: -2px
}
.gg-pen::after,
.gg-pen::before {
content: "";
display: block;
box-sizing: border-box;
position: absolute
}
.gg-pen::before {
background: currentColor;
border-left: 0;
right: -6px;
width: 3px;
height: 4px;
border-radius: 1px;
top: 0
}
.gg-pen::after {
width: 8px;
height: 7px;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-right: 7px solid;
left: -11px;
top: -2px
}
</style> </style>
` `

View File

@ -178,9 +178,14 @@ window.frontend = (opts) => new Proxy({
let isChatMsg = e.target.closest('.msg') let isChatMsg = e.target.closest('.msg')
let isChatLine = e.target.id == 'chatline' let isChatLine = e.target.id == 'chatline'
let isChatEmptySpace = e.target.id == 'messages' let isChatEmptySpace = e.target.id == 'messages'
let isUI = e.target.closest('.ui') let isUI = e.target.closest('.ui') ||
e.target.closest('.btn') ||
e.target.closest('button') ||
e.target.closest('textarea') ||
e.target.closest('input') ||
e.target.closest('a')
//console.dir({class: e.target.className, id: e.target.id, isChatMsg,isChatLine,isChatEmptySpace,isUI, tagName: e.target.tagName}) //console.dir({class: e.target.className, id: e.target.id, isChatMsg,isChatLine,isChatEmptySpace,isUI, tagName: e.target.tagName})
if( isUI || e.target.tagName.match(/^(BUTTON|TEXTAREA|INPUT|A)/) || e.target.className.match(/(btn)/) ) return if( isUI ) return
if( show ){ if( show ){
$chat.visible = true $chat.visible = true
}else{ }else{

View File

@ -1,4 +1,4 @@
// wrapper to survive in/outside modules // wrapper to collect interactive raycastable objects
xrf.interactiveGroup = function(THREE,renderer,camera){ xrf.interactiveGroup = function(THREE,renderer,camera){
@ -26,6 +26,8 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const scope = this; const scope = this;
scope.objects = [] scope.objects = []
scope.raycastAll = false
const raycaster = new Raycaster(); const raycaster = new Raycaster();
const tempMatrix = new Matrix4(); const tempMatrix = new Matrix4();
@ -44,7 +46,8 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
raycaster.setFromCamera( _pointer, camera ); raycaster.setFromCamera( _pointer, camera );
const intersects = raycaster.intersectObjects( scope.objects, false ); let objects = scope.raycastAll ? xrf.scene.children : scope.objects
const intersects = raycaster.intersectObjects( objects, scope.raycastAll );
if ( intersects.length > 0 ) { if ( intersects.length > 0 ) {
@ -54,7 +57,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const uv = intersection.uv; const uv = intersection.uv;
_event.type = event.type; _event.type = event.type;
_event.data.set( uv.x, 1 - uv.y ); if( uv ) _event.data.set( uv.x, 1 - uv.y );
object.dispatchEvent( _event ); object.dispatchEvent( _event );
}else{ }else{
@ -93,7 +96,8 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld ); raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix ); raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
const intersections = raycaster.intersectObjects( scope.objects, false ); let objects = scope.raycastAll ? xrf.scene.children : scope.objects
const intersections = raycaster.intersectObjects( objects, scope.raycastAll );
if ( intersections.length > 0 ) { if ( intersections.length > 0 ) {
@ -105,7 +109,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const uv = intersection.uv; const uv = intersection.uv;
_event.type = eventsMapper[ event.type ]; _event.type = eventsMapper[ event.type ];
_event.data.set( uv.x, 1 - uv.y ); if( uv ) _event.data.set( uv.x, 1 - uv.y );
object.dispatchEvent( _event ); object.dispatchEvent( _event );
@ -132,6 +136,8 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
} }
// we create our own add to avoid unnecessary unparenting of buffergeometries from
// their 3D model (which breaks animations)
add(obj, unparent){ add(obj, unparent){
if( unparent ) Group.prototype.add.call( this, obj ) if( unparent ) Group.prototype.add.call( this, obj )
this.objects.push(obj) this.objects.push(obj)