diff --git a/example/aframe/sandbox/index.html b/example/aframe/sandbox/index.html
index 9b7556a..b3fb796 100644
--- a/example/aframe/sandbox/index.html
+++ b/example/aframe/sandbox/index.html
@@ -18,13 +18,13 @@
light="defaultLightsEnabled: false">
-
+
-
+
diff --git a/src/3rd/js/aframe/build/three.module.js b/src/3rd/js/aframe/build/three.module.js
index d5683db..11c6835 100644
--- a/src/3rd/js/aframe/build/three.module.js
+++ b/src/3rd/js/aframe/build/three.module.js
@@ -10,7 +10,7 @@ import { ColladaLoader } from 'super-three/examples/jsm/loaders/ColladaLoader';
import { MTLLoader } from 'super-three/examples/jsm/loaders/MTLLoader';
import * as BufferGeometryUtils from 'super-three/examples/jsm/utils/BufferGeometryUtils';
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;
@@ -27,6 +27,7 @@ THREE.ColladaLoader = ColladaLoader;
THREE.OBB = OBB;
THREE.BufferGeometryUtils = BufferGeometryUtils;
THREE.LightProbeGenerator = LightProbeGenerator;
+THREE.TransformControls = TransformControls;
//THREE.Text = Text
export default THREE;
diff --git a/src/3rd/js/aframe/index.js b/src/3rd/js/aframe/index.js
index 1d3c232..bd91d91 100644
--- a/src/3rd/js/aframe/index.js
+++ b/src/3rd/js/aframe/index.js
@@ -100,7 +100,7 @@ window.AFRAME.registerComponent('xrf', {
}
// 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','')
}
@@ -146,7 +146,6 @@ window.AFRAME.registerComponent('xrf', {
el.addEventListener("click", clickHandler )
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
- el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el)
}
createEl(mesh)
diff --git a/src/3rd/js/aframe/pressable.js b/src/3rd/js/aframe/pressable.js
index aed7825..85d8675 100644
--- a/src/3rd/js/aframe/pressable.js
+++ b/src/3rd/js/aframe/pressable.js
@@ -45,7 +45,7 @@ AFRAME.registerComponent('pressable', {
handEl.object3D.getWorldPosition( this.fingerWorldPosition )
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)
const direction = new THREE.Vector3(1.0,0,0);
this.raycaster.set(this.fingerWorldPosition, direction)
@@ -59,6 +59,7 @@ AFRAME.registerComponent('pressable', {
if (intersects.length ){
if( !this.pressed ){
this.el.emit('pressedstarted');
+ this.el.emit('click');
this.pressed = setTimeout( () => {
this.el.emit('pressedended');
this.pressed = null
diff --git a/src/3rd/js/plugin/frontend/$editor.js b/src/3rd/js/plugin/frontend/$editor.js
new file mode 100644
index 0000000..a6d8c73
--- /dev/null
+++ b/src/3rd/js/plugin/frontend/$editor.js
@@ -0,0 +1,126 @@
+// reactive component for displaying the menu
+$editor = (el,opts) => new Proxy({
+
+ html: `
+
+
+
+
+ `,
+
+ 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}
${this.getMetaData(this.selected)}`)
+ notify(`XR Fragment: #${this.selected.name}
${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(`XR Fragment: #${n.name}
${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}
src: ${n.userData.src}
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)
+})
diff --git a/src/3rd/js/plugin/frontend/.js b/src/3rd/js/plugin/frontend/.js
new file mode 100644
index 0000000..144e055
--- /dev/null
+++ b/src/3rd/js/plugin/frontend/.js
@@ -0,0 +1,40 @@
+window.$editor = (opts) => new Proxy({
+ opts,
+ html: `
+
+ `,
+
+ 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)}
+})
diff --git a/src/3rd/js/plugin/frontend/css.js b/src/3rd/js/plugin/frontend/css.js
index 4d66c37..4f803ba 100644
--- a/src/3rd/js/plugin/frontend/css.js
+++ b/src/3rd/js/plugin/frontend/css.js
@@ -781,5 +781,46 @@ document.head.innerHTML += `
bottom: 8px;
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
+ }
`
diff --git a/src/3rd/js/plugin/frontend/frontend.js b/src/3rd/js/plugin/frontend/frontend.js
index 0e71799..9300013 100644
--- a/src/3rd/js/plugin/frontend/frontend.js
+++ b/src/3rd/js/plugin/frontend/frontend.js
@@ -178,9 +178,14 @@ window.frontend = (opts) => new Proxy({
let isChatMsg = e.target.closest('.msg')
let isChatLine = e.target.id == 'chatline'
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})
- if( isUI || e.target.tagName.match(/^(BUTTON|TEXTAREA|INPUT|A)/) || e.target.className.match(/(btn)/) ) return
+ if( isUI ) return
if( show ){
$chat.visible = true
}else{
diff --git a/src/3rd/js/three/util/interactive.js b/src/3rd/js/three/util/interactive.js
index d1dc3d0..80299f2 100644
--- a/src/3rd/js/three/util/interactive.js
+++ b/src/3rd/js/three/util/interactive.js
@@ -1,4 +1,4 @@
-// wrapper to survive in/outside modules
+// wrapper to collect interactive raycastable objects
xrf.interactiveGroup = function(THREE,renderer,camera){
@@ -26,6 +26,8 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const scope = this;
scope.objects = []
+ scope.raycastAll = false
+
const raycaster = new Raycaster();
const tempMatrix = new Matrix4();
@@ -44,7 +46,8 @@ xrf.interactiveGroup = function(THREE,renderer,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 ) {
@@ -54,7 +57,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const uv = intersection.uv;
_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 );
}else{
@@ -93,7 +96,8 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
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 ) {
@@ -105,7 +109,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const uv = intersection.uv;
_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 );
@@ -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){
if( unparent ) Group.prototype.add.call( this, obj )
this.objects.push(obj)