diff --git a/.vimrc b/.vimrc
index 4cddf77..a79e681 100644
--- a/.vimrc
+++ b/.vimrc
@@ -1,4 +1,4 @@
noremap :!haxe --no-output %
-noremap :!./make doc
+noremap :!./make build_js
noremap :!./make && echo OK && ./make tests
noremap :!./make tests \| less
diff --git a/dist/xrfragment.aframe.js b/dist/xrfragment.aframe.js
index 2b44c65..d071b60 100644
--- a/dist/xrfragment.aframe.js
+++ b/dist/xrfragment.aframe.js
@@ -593,114 +593,317 @@ xrfragment_XRF.isUrlOrPretypedView = new EReg("(^#|://)?\\..*","");
xrfragment_XRF.isString = new EReg(".*","");
})({});
var xrfragment = $hx_exports["xrfragment"];
-xrfragment.xrf = {}
-xrfragment.model = {}
+// wrapper to survive in/outside modules
-xrfragment.init = function(opts){
+xrfragment.InteractiveGroup = function(THREE,renderer,camera){
+
+ let {
+ Group,
+ Matrix4,
+ Raycaster,
+ Vector2
+ } = THREE
+
+ const _pointer = new Vector2();
+ const _event = { type: '', data: _pointer };
+
+ class InteractiveGroup extends Group {
+
+ constructor( renderer, camera ) {
+
+ super();
+
+ if( !renderer || !camera ) return
+
+ const scope = this;
+
+ const raycaster = new Raycaster();
+ const tempMatrix = new Matrix4();
+
+ // Pointer Events
+
+ const element = renderer.domElement;
+
+ function onPointerEvent( event ) {
+
+ //event.stopPropagation();
+
+ const rect = renderer.domElement.getBoundingClientRect();
+
+ _pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
+ _pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
+
+ raycaster.setFromCamera( _pointer, camera );
+
+ const intersects = raycaster.intersectObjects( scope.children, false );
+
+ if ( intersects.length > 0 ) {
+
+ const intersection = intersects[ 0 ];
+
+ const object = intersection.object;
+ const uv = intersection.uv;
+
+ _event.type = event.type;
+ _event.data.set( uv.x, 1 - uv.y );
+
+ object.dispatchEvent( _event );
+
+ }
+
+ }
+
+ element.addEventListener( 'pointerdown', onPointerEvent );
+ element.addEventListener( 'pointerup', onPointerEvent );
+ element.addEventListener( 'pointermove', onPointerEvent );
+ element.addEventListener( 'mousedown', onPointerEvent );
+ element.addEventListener( 'mouseup', onPointerEvent );
+ element.addEventListener( 'mousemove', onPointerEvent );
+ element.addEventListener( 'click', onPointerEvent );
+
+ // WebXR Controller Events
+ // TODO: Dispatch pointerevents too
+
+ const events = {
+ 'move': 'mousemove',
+ 'select': 'click',
+ 'selectstart': 'mousedown',
+ 'selectend': 'mouseup'
+ };
+
+ function onXRControllerEvent( event ) {
+
+ const controller = event.target;
+
+ tempMatrix.identity().extractRotation( controller.matrixWorld );
+
+ raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
+ raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
+
+ const intersections = raycaster.intersectObjects( scope.children, false );
+
+ if ( intersections.length > 0 ) {
+
+ const intersection = intersections[ 0 ];
+
+ const object = intersection.object;
+ const uv = intersection.uv;
+
+ _event.type = events[ event.type ];
+ _event.data.set( uv.x, 1 - uv.y );
+ if( _event.type != "mousemove" ){
+ console.log(event.type+" => "+_event.type)
+ }
+
+ object.dispatchEvent( _event );
+
+ }
+
+ }
+
+ const controller1 = renderer.xr.getController( 0 );
+ controller1.addEventListener( 'move', onXRControllerEvent );
+ controller1.addEventListener( 'select', onXRControllerEvent );
+ controller1.addEventListener( 'selectstart', onXRControllerEvent );
+ controller1.addEventListener( 'selectend', onXRControllerEvent );
+
+ const controller2 = renderer.xr.getController( 1 );
+ controller2.addEventListener( 'move', onXRControllerEvent );
+ controller2.addEventListener( 'select', onXRControllerEvent );
+ controller2.addEventListener( 'selectstart', onXRControllerEvent );
+ controller2.addEventListener( 'selectend', onXRControllerEvent );
+
+ }
+
+ }
+
+ return new InteractiveGroup(renderer,camera)
+}
+let xrf = xrfragment
+xrf.frag = {}
+xrf.model = {}
+
+xrf.init = function(opts){
opts = opts || {}
let XRF = function(){
alert("queries are not implemented (yet)")
}
- for ( let i in opts ) xrfragment[i] = opts[i]
- for ( let i in xrfragment.XRF ) xrfragment.XRF[i] // shortcuts to constants (NAVIGATOR e.g.)
- xrfragment.Parser.debug = xrfragment.debug
- if( opts.loaders ) opts.loaders.map( xrfragment.patchLoader )
- xrfragment.patchRenderer(opts.renderer)
- return xrfragment
+ for ( let i in opts ) xrf[i] = opts[i]
+ for ( let i in xrf.XRF ) xrf.XRF[i] // shortcuts to constants (NAVIGATOR e.g.)
+ xrf.Parser.debug = xrf.debug
+ if( opts.loaders ) Object.values(opts.loaders).map( xrf.patchLoader )
+ xrf.patchRenderer(opts.renderer)
+ xrf.navigate.init()
+ return xrf
}
-xrfragment.patchRenderer = function(renderer){
+xrf.patchRenderer = function(renderer){
+ renderer.xr.addEventListener( 'sessionstart', () => xrf.baseReferenceSpace = renderer.xr.getReferenceSpace() );
+ renderer.xr.enabled = true;
renderer.render = ((render) => function(scene,camera){
- if( xrfragment.getLastModel() && xrfragment.getLastModel().render )
- xrfragment.getLastModel().render(scene,camera)
+ if( xrf.model && xrf.model.render )
+ xrf.model.render(scene,camera)
render(scene,camera)
})(renderer.render.bind(renderer))
}
-xrfragment.patchLoader = function(loader){
+xrf.patchLoader = function(loader){
loader.prototype.load = ((load) => function(url, onLoad, onProgress, onError){
load.call( this,
url,
- (model) => { onLoad(model); xrfragment.parseModel(model,url) },
+ (model) => { onLoad(model); xrf.parseModel(model,url) },
onProgress,
onError)
})(loader.prototype.load)
}
-xrfragment.getFile = (url) => url.split("/").pop().replace(/#.*/,'')
+xrf.getFile = (url) => url.split("/").pop().replace(/#.*/,'')
-xrfragment.parseModel = function(model,url){
- let file = xrfragment.getFile(url)
+xrf.parseModel = function(model,url){
+ let file = xrf.getFile(url)
model.file = file
model.render = function(){}
- xrfragment.model[file] = model
+ model.interactive = xrf.InteractiveGroup( xrf.THREE, xrf.renderer, xrf.camera)
+ model.scene.add(model.interactive)
+
console.log("scanning "+file)
model.scene.traverse( (mesh) => {
- console.log("◎ "+mesh.name)
- if( mesh.userData ){
- let frag = {}
- for( let k in mesh.userData ) xrfragment.Parser.parse( k, mesh.userData[k], frag )
- for( let k in frag ){
- let opts = {frag, mesh, model, camera: xrfragment.camera, scene: xrfragment.scene, renderer: xrfragment.renderer, THREE: xrfragment.THREE }
- xrfragment.evalFragment(k,opts)
- }
- }
+ console.log("◎ "+ (mesh.name||`THREE.${mesh.constructor.name}`))
+ xrf.eval.mesh(mesh,model)
})
}
-xrfragment.evalFragment = (k, opts ) => {
- // call native function (xrf/env.js e.g.), or pass it to user decorator
- let func = xrfragment.xrf[k] || function(){}
- if( xrfragment[k] ) xrfragment[k]( func, opts.frag[k], opts)
- else func( opts.frag[k], opts)
-}
-
-xrfragment.getLastModel = () => Object.values(xrfragment.model)[ Object.values(xrfragment.model).length-1 ]
+xrf.getLastModel = () => xrf.model.last
-xrfragment.eval = function( url, model ){
+xrf.eval = function( url, model ){
let notice = false
- model = model || xrfragment.getLastModel()
- let { THREE, camera } = xrfragment
- let frag = xrfragment.URI.parse( url, xrfragment.XRF.NAVIGATOR )
+ model = model || xrf.model
+ let { THREE, camera } = xrf
+ let frag = xrf.URI.parse( url, xrf.XRF.NAVIGATOR )
let meshes = frag.q ? [] : [camera]
for ( let i in meshes ) {
for ( let k in frag ){
let mesh = meshes[i]
if( !String(k).match(/(pos|rot)/) ) notice = true
- let opts = {frag, mesh, model, camera: xrfragment.camera, scene: xrfragment.scene, renderer: xrfragment.renderer, THREE: xrfragment.THREE }
- xrfragment.evalFragment(k,opts)
+ let opts = {frag, mesh, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
+ xrf.eval.fragment(k,opts)
}
}
if( notice ) alert("only 'pos' and 'rot' XRF.NAVIGATOR-flagged XR fragments are supported (for now)")
}
-xrfragment.xrf.env = function(v, opts){
+
+xrf.eval.mesh = (mesh,model) => {
+ if( mesh.userData ){
+ let frag = {}
+ for( let k in mesh.userData ) xrf.Parser.parse( k, mesh.userData[k], frag )
+ for( let k in frag ){
+ let opts = {frag, mesh, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
+ xrf.eval.fragment(k,opts)
+ }
+ }
+}
+
+xrf.eval.fragment = (k, opts ) => {
+ // call native function (xrf/env.js e.g.), or pass it to user decorator
+ let func = xrf.frag[k] || function(){}
+ if( xrf[k] ) xrf[k]( func, opts.frag[k], opts)
+ else func( opts.frag[k], opts)
+}
+
+xrf.reset = () => {
+ if( !xrf.model.scene ) return
+ xrf.scene.remove( xrf.model.scene )
+ xrf.model.scene.traverse( function(node){
+ if( node instanceof THREE.Mesh ){
+ node.geometry.dispose()
+ node.material.dispose()
+ }
+ })
+}
+
+xrf.navigate = {}
+
+xrf.navigate.to = (url) => {
+ return new Promise( (resolve,reject) => {
+ console.log("xrfragment: navigating to "+url)
+ if( xrf.model && xrf.model.scene ) xrf.model.scene.visible = false
+ const urlObj = new URL( url.match(/:\/\//) ? url : String(`https://fake.com/${url}`).replace(/\/\//,'/') )
+ let dir = url.substring(0, url.lastIndexOf('/') + 1)
+ const file = urlObj.pathname.substring(urlObj.pathname.lastIndexOf('/') + 1);
+ const ext = file.split('.').pop()
+ const Loader = xrf.loaders[ext]
+ if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
+ // force relative path
+ if( dir ) dir = dir[0] == '.' ? dir : `.${dir}`
+ const loader = new Loader().setPath( dir )
+ loader.load( file, function(model){
+ xrf.scene.add( model.scene )
+ xrf.reset()
+ xrf.model = model
+ xrf.navigate.commit( file )
+ resolve(model)
+ })
+ })
+}
+
+xrf.navigate.init = () => {
+ if( xrf.navigate.init.inited ) return
+ window.addEventListener('popstate', function (event){
+ console.dir(event)
+ xrf.navigate.to( document.location.search.substr(1) + document.location.hash )
+ })
+ xrf.navigate.init.inited = true
+}
+
+xrf.navigate.commit = (file) => {
+ window.history.pushState({},null, document.location.pathname + `?${file}${document.location.hash}` )
+}
+xrf.frag.env = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
let env = mesh.getObjectByName(v.string)
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = env.material.map
- scene.texture = env.material.map
+ //scene.texture = env.material.map
renderer.toneMapping = THREE.ACESFilmicToneMapping;
- renderer.toneMappingExposure = 1;
+ renderer.toneMappingExposure = 2;
+ // apply to meshes *DISABLED* renderer.environment does this
+ const maxAnisotropy = renderer.capabilities.getMaxAnisotropy();
+ setTimeout( () => {
+ scene.traverse( (mesh) => {
+ //if (mesh.material && mesh.material.map && mesh.material.metalness == 1.0) {
+ // mesh.material = new THREE.MeshBasicMaterial({ map: mesh.material.map });
+ // mesh.material.dithering = true
+ // mesh.material.map.anisotropy = maxAnisotropy;
+ // mesh.material.needsUpdate = true;
+ //}
+ //if (mesh.material && mesh.material.metalness == 1.0 ){
+ // mesh.material = new THREE.MeshBasicMaterial({
+ // color:0xffffff,
+ // emissive: mesh.material.map,
+ // envMap: env.material.map,
+ // side: THREE.DoubleSide,
+ // flatShading: true
+ // })
+ // mesh.material.needsUpdate = true
+ // //mesh.material.envMap = env.material.map;
+ // //mesh.material.envMap.intensity = 5;
+ // //mesh.material.needsUpdate = true;
+ //}
+ });
+ },500)
console.log(` └ applied image '${v.string}' as environment map`)
}
-xrfragment.xrf.href = function(v, opts){
+xrf.frag.href = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
- let size = 5
let texture = mesh.material.map
+ texture.mapping = THREE.ClampToEdgeWrapping
+ texture.needsUpdate = true
+ mesh.material.dispose()
- /*
- texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
- mesh.material = new THREE.MeshStandardMaterial( {
- envMap: texture,
- roughness: 0.0,
- metalness: 1,
- side: THREE.DoubleSide,
- })
- */
-
+ // poor man's equi-portal
mesh.material = new THREE.ShaderMaterial( {
side: THREE.DoubleSide,
uniforms: {
@@ -729,33 +932,99 @@ xrfragment.xrf.href = function(v, opts){
vec3 direction = normalize(vWorldPosition - cameraPosition);
vec2 sampleUV;
sampleUV.y = -clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);
- sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2 + 0.5;
- gl_FragColor = texture2D(pano, sampleUV);
+ sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2;
+ sampleUV.x += 0.33; // adjust focus to AFRAME's $('a-scene').components.screenshot.capture()
+ vec4 color = texture2D(pano, sampleUV);
+ // Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js)
+ float luminance = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
+ vec4 grayscale_color = vec4(vec3(luminance) + vec3(0.33), color.a);
+ gl_FragColor = grayscale_color;
}
- `
+ `,
});
+ mesh.material.needsUpdate = true
+
+ const handleTeleport = (e) => {
+ if( mesh.clicked ) return
+ this.clicked = true
+ let portalArea = 1 // 1 meter
+ const meshWorldPosition = new THREE.Vector3();
+ meshWorldPosition.setFromMatrixPosition(mesh.matrixWorld);
+
+ const cameraDirection = new THREE.Vector3();
+ camera.getWorldPosition(cameraDirection);
+ cameraDirection.sub(meshWorldPosition);
+ cameraDirection.normalize();
+ cameraDirection.multiplyScalar(portalArea); // move away from portal
+ const newPos = meshWorldPosition.clone().add(cameraDirection);
+
+ const positionInFrontOfPortal = () => {
+ camera.position.copy(newPos);
+ camera.lookAt(meshWorldPosition);
+
+ if( xrf.baseReferenceSpace ){ // WebXR VR/AR roomscale reposition
+ const offsetPosition = { x: -newPos.x, y: 0, z: -newPos.z, w: 1 };
+ const offsetRotation = new THREE.Quaternion();
+ const transform = new XRRigidTransform( offsetPosition, offsetRotation );
+ const teleportSpaceOffset = xrf.baseReferenceSpace.getOffsetReferenceSpace( transform );
+ xrf.renderer.xr.setReferenceSpace( teleportSpaceOffset );
+ }
+
+ document.location.hash = `#pos=${camera.position.x},${camera.position.y},${camera.position.z}`;
+ }
+
+ const distance = camera.position.distanceTo(newPos);
+ if( distance > portalArea ) positionInFrontOfPortal()
+ else xrf.navigate.to(v.string) // ok let's surf to HREF!
+
+ setTimeout( () => mesh.clicked = false, 200 ) // prevent double clicks
+ }
+
+ if( !opts.frag.q ) mesh.addEventListener('click', handleTeleport )
+
+ // lazy remove mesh (because we're inside a traverse)
+ setTimeout( () => {
+ model.interactive.add(mesh) // make clickable
+ },200)
}
-xrfragment.xrf.pos = function(v, opts){
+xrf.frag.pos = function(v, opts){
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
console.log(" └ setting camera position to "+v.string)
camera.position.x = v.x
camera.position.y = v.y
camera.position.z = v.z
}
-xrfragment.xrf.rot = function(v, opts){
+xrf.frag.q = function(v, opts){
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ console.log(" └ running query ")
+ for ( let i in v.query ) {
+ let target = v.query[i]
+
+ // remove objects if requested
+ if( target.id != undefined && (target.mesh = scene.getObjectByName(i)) ){
+ target.mesh.visible = target.id
+ target.mesh.parent.remove(target.mesh)
+ console.log(` └ removing mesh: ${i}`)
+ }else console.log(` └ mesh not found: ${i}`)
+ }
+}
+xrf.frag.rot = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
camera.rotation.x = v.x * Math.PI / 180;
camera.rotation.y = v.y * Math.PI / 180;
camera.rotation.z = v.z * Math.PI / 180;
}
-xrfragment.xrf.src = function(v, opts){
+// *TODO* use webgl instancing
+
+xrf.frag.src = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
if( v.string[0] == "#" ){ // local
console.log(" └ instancing src")
- let args = xrfragment.URI.parse(v.string)
+ let frag = xrfragment.URI.parse(v.string)
// Get an instance of the original model
const modelInstance = new THREE.Group();
- modelInstance.add(model.scene.clone());
+ let sceneInstance = model.scene.clone()
+ modelInstance.add(sceneInstance)
modelInstance.position.z = mesh.position.z
modelInstance.position.y = mesh.position.y
modelInstance.position.x = mesh.position.x
@@ -763,19 +1032,10 @@ xrfragment.xrf.src = function(v, opts){
modelInstance.scale.y = mesh.scale.y
modelInstance.scale.x = mesh.scale.z
// now apply XR Fragments overrides from URI
- // *TODO* move to a central location (pull-up)
- for( var i in args ){
- if( i == "scale" ){
- console.log(" └ setting scale")
- modelInstance.scale.x = args[i].x
- modelInstance.scale.y = args[i].y
- modelInstance.scale.z = args[i].z
- }
- }
+ for( var i in frag )
+ xrf.eval.fragment(i, Object.assign(opts,{frag, model:modelInstance,scene:sceneInstance}))
// Add the instance to the scene
- scene.add(modelInstance);
- console.dir(model)
- console.dir(modelInstance)
+ model.scene.add(modelInstance);
}
}
window.AFRAME.registerComponent('xrf', {
@@ -784,72 +1044,72 @@ window.AFRAME.registerComponent('xrf', {
},
init: function () {
if( !AFRAME.XRF ) this.initXRFragments()
- if( !this.rig && this.el.querySelector('[camera]') )
- AFRAME.XRF.rig = this.el
+ if( typeof this.data == "string" ){
+ AFRAME.XRF.navigate.to(this.data)
+ .then( (model) => {
+ let gets = [ ...document.querySelectorAll('[xrf-get]') ]
+ gets.map( (g) => g.emit('update',model) )
+ })
+ }
},
+
initXRFragments: function(){
let aScene = document.querySelector('a-scene')
// enable XR fragments
let XRF = AFRAME.XRF = xrfragment.init({
THREE,
- camera: aScene.camera,
+ camera: aScene.camera,
scene: aScene.object3D,
renderer: aScene.renderer,
debug: true,
- loaders: [ THREE.GLTFLoader ], // which 3D assets to check for XR fragments?
+ loaders: { gltf: THREE.GLTFLoader } // which 3D assets (exts) to check for XR fragments?
})
+ if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare ABOVE entities with xrf-attributes'
- // override the 'pos' XR Fragment so we can translate the camera rig (not the camera itself)
- XRF.pos = (xrf,v,opts) => {
- let { mesh, model, camera, scene, renderer, THREE} = opts
- console.log("!pos")
- camera.parent.parent.position.x = v.x
- camera.parent.parent.position.y = v.y
- camera.parent.parent.position.z = v.z
- // xrf(v,opts) // skip threejs handler
+ // override the camera-related XR Fragments so the camera-rig is affected
+ let camOverride = (xrf,v,opts) => {
+ opts.camera = $('[camera]').object3D //parentElement.object3D
+ xrf(v,opts)
}
+
+ XRF.pos = camOverride
+ XRF.rot = camOverride
+ XRF.href = camOverride
- // override the 'rot' XR Fragment so we can translate the camera rig (not the camera itself)
- XRF.rot = (xrf,v,opts) => {
- let { mesh, model, camera, scene, renderer, THREE} = opts
- camera.parent.parent.rotation.x = v.x * Math.PI / 180;
- camera.parent.parent.rotation.y = v.y * Math.PI / 180;
- camera.parent.parent.rotation.z = v.z * Math.PI / 180;
- // xrf(v,opts) // skip threejs handler
- }
- }
-});
+ },
+})
-AFRAME.registerComponent('gltf-to-entity', {
+window.AFRAME.registerComponent('xrf-get', {
schema: {
- from: {default: '', type: 'selector'},
- name: {default: ''},
- duplicate: {type:'boolean'}
+ name: {type: 'string'},
+ duplicate: {type: 'boolean'}
},
init: function () {
- var el = this.el;
- var data = this.data;
- data.from.addEventListener('model-loaded', evt => {
- var model;
- var subset;
- model = evt.detail.model;
- console.dir(this.data.from)
- subset = model.getObjectByName(data.name);
- if (!subset){
- console.error("Sub-object", data.name, "not found in #"+data.from.id);
+ var el = this.el;
+ var meshname = this.data.name || this.data;
+
+ this.el.addEventListener('update', (evt) => {
+
+ let scene = evt.detail.scene
+ let mesh = scene.getObjectByName(meshname);
+ if (!mesh){
+ console.error("mesh with name '"+meshname+"' not found in model")
return;
}
- if( !this.data.duplicate ) subset.parent.remove(subset)
- let clone = subset.clone()
- ////subset.updateMatrixWorld();
- el.object3D.position.setFromMatrixPosition(data.from.object3D.matrixWorld);
- el.object3D.quaternion.setFromRotationMatrix(data.from.object3D.matrixWorld);
-
- el.setObject3D('mesh', clone );
- el.emit('model-loaded', el.getObject3D('mesh'));
+ if( !this.data.duplicate ) mesh.parent.remove(mesh)
+ if( this.mesh ) this.mesh.parent.remove(this.mesh) // cleanup old clone
+ let clone = this.mesh = mesh.clone()
+ ////mesh.updateMatrixWorld();
+ this.el.object3D.position.setFromMatrixPosition(scene.matrixWorld);
+ this.el.object3D.quaternion.setFromRotationMatrix(scene.matrixWorld);
+ this.el.setObject3D('mesh', clone );
+ if( !this.el.id ) this.el.setAttribute("id",`xrf-${clone.name}`)
+
+ })
- });
}
+
});
+
diff --git a/dist/xrfragment.py b/dist/xrfragment.py
deleted file mode 100644
index f038d9f..0000000
--- a/dist/xrfragment.py
+++ /dev/null
@@ -1,1678 +0,0 @@
-import sys
-
-import math as python_lib_Math
-import math as Math
-import inspect as python_lib_Inspect
-import sys as python_lib_Sys
-import functools as python_lib_Functools
-import re as python_lib_Re
-import traceback as python_lib_Traceback
-import urllib.parse as python_lib_urllib_Parse
-
-
-class _hx_AnonObject:
- _hx_disable_getattr = False
- def __init__(self, fields):
- self.__dict__ = fields
- def __repr__(self):
- return repr(self.__dict__)
- def __contains__(self, item):
- return item in self.__dict__
- def __getitem__(self, item):
- return self.__dict__[item]
- def __getattr__(self, name):
- if (self._hx_disable_getattr):
- raise AttributeError('field does not exist')
- else:
- return None
- def _hx_hasattr(self,field):
- self._hx_disable_getattr = True
- try:
- getattr(self, field)
- self._hx_disable_getattr = False
- return True
- except AttributeError:
- self._hx_disable_getattr = False
- return False
-
-
-
-class Enum:
- _hx_class_name = "Enum"
- __slots__ = ("tag", "index", "params")
- _hx_fields = ["tag", "index", "params"]
- _hx_methods = ["__str__"]
-
- def __init__(self,tag,index,params):
- self.tag = tag
- self.index = index
- self.params = params
-
- def __str__(self):
- if (self.params is None):
- return self.tag
- else:
- return self.tag + '(' + (', '.join(str(v) for v in self.params)) + ')'
-
-
-
-class Class: pass
-
-
-class EReg:
- _hx_class_name = "EReg"
- __slots__ = ("pattern", "matchObj", "_hx_global")
- _hx_fields = ["pattern", "matchObj", "global"]
- _hx_methods = ["split"]
-
- def __init__(self,r,opt):
- self.matchObj = None
- self._hx_global = False
- options = 0
- _g = 0
- _g1 = len(opt)
- while (_g < _g1):
- i = _g
- _g = (_g + 1)
- c = (-1 if ((i >= len(opt))) else ord(opt[i]))
- if (c == 109):
- options = (options | python_lib_Re.M)
- if (c == 105):
- options = (options | python_lib_Re.I)
- if (c == 115):
- options = (options | python_lib_Re.S)
- if (c == 117):
- options = (options | python_lib_Re.U)
- if (c == 103):
- self._hx_global = True
- self.pattern = python_lib_Re.compile(r,options)
-
- def split(self,s):
- if self._hx_global:
- ret = []
- lastEnd = 0
- x = python_HaxeIterator(python_lib_Re.finditer(self.pattern,s))
- while x.hasNext():
- x1 = x.next()
- x2 = HxString.substring(s,lastEnd,x1.start())
- ret.append(x2)
- lastEnd = x1.end()
- x = HxString.substr(s,lastEnd,None)
- ret.append(x)
- return ret
- else:
- self.matchObj = python_lib_Re.search(self.pattern,s)
- if (self.matchObj is None):
- return [s]
- else:
- return [HxString.substring(s,0,self.matchObj.start()), HxString.substr(s,self.matchObj.end(),None)]
-
-
-
-class Reflect:
- _hx_class_name = "Reflect"
- __slots__ = ()
- _hx_statics = ["field", "deleteField"]
-
- @staticmethod
- def field(o,field):
- return python_Boot.field(o,field)
-
- @staticmethod
- def deleteField(o,field):
- if (field in python_Boot.keywords):
- field = ("_hx_" + field)
- elif ((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95))):
- field = ("_hx_" + field)
- if (not python_Boot.hasField(o,field)):
- return False
- o.__delattr__(field)
- return True
-
-
-class Std:
- _hx_class_name = "Std"
- __slots__ = ()
- _hx_statics = ["isOfType", "string", "parseInt", "shortenPossibleNumber", "parseFloat"]
-
- @staticmethod
- def isOfType(v,t):
- if ((v is None) and ((t is None))):
- return False
- if (t is None):
- return False
- if ((type(t) == type) and (t == Dynamic)):
- return (v is not None)
- isBool = isinstance(v,bool)
- if (((type(t) == type) and (t == Bool)) and isBool):
- return True
- if ((((not isBool) and (not ((type(t) == type) and (t == Bool)))) and ((type(t) == type) and (t == Int))) and isinstance(v,int)):
- return True
- vIsFloat = isinstance(v,float)
- tmp = None
- tmp1 = None
- if (((not isBool) and vIsFloat) and ((type(t) == type) and (t == Int))):
- f = v
- tmp1 = (((f != Math.POSITIVE_INFINITY) and ((f != Math.NEGATIVE_INFINITY))) and (not python_lib_Math.isnan(f)))
- else:
- tmp1 = False
- if tmp1:
- tmp1 = None
- try:
- tmp1 = int(v)
- except BaseException as _g:
- None
- tmp1 = None
- tmp = (v == tmp1)
- else:
- tmp = False
- if ((tmp and ((v <= 2147483647))) and ((v >= -2147483648))):
- return True
- if (((not isBool) and ((type(t) == type) and (t == Float))) and isinstance(v,(float, int))):
- return True
- if ((type(t) == type) and (t == str)):
- return isinstance(v,str)
- isEnumType = ((type(t) == type) and (t == Enum))
- if ((isEnumType and python_lib_Inspect.isclass(v)) and hasattr(v,"_hx_constructs")):
- return True
- if isEnumType:
- return False
- isClassType = ((type(t) == type) and (t == Class))
- if ((((isClassType and (not isinstance(v,Enum))) and python_lib_Inspect.isclass(v)) and hasattr(v,"_hx_class_name")) and (not hasattr(v,"_hx_constructs"))):
- return True
- if isClassType:
- return False
- tmp = None
- try:
- tmp = isinstance(v,t)
- except BaseException as _g:
- None
- tmp = False
- if tmp:
- return True
- if python_lib_Inspect.isclass(t):
- cls = t
- loop = None
- def _hx_local_1(intf):
- f = (intf._hx_interfaces if (hasattr(intf,"_hx_interfaces")) else [])
- if (f is not None):
- _g = 0
- while (_g < len(f)):
- i = (f[_g] if _g >= 0 and _g < len(f) else None)
- _g = (_g + 1)
- if (i == cls):
- return True
- else:
- l = loop(i)
- if l:
- return True
- return False
- else:
- return False
- loop = _hx_local_1
- currentClass = v.__class__
- result = False
- while (currentClass is not None):
- if loop(currentClass):
- result = True
- break
- currentClass = python_Boot.getSuperClass(currentClass)
- return result
- else:
- return False
-
- @staticmethod
- def string(s):
- return python_Boot.toString1(s,"")
-
- @staticmethod
- def parseInt(x):
- if (x is None):
- return None
- try:
- return int(x)
- except BaseException as _g:
- None
- base = 10
- _hx_len = len(x)
- foundCount = 0
- sign = 0
- firstDigitIndex = 0
- lastDigitIndex = -1
- previous = 0
- _g = 0
- _g1 = _hx_len
- while (_g < _g1):
- i = _g
- _g = (_g + 1)
- c = (-1 if ((i >= len(x))) else ord(x[i]))
- if (((c > 8) and ((c < 14))) or ((c == 32))):
- if (foundCount > 0):
- return None
- continue
- else:
- c1 = c
- if (c1 == 43):
- if (foundCount == 0):
- sign = 1
- elif (not (((48 <= c) and ((c <= 57))))):
- if (not (((base == 16) and ((((97 <= c) and ((c <= 122))) or (((65 <= c) and ((c <= 90))))))))):
- break
- elif (c1 == 45):
- if (foundCount == 0):
- sign = -1
- elif (not (((48 <= c) and ((c <= 57))))):
- if (not (((base == 16) and ((((97 <= c) and ((c <= 122))) or (((65 <= c) and ((c <= 90))))))))):
- break
- elif (c1 == 48):
- if (not (((foundCount == 0) or (((foundCount == 1) and ((sign != 0))))))):
- if (not (((48 <= c) and ((c <= 57))))):
- if (not (((base == 16) and ((((97 <= c) and ((c <= 122))) or (((65 <= c) and ((c <= 90))))))))):
- break
- elif ((c1 == 120) or ((c1 == 88))):
- if ((previous == 48) and ((((foundCount == 1) and ((sign == 0))) or (((foundCount == 2) and ((sign != 0))))))):
- base = 16
- elif (not (((48 <= c) and ((c <= 57))))):
- if (not (((base == 16) and ((((97 <= c) and ((c <= 122))) or (((65 <= c) and ((c <= 90))))))))):
- break
- elif (not (((48 <= c) and ((c <= 57))))):
- if (not (((base == 16) and ((((97 <= c) and ((c <= 122))) or (((65 <= c) and ((c <= 90))))))))):
- break
- if (((foundCount == 0) and ((sign == 0))) or (((foundCount == 1) and ((sign != 0))))):
- firstDigitIndex = i
- foundCount = (foundCount + 1)
- lastDigitIndex = i
- previous = c
- if (firstDigitIndex <= lastDigitIndex):
- digits = HxString.substring(x,firstDigitIndex,(lastDigitIndex + 1))
- try:
- return (((-1 if ((sign == -1)) else 1)) * int(digits,base))
- except BaseException as _g:
- return None
- return None
-
- @staticmethod
- def shortenPossibleNumber(x):
- r = ""
- _g = 0
- _g1 = len(x)
- while (_g < _g1):
- i = _g
- _g = (_g + 1)
- c = ("" if (((i < 0) or ((i >= len(x))))) else x[i])
- _g2 = HxString.charCodeAt(c,0)
- if (_g2 is None):
- break
- else:
- _g3 = _g2
- if (((((((((((_g3 == 57) or ((_g3 == 56))) or ((_g3 == 55))) or ((_g3 == 54))) or ((_g3 == 53))) or ((_g3 == 52))) or ((_g3 == 51))) or ((_g3 == 50))) or ((_g3 == 49))) or ((_g3 == 48))) or ((_g3 == 46))):
- r = (("null" if r is None else r) + ("null" if c is None else c))
- else:
- break
- return r
-
- @staticmethod
- def parseFloat(x):
- try:
- return float(x)
- except BaseException as _g:
- None
- if (x is not None):
- r1 = Std.shortenPossibleNumber(x)
- if (r1 != x):
- return Std.parseFloat(r1)
- return Math.NaN
-
-
-class Float: pass
-
-
-class Int: pass
-
-
-class Bool: pass
-
-
-class Dynamic: pass
-
-
-class StringTools:
- _hx_class_name = "StringTools"
- __slots__ = ()
- _hx_statics = ["isSpace", "ltrim", "rtrim", "trim", "replace"]
-
- @staticmethod
- def isSpace(s,pos):
- if (((len(s) == 0) or ((pos < 0))) or ((pos >= len(s)))):
- return False
- c = HxString.charCodeAt(s,pos)
- if (not (((c > 8) and ((c < 14))))):
- return (c == 32)
- else:
- return True
-
- @staticmethod
- def ltrim(s):
- l = len(s)
- r = 0
- while ((r < l) and StringTools.isSpace(s,r)):
- r = (r + 1)
- if (r > 0):
- return HxString.substr(s,r,(l - r))
- else:
- return s
-
- @staticmethod
- def rtrim(s):
- l = len(s)
- r = 0
- while ((r < l) and StringTools.isSpace(s,((l - r) - 1))):
- r = (r + 1)
- if (r > 0):
- return HxString.substr(s,0,(l - r))
- else:
- return s
-
- @staticmethod
- def trim(s):
- return StringTools.ltrim(StringTools.rtrim(s))
-
- @staticmethod
- def replace(s,sub,by):
- _this = (list(s) if ((sub == "")) else s.split(sub))
- return by.join([python_Boot.toString1(x1,'') for x1 in _this])
-
-
-class haxe_IMap:
- _hx_class_name = "haxe.IMap"
- __slots__ = ()
-
-
-class haxe_Exception(Exception):
- _hx_class_name = "haxe.Exception"
- __slots__ = ("_hx___nativeStack", "_hx___nativeException", "_hx___previousException")
- _hx_fields = ["__nativeStack", "__nativeException", "__previousException"]
- _hx_methods = ["unwrap"]
- _hx_statics = ["caught"]
- _hx_interfaces = []
- _hx_super = Exception
-
-
- def __init__(self,message,previous = None,native = None):
- self._hx___previousException = None
- self._hx___nativeException = None
- self._hx___nativeStack = None
- super().__init__(message)
- self._hx___previousException = previous
- if ((native is not None) and Std.isOfType(native,BaseException)):
- self._hx___nativeException = native
- self._hx___nativeStack = haxe_NativeStackTrace.exceptionStack()
- else:
- self._hx___nativeException = self
- infos = python_lib_Traceback.extract_stack()
- if (len(infos) != 0):
- infos.pop()
- infos.reverse()
- self._hx___nativeStack = infos
-
- def unwrap(self):
- return self._hx___nativeException
-
- @staticmethod
- def caught(value):
- if Std.isOfType(value,haxe_Exception):
- return value
- elif Std.isOfType(value,BaseException):
- return haxe_Exception(str(value),None,value)
- else:
- return haxe_ValueException(value,None,value)
-
-
-
-class haxe_NativeStackTrace:
- _hx_class_name = "haxe.NativeStackTrace"
- __slots__ = ()
- _hx_statics = ["saveStack", "exceptionStack"]
-
- @staticmethod
- def saveStack(exception):
- pass
-
- @staticmethod
- def exceptionStack():
- exc = python_lib_Sys.exc_info()
- if (exc[2] is not None):
- infos = python_lib_Traceback.extract_tb(exc[2])
- infos.reverse()
- return infos
- else:
- return []
-
-
-class haxe_ValueException(haxe_Exception):
- _hx_class_name = "haxe.ValueException"
- __slots__ = ("value",)
- _hx_fields = ["value"]
- _hx_methods = ["unwrap"]
- _hx_statics = []
- _hx_interfaces = []
- _hx_super = haxe_Exception
-
-
- def __init__(self,value,previous = None,native = None):
- self.value = None
- super().__init__(Std.string(value),previous,native)
- self.value = value
-
- def unwrap(self):
- return self.value
-
-
-
-class haxe_ds_StringMap:
- _hx_class_name = "haxe.ds.StringMap"
- __slots__ = ("h",)
- _hx_fields = ["h"]
- _hx_interfaces = [haxe_IMap]
-
- def __init__(self):
- self.h = dict()
-
-
-
-class haxe_iterators_ArrayIterator:
- _hx_class_name = "haxe.iterators.ArrayIterator"
- __slots__ = ("array", "current")
- _hx_fields = ["array", "current"]
- _hx_methods = ["hasNext", "next"]
-
- def __init__(self,array):
- self.current = 0
- self.array = array
-
- def hasNext(self):
- return (self.current < len(self.array))
-
- def next(self):
- def _hx_local_3():
- def _hx_local_2():
- _hx_local_0 = self
- _hx_local_1 = _hx_local_0.current
- _hx_local_0.current = (_hx_local_1 + 1)
- return _hx_local_1
- return python_internal_ArrayImpl._get(self.array, _hx_local_2())
- return _hx_local_3()
-
-
-
-class haxe_iterators_ArrayKeyValueIterator:
- _hx_class_name = "haxe.iterators.ArrayKeyValueIterator"
- __slots__ = ("current", "array")
- _hx_fields = ["current", "array"]
- _hx_methods = ["hasNext", "next"]
-
- def __init__(self,array):
- self.current = 0
- self.array = array
-
- def hasNext(self):
- return (self.current < len(self.array))
-
- def next(self):
- def _hx_local_3():
- def _hx_local_2():
- _hx_local_0 = self
- _hx_local_1 = _hx_local_0.current
- _hx_local_0.current = (_hx_local_1 + 1)
- return _hx_local_1
- return _hx_AnonObject({'value': python_internal_ArrayImpl._get(self.array, self.current), 'key': _hx_local_2()})
- return _hx_local_3()
-
-
-
-class python_Boot:
- _hx_class_name = "python.Boot"
- __slots__ = ()
- _hx_statics = ["keywords", "toString1", "fields", "simpleField", "hasField", "field", "getInstanceFields", "getSuperClass", "getClassFields", "prefixLength", "unhandleKeywords"]
-
- @staticmethod
- def toString1(o,s):
- if (o is None):
- return "null"
- if isinstance(o,str):
- return o
- if (s is None):
- s = ""
- if (len(s) >= 5):
- return "<...>"
- if isinstance(o,bool):
- if o:
- return "true"
- else:
- return "false"
- if (isinstance(o,int) and (not isinstance(o,bool))):
- return str(o)
- if isinstance(o,float):
- try:
- if (o == int(o)):
- return str(Math.floor((o + 0.5)))
- else:
- return str(o)
- except BaseException as _g:
- None
- return str(o)
- if isinstance(o,list):
- o1 = o
- l = len(o1)
- st = "["
- s = (("null" if s is None else s) + "\t")
- _g = 0
- _g1 = l
- while (_g < _g1):
- i = _g
- _g = (_g + 1)
- prefix = ""
- if (i > 0):
- prefix = ","
- st = (("null" if st is None else st) + HxOverrides.stringOrNull(((("null" if prefix is None else prefix) + HxOverrides.stringOrNull(python_Boot.toString1((o1[i] if i >= 0 and i < len(o1) else None),s))))))
- st = (("null" if st is None else st) + "]")
- return st
- try:
- if hasattr(o,"toString"):
- return o.toString()
- except BaseException as _g:
- None
- if hasattr(o,"__class__"):
- if isinstance(o,_hx_AnonObject):
- toStr = None
- try:
- fields = python_Boot.fields(o)
- _g = []
- _g1 = 0
- while (_g1 < len(fields)):
- f = (fields[_g1] if _g1 >= 0 and _g1 < len(fields) else None)
- _g1 = (_g1 + 1)
- x = ((("" + ("null" if f is None else f)) + " : ") + HxOverrides.stringOrNull(python_Boot.toString1(python_Boot.simpleField(o,f),(("null" if s is None else s) + "\t"))))
- _g.append(x)
- fieldsStr = _g
- toStr = (("{ " + HxOverrides.stringOrNull(", ".join([x1 for x1 in fieldsStr]))) + " }")
- except BaseException as _g:
- None
- return "{ ... }"
- if (toStr is None):
- return "{ ... }"
- else:
- return toStr
- if isinstance(o,Enum):
- o1 = o
- l = len(o1.params)
- hasParams = (l > 0)
- if hasParams:
- paramsStr = ""
- _g = 0
- _g1 = l
- while (_g < _g1):
- i = _g
- _g = (_g + 1)
- prefix = ""
- if (i > 0):
- prefix = ","
- paramsStr = (("null" if paramsStr is None else paramsStr) + HxOverrides.stringOrNull(((("null" if prefix is None else prefix) + HxOverrides.stringOrNull(python_Boot.toString1(o1.params[i],s))))))
- return (((HxOverrides.stringOrNull(o1.tag) + "(") + ("null" if paramsStr is None else paramsStr)) + ")")
- else:
- return o1.tag
- if hasattr(o,"_hx_class_name"):
- if (o.__class__.__name__ != "type"):
- fields = python_Boot.getInstanceFields(o)
- _g = []
- _g1 = 0
- while (_g1 < len(fields)):
- f = (fields[_g1] if _g1 >= 0 and _g1 < len(fields) else None)
- _g1 = (_g1 + 1)
- x = ((("" + ("null" if f is None else f)) + " : ") + HxOverrides.stringOrNull(python_Boot.toString1(python_Boot.simpleField(o,f),(("null" if s is None else s) + "\t"))))
- _g.append(x)
- fieldsStr = _g
- toStr = (((HxOverrides.stringOrNull(o._hx_class_name) + "( ") + HxOverrides.stringOrNull(", ".join([x1 for x1 in fieldsStr]))) + " )")
- return toStr
- else:
- fields = python_Boot.getClassFields(o)
- _g = []
- _g1 = 0
- while (_g1 < len(fields)):
- f = (fields[_g1] if _g1 >= 0 and _g1 < len(fields) else None)
- _g1 = (_g1 + 1)
- x = ((("" + ("null" if f is None else f)) + " : ") + HxOverrides.stringOrNull(python_Boot.toString1(python_Boot.simpleField(o,f),(("null" if s is None else s) + "\t"))))
- _g.append(x)
- fieldsStr = _g
- toStr = (((("#" + HxOverrides.stringOrNull(o._hx_class_name)) + "( ") + HxOverrides.stringOrNull(", ".join([x1 for x1 in fieldsStr]))) + " )")
- return toStr
- if ((type(o) == type) and (o == str)):
- return "#String"
- if ((type(o) == type) and (o == list)):
- return "#Array"
- if callable(o):
- return "function"
- try:
- if hasattr(o,"__repr__"):
- return o.__repr__()
- except BaseException as _g:
- None
- if hasattr(o,"__str__"):
- return o.__str__([])
- if hasattr(o,"__name__"):
- return o.__name__
- return "???"
- else:
- return str(o)
-
- @staticmethod
- def fields(o):
- a = []
- if (o is not None):
- if hasattr(o,"_hx_fields"):
- fields = o._hx_fields
- if (fields is not None):
- return list(fields)
- if isinstance(o,_hx_AnonObject):
- d = o.__dict__
- keys = d.keys()
- handler = python_Boot.unhandleKeywords
- for k in keys:
- if (k != '_hx_disable_getattr'):
- a.append(handler(k))
- elif hasattr(o,"__dict__"):
- d = o.__dict__
- keys1 = d.keys()
- for k in keys1:
- a.append(k)
- return a
-
- @staticmethod
- def simpleField(o,field):
- if (field is None):
- return None
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
-
- @staticmethod
- def hasField(o,field):
- if isinstance(o,_hx_AnonObject):
- return o._hx_hasattr(field)
- return hasattr(o,(("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field)))
-
- @staticmethod
- def field(o,field):
- if (field is None):
- return None
- if isinstance(o,str):
- field1 = field
- _hx_local_0 = len(field1)
- if (_hx_local_0 == 10):
- if (field1 == "charCodeAt"):
- return python_internal_MethodClosure(o,HxString.charCodeAt)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_0 == 11):
- if (field1 == "lastIndexOf"):
- return python_internal_MethodClosure(o,HxString.lastIndexOf)
- elif (field1 == "toLowerCase"):
- return python_internal_MethodClosure(o,HxString.toLowerCase)
- elif (field1 == "toUpperCase"):
- return python_internal_MethodClosure(o,HxString.toUpperCase)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_0 == 9):
- if (field1 == "substring"):
- return python_internal_MethodClosure(o,HxString.substring)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_0 == 5):
- if (field1 == "split"):
- return python_internal_MethodClosure(o,HxString.split)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_0 == 7):
- if (field1 == "indexOf"):
- return python_internal_MethodClosure(o,HxString.indexOf)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_0 == 8):
- if (field1 == "toString"):
- return python_internal_MethodClosure(o,HxString.toString)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_0 == 6):
- if (field1 == "charAt"):
- return python_internal_MethodClosure(o,HxString.charAt)
- elif (field1 == "length"):
- return len(o)
- elif (field1 == "substr"):
- return python_internal_MethodClosure(o,HxString.substr)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif isinstance(o,list):
- field1 = field
- _hx_local_1 = len(field1)
- if (_hx_local_1 == 11):
- if (field1 == "lastIndexOf"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.lastIndexOf)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_1 == 4):
- if (field1 == "copy"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.copy)
- elif (field1 == "join"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.join)
- elif (field1 == "push"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.push)
- elif (field1 == "sort"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.sort)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_1 == 5):
- if (field1 == "shift"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.shift)
- elif (field1 == "slice"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.slice)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_1 == 7):
- if (field1 == "indexOf"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.indexOf)
- elif (field1 == "reverse"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.reverse)
- elif (field1 == "unshift"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.unshift)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_1 == 3):
- if (field1 == "map"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.map)
- elif (field1 == "pop"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.pop)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_1 == 8):
- if (field1 == "contains"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.contains)
- elif (field1 == "iterator"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.iterator)
- elif (field1 == "toString"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.toString)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_1 == 16):
- if (field1 == "keyValueIterator"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.keyValueIterator)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- elif (_hx_local_1 == 6):
- if (field1 == "concat"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.concat)
- elif (field1 == "filter"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.filter)
- elif (field1 == "insert"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.insert)
- elif (field1 == "length"):
- return len(o)
- elif (field1 == "remove"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.remove)
- elif (field1 == "splice"):
- return python_internal_MethodClosure(o,python_internal_ArrayImpl.splice)
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
- else:
- field1 = (("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))
- if hasattr(o,field1):
- return getattr(o,field1)
- else:
- return None
-
- @staticmethod
- def getInstanceFields(c):
- f = (list(c._hx_fields) if (hasattr(c,"_hx_fields")) else [])
- if hasattr(c,"_hx_methods"):
- f = (f + c._hx_methods)
- sc = python_Boot.getSuperClass(c)
- if (sc is None):
- return f
- else:
- scArr = python_Boot.getInstanceFields(sc)
- scMap = set(scArr)
- _g = 0
- while (_g < len(f)):
- f1 = (f[_g] if _g >= 0 and _g < len(f) else None)
- _g = (_g + 1)
- if (not (f1 in scMap)):
- scArr.append(f1)
- return scArr
-
- @staticmethod
- def getSuperClass(c):
- if (c is None):
- return None
- try:
- if hasattr(c,"_hx_super"):
- return c._hx_super
- return None
- except BaseException as _g:
- None
- return None
-
- @staticmethod
- def getClassFields(c):
- if hasattr(c,"_hx_statics"):
- x = c._hx_statics
- return list(x)
- else:
- return []
-
- @staticmethod
- def unhandleKeywords(name):
- if (HxString.substr(name,0,python_Boot.prefixLength) == "_hx_"):
- real = HxString.substr(name,python_Boot.prefixLength,None)
- if (real in python_Boot.keywords):
- return real
- return name
-
-
-class python_HaxeIterator:
- _hx_class_name = "python.HaxeIterator"
- __slots__ = ("it", "x", "has", "checked")
- _hx_fields = ["it", "x", "has", "checked"]
- _hx_methods = ["next", "hasNext"]
-
- def __init__(self,it):
- self.checked = False
- self.has = False
- self.x = None
- self.it = it
-
- def next(self):
- if (not self.checked):
- self.hasNext()
- self.checked = False
- return self.x
-
- def hasNext(self):
- if (not self.checked):
- try:
- self.x = self.it.__next__()
- self.has = True
- except BaseException as _g:
- None
- if Std.isOfType(haxe_Exception.caught(_g).unwrap(),StopIteration):
- self.has = False
- self.x = None
- else:
- raise _g
- self.checked = True
- return self.has
-
-
-
-class python_internal_ArrayImpl:
- _hx_class_name = "python.internal.ArrayImpl"
- __slots__ = ()
- _hx_statics = ["concat", "copy", "iterator", "keyValueIterator", "indexOf", "lastIndexOf", "join", "toString", "pop", "push", "unshift", "remove", "contains", "shift", "slice", "sort", "splice", "map", "filter", "insert", "reverse", "_get"]
-
- @staticmethod
- def concat(a1,a2):
- return (a1 + a2)
-
- @staticmethod
- def copy(x):
- return list(x)
-
- @staticmethod
- def iterator(x):
- return python_HaxeIterator(x.__iter__())
-
- @staticmethod
- def keyValueIterator(x):
- return haxe_iterators_ArrayKeyValueIterator(x)
-
- @staticmethod
- def indexOf(a,x,fromIndex = None):
- _hx_len = len(a)
- l = (0 if ((fromIndex is None)) else ((_hx_len + fromIndex) if ((fromIndex < 0)) else fromIndex))
- if (l < 0):
- l = 0
- _g = l
- _g1 = _hx_len
- while (_g < _g1):
- i = _g
- _g = (_g + 1)
- if HxOverrides.eq(a[i],x):
- return i
- return -1
-
- @staticmethod
- def lastIndexOf(a,x,fromIndex = None):
- _hx_len = len(a)
- l = (_hx_len if ((fromIndex is None)) else (((_hx_len + fromIndex) + 1) if ((fromIndex < 0)) else (fromIndex + 1)))
- if (l > _hx_len):
- l = _hx_len
- while True:
- l = (l - 1)
- tmp = l
- if (not ((tmp > -1))):
- break
- if HxOverrides.eq(a[l],x):
- return l
- return -1
-
- @staticmethod
- def join(x,sep):
- return sep.join([python_Boot.toString1(x1,'') for x1 in x])
-
- @staticmethod
- def toString(x):
- return (("[" + HxOverrides.stringOrNull(",".join([python_Boot.toString1(x1,'') for x1 in x]))) + "]")
-
- @staticmethod
- def pop(x):
- if (len(x) == 0):
- return None
- else:
- return x.pop()
-
- @staticmethod
- def push(x,e):
- x.append(e)
- return len(x)
-
- @staticmethod
- def unshift(x,e):
- x.insert(0, e)
-
- @staticmethod
- def remove(x,e):
- try:
- x.remove(e)
- return True
- except BaseException as _g:
- None
- return False
-
- @staticmethod
- def contains(x,e):
- return (e in x)
-
- @staticmethod
- def shift(x):
- if (len(x) == 0):
- return None
- return x.pop(0)
-
- @staticmethod
- def slice(x,pos,end = None):
- return x[pos:end]
-
- @staticmethod
- def sort(x,f):
- x.sort(key= python_lib_Functools.cmp_to_key(f))
-
- @staticmethod
- def splice(x,pos,_hx_len):
- if (pos < 0):
- pos = (len(x) + pos)
- if (pos < 0):
- pos = 0
- res = x[pos:(pos + _hx_len)]
- del x[pos:(pos + _hx_len)]
- return res
-
- @staticmethod
- def map(x,f):
- return list(map(f,x))
-
- @staticmethod
- def filter(x,f):
- return list(filter(f,x))
-
- @staticmethod
- def insert(a,pos,x):
- a.insert(pos, x)
-
- @staticmethod
- def reverse(a):
- a.reverse()
-
- @staticmethod
- def _get(x,idx):
- if ((idx > -1) and ((idx < len(x)))):
- return x[idx]
- else:
- return None
-
-
-class HxOverrides:
- _hx_class_name = "HxOverrides"
- __slots__ = ()
- _hx_statics = ["eq", "stringOrNull", "push", "arrayGet"]
-
- @staticmethod
- def eq(a,b):
- if (isinstance(a,list) or isinstance(b,list)):
- return a is b
- return (a == b)
-
- @staticmethod
- def stringOrNull(s):
- if (s is None):
- return "null"
- else:
- return s
-
- @staticmethod
- def push(x,e):
- if isinstance(x,list):
- _this = x
- _this.append(e)
- return len(_this)
- return x.push(e)
-
- @staticmethod
- def arrayGet(a,i):
- if isinstance(a,list):
- x = a
- if ((i > -1) and ((i < len(x)))):
- return x[i]
- else:
- return None
- else:
- return a[i]
-
-
-class python_internal_MethodClosure:
- _hx_class_name = "python.internal.MethodClosure"
- __slots__ = ("obj", "func")
- _hx_fields = ["obj", "func"]
- _hx_methods = ["__call__"]
-
- def __init__(self,obj,func):
- self.obj = obj
- self.func = func
-
- def __call__(self,*args):
- return self.func(self.obj,*args)
-
-
-
-class HxString:
- _hx_class_name = "HxString"
- __slots__ = ()
- _hx_statics = ["split", "charCodeAt", "charAt", "lastIndexOf", "toUpperCase", "toLowerCase", "indexOf", "indexOfImpl", "toString", "substring", "substr"]
-
- @staticmethod
- def split(s,d):
- if (d == ""):
- return list(s)
- else:
- return s.split(d)
-
- @staticmethod
- def charCodeAt(s,index):
- if ((((s is None) or ((len(s) == 0))) or ((index < 0))) or ((index >= len(s)))):
- return None
- else:
- return ord(s[index])
-
- @staticmethod
- def charAt(s,index):
- if ((index < 0) or ((index >= len(s)))):
- return ""
- else:
- return s[index]
-
- @staticmethod
- def lastIndexOf(s,_hx_str,startIndex = None):
- if (startIndex is None):
- return s.rfind(_hx_str, 0, len(s))
- elif (_hx_str == ""):
- length = len(s)
- if (startIndex < 0):
- startIndex = (length + startIndex)
- if (startIndex < 0):
- startIndex = 0
- if (startIndex > length):
- return length
- else:
- return startIndex
- else:
- i = s.rfind(_hx_str, 0, (startIndex + 1))
- startLeft = (max(0,((startIndex + 1) - len(_hx_str))) if ((i == -1)) else (i + 1))
- check = s.find(_hx_str, startLeft, len(s))
- if ((check > i) and ((check <= startIndex))):
- return check
- else:
- return i
-
- @staticmethod
- def toUpperCase(s):
- return s.upper()
-
- @staticmethod
- def toLowerCase(s):
- return s.lower()
-
- @staticmethod
- def indexOf(s,_hx_str,startIndex = None):
- if (startIndex is None):
- return s.find(_hx_str)
- else:
- return HxString.indexOfImpl(s,_hx_str,startIndex)
-
- @staticmethod
- def indexOfImpl(s,_hx_str,startIndex):
- if (_hx_str == ""):
- length = len(s)
- if (startIndex < 0):
- startIndex = (length + startIndex)
- if (startIndex < 0):
- startIndex = 0
- if (startIndex > length):
- return length
- else:
- return startIndex
- return s.find(_hx_str, startIndex)
-
- @staticmethod
- def toString(s):
- return s
-
- @staticmethod
- def substring(s,startIndex,endIndex = None):
- if (startIndex < 0):
- startIndex = 0
- if (endIndex is None):
- return s[startIndex:]
- else:
- if (endIndex < 0):
- endIndex = 0
- if (endIndex < startIndex):
- return s[endIndex:startIndex]
- else:
- return s[startIndex:endIndex]
-
- @staticmethod
- def substr(s,startIndex,_hx_len = None):
- if (_hx_len is None):
- return s[startIndex:]
- else:
- if (_hx_len == 0):
- return ""
- if (startIndex < 0):
- startIndex = (len(s) + startIndex)
- if (startIndex < 0):
- startIndex = 0
- return s[startIndex:(startIndex + _hx_len)]
-
-
-class xrfragment_Parser:
- _hx_class_name = "xrfragment.Parser"
- __slots__ = ()
- _hx_statics = ["error", "debug", "parse"]
-
- @staticmethod
- def parse(key,value,resultMap):
- Frag = haxe_ds_StringMap()
- Frag.h["prio"] = (xrfragment_XRF.ASSET | xrfragment_XRF.T_INT)
- Frag.h["#"] = (xrfragment_XRF.ASSET | xrfragment_XRF.T_PREDEFINED_VIEW)
- Frag.h["class"] = (xrfragment_XRF.ASSET | xrfragment_XRF.T_STRING)
- Frag.h["src"] = (xrfragment_XRF.ASSET | xrfragment_XRF.T_URL)
- Frag.h["pos"] = (((((xrfragment_XRF.PV_OVERRIDE | xrfragment_XRF.ROUNDROBIN) | xrfragment_XRF.T_VECTOR3) | xrfragment_XRF.T_STRING_OBJ) | xrfragment_XRF.EMBEDDED) | xrfragment_XRF.NAVIGATOR)
- Frag.h["href"] = ((xrfragment_XRF.ASSET | xrfragment_XRF.T_URL) | xrfragment_XRF.T_PREDEFINED_VIEW)
- Frag.h["q"] = ((xrfragment_XRF.PV_OVERRIDE | xrfragment_XRF.T_STRING) | xrfragment_XRF.EMBEDDED)
- Frag.h["scale"] = ((((xrfragment_XRF.QUERY_OPERATOR | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.ROUNDROBIN) | xrfragment_XRF.T_VECTOR3) | xrfragment_XRF.EMBEDDED)
- Frag.h["rot"] = (((((xrfragment_XRF.QUERY_OPERATOR | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.ROUNDROBIN) | xrfragment_XRF.T_VECTOR3) | xrfragment_XRF.EMBEDDED) | xrfragment_XRF.NAVIGATOR)
- Frag.h["translate"] = ((((xrfragment_XRF.QUERY_OPERATOR | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.ROUNDROBIN) | xrfragment_XRF.T_VECTOR3) | xrfragment_XRF.EMBEDDED)
- Frag.h["visible"] = ((((xrfragment_XRF.QUERY_OPERATOR | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.ROUNDROBIN) | xrfragment_XRF.T_INT) | xrfragment_XRF.EMBEDDED)
- Frag.h["env"] = (((xrfragment_XRF.ASSET | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.T_STRING) | xrfragment_XRF.EMBEDDED)
- Frag.h["t"] = (((((xrfragment_XRF.ASSET | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.ROUNDROBIN) | xrfragment_XRF.T_VECTOR2) | xrfragment_XRF.NAVIGATOR) | xrfragment_XRF.EMBEDDED)
- Frag.h["gravity"] = (((xrfragment_XRF.ASSET | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.T_VECTOR3) | xrfragment_XRF.EMBEDDED)
- Frag.h["physics"] = (((xrfragment_XRF.ASSET | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.T_VECTOR3) | xrfragment_XRF.EMBEDDED)
- Frag.h["fov"] = ((((xrfragment_XRF.ASSET | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.T_INT) | xrfragment_XRF.NAVIGATOR) | xrfragment_XRF.EMBEDDED)
- Frag.h["clip"] = ((((xrfragment_XRF.ASSET | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.T_VECTOR2) | xrfragment_XRF.NAVIGATOR) | xrfragment_XRF.EMBEDDED)
- Frag.h["fog"] = ((((xrfragment_XRF.ASSET | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.T_STRING) | xrfragment_XRF.NAVIGATOR) | xrfragment_XRF.EMBEDDED)
- Frag.h["namespace"] = (xrfragment_XRF.ASSET | xrfragment_XRF.T_STRING)
- Frag.h["SPDX"] = (xrfragment_XRF.ASSET | xrfragment_XRF.T_STRING)
- Frag.h["unit"] = (xrfragment_XRF.ASSET | xrfragment_XRF.T_STRING)
- Frag.h["description"] = (xrfragment_XRF.ASSET | xrfragment_XRF.T_STRING)
- Frag.h["session"] = (((((xrfragment_XRF.ASSET | xrfragment_XRF.T_URL) | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.NAVIGATOR) | xrfragment_XRF.EMBEDDED) | xrfragment_XRF.PROMPT)
- if ((len(value) == 0) and (not (key in Frag.h))):
- value1 = xrfragment_XRF(key,(xrfragment_XRF.PV_EXECUTE | xrfragment_XRF.NAVIGATOR))
- setattr(resultMap,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),value1)
- return True
- if ((len(key.split(".")) > 1) and ((len(value.split(".")) > 1))):
- value1 = xrfragment_XRF(key,(((xrfragment_XRF.ASSET | xrfragment_XRF.PV_OVERRIDE) | xrfragment_XRF.T_STRING) | xrfragment_XRF.PROP_BIND))
- setattr(resultMap,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),value1)
- return True
- if (key in Frag.h):
- v = xrfragment_XRF(key,Frag.h.get(key,None))
- if (not v.validate(value)):
- print(str((((("⚠ fragment '" + ("null" if key is None else key)) + "' has incompatible value (") + ("null" if value is None else value)) + ")")))
- return False
- if xrfragment_Parser.debug:
- print(str(((("✔ " + ("null" if key is None else key)) + ": ") + HxOverrides.stringOrNull(v.string))))
- setattr(resultMap,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),v)
- return True
-
-
-class xrfragment_Query:
- _hx_class_name = "xrfragment.Query"
- __slots__ = ("str", "q", "isProp", "isExclude", "isClass", "isNumber")
- _hx_fields = ["str", "q", "isProp", "isExclude", "isClass", "isNumber"]
- _hx_methods = ["toObject", "expandAliases", "get", "parse", "test", "testProperty"]
-
- def __init__(self,_hx_str):
- self.isNumber = EReg("^[0-9\\.]+$","")
- self.isClass = EReg("^[-]?class$","")
- self.isExclude = EReg("^-","")
- self.isProp = EReg("^.*:[><=!]?","")
- self.q = _hx_AnonObject({})
- self.str = ""
- if (_hx_str is not None):
- self.parse(_hx_str)
-
- def toObject(self):
- return self.q
-
- def expandAliases(self,token):
- classAlias = EReg("^(-)?\\.","")
- classAlias.matchObj = python_lib_Re.search(classAlias.pattern,token)
- if (classAlias.matchObj is not None):
- return StringTools.replace(token,".","class:")
- else:
- return token
-
- def get(self):
- return self.q
-
- def parse(self,_hx_str,recurse = None):
- if (recurse is None):
- recurse = False
- _gthis = self
- token = _hx_str.split(" ")
- q = _hx_AnonObject({})
- def _hx_local_0(_hx_str,prefix = None):
- if (prefix is None):
- prefix = ""
- _hx_str = StringTools.trim(_hx_str)
- k = HxOverrides.arrayGet(_hx_str.split(":"), 0)
- v = HxOverrides.arrayGet(_hx_str.split(":"), 1)
- _hx_filter = _hx_AnonObject({})
- if Reflect.field(q,(("null" if prefix is None else prefix) + ("null" if k is None else k))):
- _hx_filter = Reflect.field(q,(("null" if prefix is None else prefix) + ("null" if k is None else k)))
- value = (Reflect.field(_hx_filter,"rules") if ((Reflect.field(_hx_filter,"rules") is not None)) else list())
- setattr(_hx_filter,(("_hx_" + "rules") if (("rules" in python_Boot.keywords)) else (("_hx_" + "rules") if (((((len("rules") > 2) and ((ord("rules"[0]) == 95))) and ((ord("rules"[1]) == 95))) and ((ord("rules"[(len("rules") - 1)]) != 95)))) else "rules")),value)
- _this = _gthis.isProp
- _this.matchObj = python_lib_Re.search(_this.pattern,_hx_str)
- if (_this.matchObj is not None):
- oper = ""
- startIndex = None
- if (((_hx_str.find("*") if ((startIndex is None)) else HxString.indexOfImpl(_hx_str,"*",startIndex))) != -1):
- oper = "*"
- startIndex = None
- if (((_hx_str.find(">") if ((startIndex is None)) else HxString.indexOfImpl(_hx_str,">",startIndex))) != -1):
- oper = ">"
- startIndex = None
- if (((_hx_str.find("<") if ((startIndex is None)) else HxString.indexOfImpl(_hx_str,"<",startIndex))) != -1):
- oper = "<"
- startIndex = None
- if (((_hx_str.find(">=") if ((startIndex is None)) else HxString.indexOfImpl(_hx_str,">=",startIndex))) != -1):
- oper = ">="
- startIndex = None
- if (((_hx_str.find("<=") if ((startIndex is None)) else HxString.indexOfImpl(_hx_str,"<=",startIndex))) != -1):
- oper = "<="
- _this = _gthis.isExclude
- _this.matchObj = python_lib_Re.search(_this.pattern,k)
- if (_this.matchObj is not None):
- oper = "!="
- k = HxString.substr(k,1,None)
- else:
- v = HxString.substr(v,len(oper),None)
- if (len(oper) == 0):
- oper = "="
- _this = _gthis.isClass
- _this.matchObj = python_lib_Re.search(_this.pattern,k)
- if (_this.matchObj is not None):
- key = (("null" if prefix is None else prefix) + ("null" if k is None else k))
- value = (oper != "!=")
- setattr(_hx_filter,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),value)
- setattr(q,(("_hx_" + v) if ((v in python_Boot.keywords)) else (("_hx_" + v) if (((((len(v) > 2) and ((ord(v[0]) == 95))) and ((ord(v[1]) == 95))) and ((ord(v[(len(v) - 1)]) != 95)))) else v)),_hx_filter)
- else:
- rule = _hx_AnonObject({})
- _this = _gthis.isNumber
- _this.matchObj = python_lib_Re.search(_this.pattern,v)
- if (_this.matchObj is not None):
- value = Std.parseFloat(v)
- setattr(rule,(("_hx_" + oper) if ((oper in python_Boot.keywords)) else (("_hx_" + oper) if (((((len(oper) > 2) and ((ord(oper[0]) == 95))) and ((ord(oper[1]) == 95))) and ((ord(oper[(len(oper) - 1)]) != 95)))) else oper)),value)
- else:
- setattr(rule,(("_hx_" + oper) if ((oper in python_Boot.keywords)) else (("_hx_" + oper) if (((((len(oper) > 2) and ((ord(oper[0]) == 95))) and ((ord(oper[1]) == 95))) and ((ord(oper[(len(oper) - 1)]) != 95)))) else oper)),v)
- Reflect.field(Reflect.field(_hx_filter,"rules"),"push")(rule)
- setattr(q,(("_hx_" + k) if ((k in python_Boot.keywords)) else (("_hx_" + k) if (((((len(k) > 2) and ((ord(k[0]) == 95))) and ((ord(k[1]) == 95))) and ((ord(k[(len(k) - 1)]) != 95)))) else k)),_hx_filter)
- return
- else:
- _this = _gthis.isExclude
- _this.matchObj = python_lib_Re.search(_this.pattern,_hx_str)
- value = (False if ((_this.matchObj is not None)) else True)
- setattr(_hx_filter,(("_hx_" + "id") if (("id" in python_Boot.keywords)) else (("_hx_" + "id") if (((((len("id") > 2) and ((ord("id"[0]) == 95))) and ((ord("id"[1]) == 95))) and ((ord("id"[(len("id") - 1)]) != 95)))) else "id")),value)
- _this = _gthis.isExclude
- _this.matchObj = python_lib_Re.search(_this.pattern,_hx_str)
- key = (HxString.substr(_hx_str,1,None) if ((_this.matchObj is not None)) else _hx_str)
- setattr(q,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),_hx_filter)
- process = _hx_local_0
- _g = 0
- _g1 = len(token)
- while (_g < _g1):
- i = _g
- _g = (_g + 1)
- process(self.expandAliases((token[i] if i >= 0 and i < len(token) else None)))
- def _hx_local_2():
- def _hx_local_1():
- self.q = q
- return self.q
- return _hx_local_1()
- return _hx_local_2()
-
- def test(self,obj = None):
- qualify = False
- _g = 0
- _g1 = python_Boot.fields(obj)
- while (_g < len(_g1)):
- k = (_g1[_g] if _g >= 0 and _g < len(_g1) else None)
- _g = (_g + 1)
- v = Std.string(Reflect.field(obj,k))
- if self.testProperty(k,v):
- qualify = True
- _g = 0
- _g1 = python_Boot.fields(obj)
- while (_g < len(_g1)):
- k = (_g1[_g] if _g >= 0 and _g < len(_g1) else None)
- _g = (_g + 1)
- v = Std.string(Reflect.field(obj,k))
- if self.testProperty(k,v,True):
- qualify = False
- return qualify
-
- def testProperty(self,property,value,exclude = None):
- conds = 0
- fails = 0
- qualify = 0
- def _hx_local_2(expr):
-
-
- conds = (conds + 1)
- fails = (fails + (0 if expr else 1))
- return expr
- testprop = _hx_local_2
- if (Reflect.field(self.q,value) is not None):
- v = Reflect.field(self.q,value)
- if (Reflect.field(v,property) is not None):
- return Reflect.field(v,property)
- _g = 0
- _g1 = python_Boot.fields(self.q)
- while (_g < len(_g1)):
- k = (_g1[_g] if _g >= 0 and _g < len(_g1) else None)
- _g = (_g + 1)
- _hx_filter = Reflect.field(self.q,k)
- if (Reflect.field(_hx_filter,"rules") is None):
- continue
- rules = Reflect.field(_hx_filter,"rules")
- _g2 = 0
- while (_g2 < len(rules)):
- rule = (rules[_g2] if _g2 >= 0 and _g2 < len(rules) else None)
- _g2 = (_g2 + 1)
- if exclude:
- if (((Reflect.field(rule,"!=") is not None) and testprop((Std.string(value) == Std.string(Reflect.field(rule,"!="))))) and exclude):
- qualify = (qualify + 1)
- else:
- if ((Reflect.field(rule,"*") is not None) and testprop((Std.parseFloat(value) is not None))):
- qualify = (qualify + 1)
- if ((Reflect.field(rule,">") is not None) and testprop((Std.parseFloat(value) > Std.parseFloat(Reflect.field(rule,">"))))):
- qualify = (qualify + 1)
- if ((Reflect.field(rule,"<") is not None) and testprop((Std.parseFloat(value) < Std.parseFloat(Reflect.field(rule,"<"))))):
- qualify = (qualify + 1)
- if ((Reflect.field(rule,">=") is not None) and testprop((Std.parseFloat(value) >= Std.parseFloat(Reflect.field(rule,">="))))):
- qualify = (qualify + 1)
- if ((Reflect.field(rule,"<=") is not None) and testprop((Std.parseFloat(value) <= Std.parseFloat(Reflect.field(rule,"<="))))):
- qualify = (qualify + 1)
- if ((Reflect.field(rule,"=") is not None) and ((testprop((value == Reflect.field(rule,"="))) or testprop((Std.parseFloat(value) == Std.parseFloat(Reflect.field(rule,"="))))))):
- qualify = (qualify + 1)
- return (qualify > 0)
-
-
-
-class xrfragment_URI:
- _hx_class_name = "xrfragment.URI"
- __slots__ = ()
- _hx_statics = ["parse"]
-
- @staticmethod
- def parse(url,_hx_filter):
- store = _hx_AnonObject({})
- startIndex = None
- if (((url.find("#") if ((startIndex is None)) else HxString.indexOfImpl(url,"#",startIndex))) == -1):
- return store
- fragment = url.split("#")
- _this = (fragment[1] if 1 < len(fragment) else None)
- splitArray = _this.split("&")
- _g = 0
- _g1 = len(splitArray)
- while (_g < _g1):
- i = _g
- _g = (_g + 1)
- _this = (splitArray[i] if i >= 0 and i < len(splitArray) else None)
- splitByEqual = _this.split("=")
- regexPlus = EReg("\\+","g")
- key = (splitByEqual[0] if 0 < len(splitByEqual) else None)
- value = ""
- if (len(splitByEqual) > 1):
- _this1 = regexPlus.split((splitByEqual[1] if 1 < len(splitByEqual) else None))
- value = python_lib_urllib_Parse.unquote(" ".join([python_Boot.toString1(x1,'') for x1 in _this1]))
- ok = xrfragment_Parser.parse(key,value,store)
- if ((_hx_filter is not None) and ((_hx_filter != 0))):
- _g = 0
- _g1 = python_Boot.fields(store)
- while (_g < len(_g1)):
- key = (_g1[_g] if _g >= 0 and _g < len(_g1) else None)
- _g = (_g + 1)
- xrf = Reflect.field(store,key)
- if (not xrf._hx_is(_hx_filter)):
- Reflect.deleteField(store,key)
- return store
-
-
-class xrfragment_XRF:
- _hx_class_name = "xrfragment.XRF"
- __slots__ = ("fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query")
- _hx_fields = ["fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query"]
- _hx_methods = ["is", "validate", "guessType"]
- _hx_statics = ["ASSET", "PROP_BIND", "QUERY_OPERATOR", "PROMPT", "ROUNDROBIN", "NAVIGATOR", "EMBEDDED", "PV_OVERRIDE", "PV_EXECUTE", "T_COLOR", "T_INT", "T_FLOAT", "T_VECTOR2", "T_VECTOR3", "T_URL", "T_PREDEFINED_VIEW", "T_STRING", "T_STRING_OBJ", "T_STRING_OBJ_PROP", "isColor", "isInt", "isFloat", "isVector", "isUrl", "isUrlOrPretypedView", "isString", "set", "unset"]
-
- def __init__(self,_fragment,_flags):
- self.query = None
- self.args = None
- self.float = None
- self.int = None
- self.string = None
- self.color = None
- self.z = None
- self.y = None
- self.x = None
- self.fragment = _fragment
- self.flags = _flags
-
- def _hx_is(self,flag):
- return (((self.flags & flag)) != 0)
-
- def validate(self,value):
- self.guessType(self,value)
- if (len(value.split("|")) > 1):
- self.args = list()
- args = value.split("|")
- _g = 0
- _g1 = len(args)
- while (_g < _g1):
- i = _g
- _g = (_g + 1)
- x = xrfragment_XRF(self.fragment,self.flags)
- self.guessType(x,(args[i] if i >= 0 and i < len(args) else None))
- _this = self.args
- _this.append(x)
- if (self.fragment == "q"):
- self.query = xrfragment_Query(value).get()
- ok = True
- if (not Std.isOfType(self.args,list)):
- if (self._hx_is(xrfragment_XRF.T_VECTOR3) and (not (((Std.isOfType(self.x,Float) and Std.isOfType(self.y,Float)) and Std.isOfType(self.z,Float))))):
- ok = False
- if (self._hx_is(xrfragment_XRF.T_VECTOR2) and (not ((Std.isOfType(self.x,Float) and Std.isOfType(self.y,Float))))):
- ok = False
- if (self._hx_is(xrfragment_XRF.T_INT) and (not Std.isOfType(self.int,Int))):
- ok = False
- return ok
-
- def guessType(self,v,_hx_str):
- v.string = _hx_str
- if (len(_hx_str.split(",")) > 1):
- xyz = _hx_str.split(",")
- if (len(xyz) > 0):
- v.x = Std.parseFloat((xyz[0] if 0 < len(xyz) else None))
- if (len(xyz) > 1):
- v.y = Std.parseFloat((xyz[1] if 1 < len(xyz) else None))
- if (len(xyz) > 2):
- v.z = Std.parseFloat((xyz[2] if 2 < len(xyz) else None))
- _this = xrfragment_XRF.isColor
- _this.matchObj = python_lib_Re.search(_this.pattern,_hx_str)
- if (_this.matchObj is not None):
- v.color = _hx_str
- _this = xrfragment_XRF.isFloat
- _this.matchObj = python_lib_Re.search(_this.pattern,_hx_str)
- if (_this.matchObj is not None):
- v.float = Std.parseFloat(_hx_str)
- _this = xrfragment_XRF.isInt
- _this.matchObj = python_lib_Re.search(_this.pattern,_hx_str)
- if (_this.matchObj is not None):
- v.int = Std.parseInt(_hx_str)
-
- @staticmethod
- def set(flag,flags):
- return (flags | flag)
-
- @staticmethod
- def unset(flag,flags):
- return (flags & ~flag)
-
-
-Math.NEGATIVE_INFINITY = float("-inf")
-Math.POSITIVE_INFINITY = float("inf")
-Math.NaN = float("nan")
-Math.PI = python_lib_Math.pi
-
-python_Boot.keywords = set(["and", "del", "from", "not", "with", "as", "elif", "global", "or", "yield", "assert", "else", "if", "pass", "None", "break", "except", "import", "raise", "True", "class", "exec", "in", "return", "False", "continue", "finally", "is", "try", "def", "for", "lambda", "while"])
-python_Boot.prefixLength = len("_hx_")
-xrfragment_Parser.error = ""
-xrfragment_Parser.debug = False
-xrfragment_XRF.ASSET = 1
-xrfragment_XRF.PROP_BIND = 2
-xrfragment_XRF.QUERY_OPERATOR = 4
-xrfragment_XRF.PROMPT = 8
-xrfragment_XRF.ROUNDROBIN = 16
-xrfragment_XRF.NAVIGATOR = 32
-xrfragment_XRF.EMBEDDED = 64
-xrfragment_XRF.PV_OVERRIDE = 128
-xrfragment_XRF.PV_EXECUTE = 256
-xrfragment_XRF.T_COLOR = 8192
-xrfragment_XRF.T_INT = 16384
-xrfragment_XRF.T_FLOAT = 32768
-xrfragment_XRF.T_VECTOR2 = 65536
-xrfragment_XRF.T_VECTOR3 = 131072
-xrfragment_XRF.T_URL = 262144
-xrfragment_XRF.T_PREDEFINED_VIEW = 524288
-xrfragment_XRF.T_STRING = 1048576
-xrfragment_XRF.T_STRING_OBJ = 2097152
-xrfragment_XRF.T_STRING_OBJ_PROP = 4194304
-xrfragment_XRF.isColor = EReg("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$","")
-xrfragment_XRF.isInt = EReg("^[0-9]+$","")
-xrfragment_XRF.isFloat = EReg("^[0-9]+\\.[0-9]+$","")
-xrfragment_XRF.isVector = EReg("([,]+|\\w)","")
-xrfragment_XRF.isUrl = EReg("(://)?\\..*","")
-xrfragment_XRF.isUrlOrPretypedView = EReg("(^#|://)?\\..*","")
-xrfragment_XRF.isString = EReg(".*","")
\ No newline at end of file
diff --git a/dist/xrfragment.three.js b/dist/xrfragment.three.js
index 5dde412..4b5f013 100644
--- a/dist/xrfragment.three.js
+++ b/dist/xrfragment.three.js
@@ -593,114 +593,317 @@ xrfragment_XRF.isUrlOrPretypedView = new EReg("(^#|://)?\\..*","");
xrfragment_XRF.isString = new EReg(".*","");
})({});
var xrfragment = $hx_exports["xrfragment"];
-xrfragment.xrf = {}
-xrfragment.model = {}
+// wrapper to survive in/outside modules
-xrfragment.init = function(opts){
+xrfragment.InteractiveGroup = function(THREE,renderer,camera){
+
+ let {
+ Group,
+ Matrix4,
+ Raycaster,
+ Vector2
+ } = THREE
+
+ const _pointer = new Vector2();
+ const _event = { type: '', data: _pointer };
+
+ class InteractiveGroup extends Group {
+
+ constructor( renderer, camera ) {
+
+ super();
+
+ if( !renderer || !camera ) return
+
+ const scope = this;
+
+ const raycaster = new Raycaster();
+ const tempMatrix = new Matrix4();
+
+ // Pointer Events
+
+ const element = renderer.domElement;
+
+ function onPointerEvent( event ) {
+
+ //event.stopPropagation();
+
+ const rect = renderer.domElement.getBoundingClientRect();
+
+ _pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
+ _pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
+
+ raycaster.setFromCamera( _pointer, camera );
+
+ const intersects = raycaster.intersectObjects( scope.children, false );
+
+ if ( intersects.length > 0 ) {
+
+ const intersection = intersects[ 0 ];
+
+ const object = intersection.object;
+ const uv = intersection.uv;
+
+ _event.type = event.type;
+ _event.data.set( uv.x, 1 - uv.y );
+
+ object.dispatchEvent( _event );
+
+ }
+
+ }
+
+ element.addEventListener( 'pointerdown', onPointerEvent );
+ element.addEventListener( 'pointerup', onPointerEvent );
+ element.addEventListener( 'pointermove', onPointerEvent );
+ element.addEventListener( 'mousedown', onPointerEvent );
+ element.addEventListener( 'mouseup', onPointerEvent );
+ element.addEventListener( 'mousemove', onPointerEvent );
+ element.addEventListener( 'click', onPointerEvent );
+
+ // WebXR Controller Events
+ // TODO: Dispatch pointerevents too
+
+ const events = {
+ 'move': 'mousemove',
+ 'select': 'click',
+ 'selectstart': 'mousedown',
+ 'selectend': 'mouseup'
+ };
+
+ function onXRControllerEvent( event ) {
+
+ const controller = event.target;
+
+ tempMatrix.identity().extractRotation( controller.matrixWorld );
+
+ raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
+ raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
+
+ const intersections = raycaster.intersectObjects( scope.children, false );
+
+ if ( intersections.length > 0 ) {
+
+ const intersection = intersections[ 0 ];
+
+ const object = intersection.object;
+ const uv = intersection.uv;
+
+ _event.type = events[ event.type ];
+ _event.data.set( uv.x, 1 - uv.y );
+ if( _event.type != "mousemove" ){
+ console.log(event.type+" => "+_event.type)
+ }
+
+ object.dispatchEvent( _event );
+
+ }
+
+ }
+
+ const controller1 = renderer.xr.getController( 0 );
+ controller1.addEventListener( 'move', onXRControllerEvent );
+ controller1.addEventListener( 'select', onXRControllerEvent );
+ controller1.addEventListener( 'selectstart', onXRControllerEvent );
+ controller1.addEventListener( 'selectend', onXRControllerEvent );
+
+ const controller2 = renderer.xr.getController( 1 );
+ controller2.addEventListener( 'move', onXRControllerEvent );
+ controller2.addEventListener( 'select', onXRControllerEvent );
+ controller2.addEventListener( 'selectstart', onXRControllerEvent );
+ controller2.addEventListener( 'selectend', onXRControllerEvent );
+
+ }
+
+ }
+
+ return new InteractiveGroup(renderer,camera)
+}
+let xrf = xrfragment
+xrf.frag = {}
+xrf.model = {}
+
+xrf.init = function(opts){
opts = opts || {}
let XRF = function(){
alert("queries are not implemented (yet)")
}
- for ( let i in opts ) xrfragment[i] = opts[i]
- for ( let i in xrfragment.XRF ) xrfragment.XRF[i] // shortcuts to constants (NAVIGATOR e.g.)
- xrfragment.Parser.debug = xrfragment.debug
- if( opts.loaders ) opts.loaders.map( xrfragment.patchLoader )
- xrfragment.patchRenderer(opts.renderer)
- return xrfragment
+ for ( let i in opts ) xrf[i] = opts[i]
+ for ( let i in xrf.XRF ) xrf.XRF[i] // shortcuts to constants (NAVIGATOR e.g.)
+ xrf.Parser.debug = xrf.debug
+ if( opts.loaders ) Object.values(opts.loaders).map( xrf.patchLoader )
+ xrf.patchRenderer(opts.renderer)
+ xrf.navigate.init()
+ return xrf
}
-xrfragment.patchRenderer = function(renderer){
+xrf.patchRenderer = function(renderer){
+ renderer.xr.addEventListener( 'sessionstart', () => xrf.baseReferenceSpace = renderer.xr.getReferenceSpace() );
+ renderer.xr.enabled = true;
renderer.render = ((render) => function(scene,camera){
- if( xrfragment.getLastModel() && xrfragment.getLastModel().render )
- xrfragment.getLastModel().render(scene,camera)
+ if( xrf.model && xrf.model.render )
+ xrf.model.render(scene,camera)
render(scene,camera)
})(renderer.render.bind(renderer))
}
-xrfragment.patchLoader = function(loader){
+xrf.patchLoader = function(loader){
loader.prototype.load = ((load) => function(url, onLoad, onProgress, onError){
load.call( this,
url,
- (model) => { onLoad(model); xrfragment.parseModel(model,url) },
+ (model) => { onLoad(model); xrf.parseModel(model,url) },
onProgress,
onError)
})(loader.prototype.load)
}
-xrfragment.getFile = (url) => url.split("/").pop().replace(/#.*/,'')
+xrf.getFile = (url) => url.split("/").pop().replace(/#.*/,'')
-xrfragment.parseModel = function(model,url){
- let file = xrfragment.getFile(url)
+xrf.parseModel = function(model,url){
+ let file = xrf.getFile(url)
model.file = file
model.render = function(){}
- xrfragment.model[file] = model
+ model.interactive = xrf.InteractiveGroup( xrf.THREE, xrf.renderer, xrf.camera)
+ model.scene.add(model.interactive)
+
console.log("scanning "+file)
model.scene.traverse( (mesh) => {
- console.log("◎ "+mesh.name)
- if( mesh.userData ){
- let frag = {}
- for( let k in mesh.userData ) xrfragment.Parser.parse( k, mesh.userData[k], frag )
- for( let k in frag ){
- let opts = {frag, mesh, model, camera: xrfragment.camera, scene: xrfragment.scene, renderer: xrfragment.renderer, THREE: xrfragment.THREE }
- xrfragment.evalFragment(k,opts)
- }
- }
+ console.log("◎ "+ (mesh.name||`THREE.${mesh.constructor.name}`))
+ xrf.eval.mesh(mesh,model)
})
}
-xrfragment.evalFragment = (k, opts ) => {
- // call native function (xrf/env.js e.g.), or pass it to user decorator
- let func = xrfragment.xrf[k] || function(){}
- if( xrfragment[k] ) xrfragment[k]( func, opts.frag[k], opts)
- else func( opts.frag[k], opts)
-}
-
-xrfragment.getLastModel = () => Object.values(xrfragment.model)[ Object.values(xrfragment.model).length-1 ]
+xrf.getLastModel = () => xrf.model.last
-xrfragment.eval = function( url, model ){
+xrf.eval = function( url, model ){
let notice = false
- model = model || xrfragment.getLastModel()
- let { THREE, camera } = xrfragment
- let frag = xrfragment.URI.parse( url, xrfragment.XRF.NAVIGATOR )
+ model = model || xrf.model
+ let { THREE, camera } = xrf
+ let frag = xrf.URI.parse( url, xrf.XRF.NAVIGATOR )
let meshes = frag.q ? [] : [camera]
for ( let i in meshes ) {
for ( let k in frag ){
let mesh = meshes[i]
if( !String(k).match(/(pos|rot)/) ) notice = true
- let opts = {frag, mesh, model, camera: xrfragment.camera, scene: xrfragment.scene, renderer: xrfragment.renderer, THREE: xrfragment.THREE }
- xrfragment.evalFragment(k,opts)
+ let opts = {frag, mesh, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
+ xrf.eval.fragment(k,opts)
}
}
if( notice ) alert("only 'pos' and 'rot' XRF.NAVIGATOR-flagged XR fragments are supported (for now)")
}
-xrfragment.xrf.env = function(v, opts){
+
+xrf.eval.mesh = (mesh,model) => {
+ if( mesh.userData ){
+ let frag = {}
+ for( let k in mesh.userData ) xrf.Parser.parse( k, mesh.userData[k], frag )
+ for( let k in frag ){
+ let opts = {frag, mesh, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
+ xrf.eval.fragment(k,opts)
+ }
+ }
+}
+
+xrf.eval.fragment = (k, opts ) => {
+ // call native function (xrf/env.js e.g.), or pass it to user decorator
+ let func = xrf.frag[k] || function(){}
+ if( xrf[k] ) xrf[k]( func, opts.frag[k], opts)
+ else func( opts.frag[k], opts)
+}
+
+xrf.reset = () => {
+ if( !xrf.model.scene ) return
+ xrf.scene.remove( xrf.model.scene )
+ xrf.model.scene.traverse( function(node){
+ if( node instanceof THREE.Mesh ){
+ node.geometry.dispose()
+ node.material.dispose()
+ }
+ })
+}
+
+xrf.navigate = {}
+
+xrf.navigate.to = (url) => {
+ return new Promise( (resolve,reject) => {
+ console.log("xrfragment: navigating to "+url)
+ if( xrf.model && xrf.model.scene ) xrf.model.scene.visible = false
+ const urlObj = new URL( url.match(/:\/\//) ? url : String(`https://fake.com/${url}`).replace(/\/\//,'/') )
+ let dir = url.substring(0, url.lastIndexOf('/') + 1)
+ const file = urlObj.pathname.substring(urlObj.pathname.lastIndexOf('/') + 1);
+ const ext = file.split('.').pop()
+ const Loader = xrf.loaders[ext]
+ if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
+ // force relative path
+ if( dir ) dir = dir[0] == '.' ? dir : `.${dir}`
+ const loader = new Loader().setPath( dir )
+ loader.load( file, function(model){
+ xrf.scene.add( model.scene )
+ xrf.reset()
+ xrf.model = model
+ xrf.navigate.commit( file )
+ resolve(model)
+ })
+ })
+}
+
+xrf.navigate.init = () => {
+ if( xrf.navigate.init.inited ) return
+ window.addEventListener('popstate', function (event){
+ console.dir(event)
+ xrf.navigate.to( document.location.search.substr(1) + document.location.hash )
+ })
+ xrf.navigate.init.inited = true
+}
+
+xrf.navigate.commit = (file) => {
+ window.history.pushState({},null, document.location.pathname + `?${file}${document.location.hash}` )
+}
+xrf.frag.env = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
let env = mesh.getObjectByName(v.string)
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = env.material.map
- scene.texture = env.material.map
+ //scene.texture = env.material.map
renderer.toneMapping = THREE.ACESFilmicToneMapping;
- renderer.toneMappingExposure = 1;
+ renderer.toneMappingExposure = 2;
+ // apply to meshes *DISABLED* renderer.environment does this
+ const maxAnisotropy = renderer.capabilities.getMaxAnisotropy();
+ setTimeout( () => {
+ scene.traverse( (mesh) => {
+ //if (mesh.material && mesh.material.map && mesh.material.metalness == 1.0) {
+ // mesh.material = new THREE.MeshBasicMaterial({ map: mesh.material.map });
+ // mesh.material.dithering = true
+ // mesh.material.map.anisotropy = maxAnisotropy;
+ // mesh.material.needsUpdate = true;
+ //}
+ //if (mesh.material && mesh.material.metalness == 1.0 ){
+ // mesh.material = new THREE.MeshBasicMaterial({
+ // color:0xffffff,
+ // emissive: mesh.material.map,
+ // envMap: env.material.map,
+ // side: THREE.DoubleSide,
+ // flatShading: true
+ // })
+ // mesh.material.needsUpdate = true
+ // //mesh.material.envMap = env.material.map;
+ // //mesh.material.envMap.intensity = 5;
+ // //mesh.material.needsUpdate = true;
+ //}
+ });
+ },500)
console.log(` └ applied image '${v.string}' as environment map`)
}
-xrfragment.xrf.href = function(v, opts){
+xrf.frag.href = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
- let size = 5
let texture = mesh.material.map
+ texture.mapping = THREE.ClampToEdgeWrapping
+ texture.needsUpdate = true
+ mesh.material.dispose()
- /*
- texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
- mesh.material = new THREE.MeshStandardMaterial( {
- envMap: texture,
- roughness: 0.0,
- metalness: 1,
- side: THREE.DoubleSide,
- })
- */
-
+ // poor man's equi-portal
mesh.material = new THREE.ShaderMaterial( {
side: THREE.DoubleSide,
uniforms: {
@@ -729,33 +932,99 @@ xrfragment.xrf.href = function(v, opts){
vec3 direction = normalize(vWorldPosition - cameraPosition);
vec2 sampleUV;
sampleUV.y = -clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);
- sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2 + 0.5;
- gl_FragColor = texture2D(pano, sampleUV);
+ sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2;
+ sampleUV.x += 0.33; // adjust focus to AFRAME's $('a-scene').components.screenshot.capture()
+ vec4 color = texture2D(pano, sampleUV);
+ // Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js)
+ float luminance = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
+ vec4 grayscale_color = vec4(vec3(luminance) + vec3(0.33), color.a);
+ gl_FragColor = grayscale_color;
}
- `
+ `,
});
+ mesh.material.needsUpdate = true
+
+ const handleTeleport = (e) => {
+ if( mesh.clicked ) return
+ this.clicked = true
+ let portalArea = 1 // 1 meter
+ const meshWorldPosition = new THREE.Vector3();
+ meshWorldPosition.setFromMatrixPosition(mesh.matrixWorld);
+
+ const cameraDirection = new THREE.Vector3();
+ camera.getWorldPosition(cameraDirection);
+ cameraDirection.sub(meshWorldPosition);
+ cameraDirection.normalize();
+ cameraDirection.multiplyScalar(portalArea); // move away from portal
+ const newPos = meshWorldPosition.clone().add(cameraDirection);
+
+ const positionInFrontOfPortal = () => {
+ camera.position.copy(newPos);
+ camera.lookAt(meshWorldPosition);
+
+ if( xrf.baseReferenceSpace ){ // WebXR VR/AR roomscale reposition
+ const offsetPosition = { x: -newPos.x, y: 0, z: -newPos.z, w: 1 };
+ const offsetRotation = new THREE.Quaternion();
+ const transform = new XRRigidTransform( offsetPosition, offsetRotation );
+ const teleportSpaceOffset = xrf.baseReferenceSpace.getOffsetReferenceSpace( transform );
+ xrf.renderer.xr.setReferenceSpace( teleportSpaceOffset );
+ }
+
+ document.location.hash = `#pos=${camera.position.x},${camera.position.y},${camera.position.z}`;
+ }
+
+ const distance = camera.position.distanceTo(newPos);
+ if( distance > portalArea ) positionInFrontOfPortal()
+ else xrf.navigate.to(v.string) // ok let's surf to HREF!
+
+ setTimeout( () => mesh.clicked = false, 200 ) // prevent double clicks
+ }
+
+ if( !opts.frag.q ) mesh.addEventListener('click', handleTeleport )
+
+ // lazy remove mesh (because we're inside a traverse)
+ setTimeout( () => {
+ model.interactive.add(mesh) // make clickable
+ },200)
}
-xrfragment.xrf.pos = function(v, opts){
+xrf.frag.pos = function(v, opts){
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
console.log(" └ setting camera position to "+v.string)
camera.position.x = v.x
camera.position.y = v.y
camera.position.z = v.z
}
-xrfragment.xrf.rot = function(v, opts){
+xrf.frag.q = function(v, opts){
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ console.log(" └ running query ")
+ for ( let i in v.query ) {
+ let target = v.query[i]
+
+ // remove objects if requested
+ if( target.id != undefined && (target.mesh = scene.getObjectByName(i)) ){
+ target.mesh.visible = target.id
+ target.mesh.parent.remove(target.mesh)
+ console.log(` └ removing mesh: ${i}`)
+ }else console.log(` └ mesh not found: ${i}`)
+ }
+}
+xrf.frag.rot = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
camera.rotation.x = v.x * Math.PI / 180;
camera.rotation.y = v.y * Math.PI / 180;
camera.rotation.z = v.z * Math.PI / 180;
}
-xrfragment.xrf.src = function(v, opts){
+// *TODO* use webgl instancing
+
+xrf.frag.src = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
if( v.string[0] == "#" ){ // local
console.log(" └ instancing src")
- let args = xrfragment.URI.parse(v.string)
+ let frag = xrfragment.URI.parse(v.string)
// Get an instance of the original model
const modelInstance = new THREE.Group();
- modelInstance.add(model.scene.clone());
+ let sceneInstance = model.scene.clone()
+ modelInstance.add(sceneInstance)
modelInstance.position.z = mesh.position.z
modelInstance.position.y = mesh.position.y
modelInstance.position.x = mesh.position.x
@@ -763,19 +1032,10 @@ xrfragment.xrf.src = function(v, opts){
modelInstance.scale.y = mesh.scale.y
modelInstance.scale.x = mesh.scale.z
// now apply XR Fragments overrides from URI
- // *TODO* move to a central location (pull-up)
- for( var i in args ){
- if( i == "scale" ){
- console.log(" └ setting scale")
- modelInstance.scale.x = args[i].x
- modelInstance.scale.y = args[i].y
- modelInstance.scale.z = args[i].z
- }
- }
+ for( var i in frag )
+ xrf.eval.fragment(i, Object.assign(opts,{frag, model:modelInstance,scene:sceneInstance}))
// Add the instance to the scene
- scene.add(modelInstance);
- console.dir(model)
- console.dir(modelInstance)
+ model.scene.add(modelInstance);
}
}
export default xrfragment;
diff --git a/example/aframe/sandbox/example3.gltf b/example/aframe/sandbox/example3.gltf
new file mode 120000
index 0000000..1f5ae60
--- /dev/null
+++ b/example/aframe/sandbox/example3.gltf
@@ -0,0 +1 @@
+../../assets/example3.gltf
\ No newline at end of file
diff --git a/example/aframe/sandbox/index.html b/example/aframe/sandbox/index.html
index d531820..620042a 100644
--- a/example/aframe/sandbox/index.html
+++ b/example/aframe/sandbox/index.html
@@ -5,10 +5,10 @@
-
+
-
-
+
+
@@ -26,15 +26,14 @@
sourcecode
⬇️ model
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -43,16 +42,32 @@
window.$ = (s) => document.querySelector(s)
if( document.location.search.length > 2 )
- $('[gltf-model]').setAttribute('gltf-model', document.location.search.substr(1) )
+ $('#home').setAttribute('xrf', document.location.search.substr(1) )
$('a-scene').addEventListener('loaded', () => {
setupConsole( $('textarea') )
- // init navigator url
- document.location.hash = $('#uri').value
+ // update url when sandbox-url is updated
window.addEventListener('hashchange', () => {
window.AFRAME.XRF.eval( $('#uri').value = document.location.hash )
})
+ if( document.location.hash.length < 2 ) document.location.hash = $('#uri').value
+
+ // add look-controls at last (otherwise it'll be buggy after scene-updates)
+ $('[camera]').setAttribute("look-controls","")
+ // add screenshot component with camera to capture proper equirects
+ $('a-scene').setAttribute("screenshot",{camera: "[camera]",width: 4096*2, height:2048*2})
+
+ // turn certain query into AFRAME entities
+ // AFRAME.XRF.href = (xrf,v,opts) => {
+ // let {model,mesh} = opts
+ // xrf(v,opts)
+ // // convert to entity
+ // let el = document.createElement("a-entity")
+ // el.setAttribute("gltf-to-entity",{ name: mesh.name})
+ // el.setAttribute("class","collidable")
+ // $('a-scene').appendChild(el)
+ // }
})
@@ -96,14 +96,14 @@
scene,
renderer,
debug: true,
- loaders: [ GLTFLoader, FBXLoader ], // which 3D assets to check for XR fragments?
+ loaders: { gltf: GLTFLoader, fbx: FBXLoader }, // which 3D assets (extensions) to check for XR fragments?
})
// init navigator url
- document.location.hash = $('#uri').value
window.addEventListener('hashchange', () => {
window.XRF.eval( $('#uri').value = document.location.hash )
})
+ if( document.location.hash.length < 2 ) document.location.hash = $('#uri').value
// optional: react/extend/hook into XR fragment
XRF.env = (xrf,v,opts) => {
@@ -127,35 +127,9 @@
window.XRF = XRF // expose to form
- // load 3D asset
- let model;
- const loader = new GLTFLoader().setPath( './../../assets/')
- let loadGLTF = function ( gltf ) {
- if( model ){
- scene.remove(model)
- //model.dispose()
- }
-
- const maxAnisotropy = renderer.capabilities.getMaxAnisotropy();
- function recursivelySetChildrenUnlit(mesh,cb) {
- cb(mesh)
- if (mesh.children) {
- for (var i = 0; i < mesh.children.length; i++) {
- recursivelySetChildrenUnlit(mesh.children[i],cb);
- }
- }
- }
-
- scene.add( model = gltf.scene );
-
- render();
-
- };
-
- let file = document.location.search.length > 2 ? document.location.search.substr(1) : 'example2.gltf'
-
+ let file = document.location.search.length > 2 ? document.location.search.substr(1) : './../../assets/example3.gltf'
$('#model').setAttribute("href","./../../asset/"+file)
- loader.load( file, loadGLTF );
+ XRF.navigate( file )
// setup mouse controls
@@ -171,8 +145,13 @@
//controls.maxPolarAngle = Math.PI / 2;
//controls.target = new THREE.Vector3(0,1.6,0)
- camera.position.set( 0, 4, 15 );
- //controls.update()
+ //let cameraRig = new THREE.Group()
+ //cameraRig.position.set( 0, 1.6, 15 );
+ camera.position.set( 0, 1.6, 15 );
+ //cameraRig.add(camera)
+ //cameraRig.position.set( 0, 4, 15 );
+
+ //controls.update()
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints( [ new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, - 5 ) ] );
@@ -207,21 +186,12 @@
const gui = new GUI( { width: 300 } );
gui.add( parameters, 'env', 0.2, 3.0, 0.1 ).onChange( onChange );
- const group = new InteractiveGroup( renderer, camera );
-
- vrbutton.addEventListener('click', () => {
- // show gui inside VR scene
- gui.domElement.style.visibility = 'hidden';
- scene.add( group );
- })
-
const mesh = new HTMLMesh( gui.domElement );
mesh.position.x = - 0.75;
mesh.position.y = 1.5;
mesh.position.z = 0.3;
mesh.rotation.y = Math.PI / 4;
mesh.scale.setScalar( 2 );
- group.add( mesh );
// Add stats.js
@@ -236,7 +206,14 @@
statsMesh.position.z = 0.3;
statsMesh.rotation.y = Math.PI / 4;
statsMesh.scale.setScalar( 2.5 );
- group.add( statsMesh );
+
+ vrbutton.addEventListener('click', () => {
+ // show gui inside VR scene
+ gui.domElement.style.visibility = 'hidden';
+ XRF.interactive.add( mesh );
+ XRF.interactive.add( statsMesh );
+ scene.add( XRF.interactive );
+ })
let fileLoaders = loadFile({
".gltf": (file) => file.arrayBuffer().then( (data) => loader.parse( data, '', loadGLTF, console.error ) ),
diff --git a/example/threejs/sandbox/other.gltf b/example/threejs/sandbox/other.gltf
new file mode 120000
index 0000000..72026a1
--- /dev/null
+++ b/example/threejs/sandbox/other.gltf
@@ -0,0 +1 @@
+../../assets/other.gltf
\ No newline at end of file
diff --git a/make b/make
index c5b5501..f16f5c8 100755
--- a/make
+++ b/make
@@ -62,6 +62,7 @@ build_js(){
src/3rd/three/*.js \
src/3rd/three/xrf/*.js \
src/3rd/aframe/*.js > dist/xrfragment.aframe.js
+ ls -la dist | grep js
exit $ok
}
diff --git a/src/3rd/aframe/index.js b/src/3rd/aframe/index.js
index 7a2acf1..7985918 100644
--- a/src/3rd/aframe/index.js
+++ b/src/3rd/aframe/index.js
@@ -4,72 +4,72 @@ window.AFRAME.registerComponent('xrf', {
},
init: function () {
if( !AFRAME.XRF ) this.initXRFragments()
- if( !this.rig && this.el.querySelector('[camera]') )
- AFRAME.XRF.rig = this.el
+ if( typeof this.data == "string" ){
+ AFRAME.XRF.navigate.to(this.data)
+ .then( (model) => {
+ let gets = [ ...document.querySelectorAll('[xrf-get]') ]
+ gets.map( (g) => g.emit('update',model) )
+ })
+ }
},
+
initXRFragments: function(){
let aScene = document.querySelector('a-scene')
// enable XR fragments
let XRF = AFRAME.XRF = xrfragment.init({
THREE,
- camera: aScene.camera,
+ camera: aScene.camera,
scene: aScene.object3D,
renderer: aScene.renderer,
debug: true,
- loaders: [ THREE.GLTFLoader ], // which 3D assets to check for XR fragments?
+ loaders: { gltf: THREE.GLTFLoader } // which 3D assets (exts) to check for XR fragments?
})
+ if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare ABOVE entities with xrf-attributes'
- // override the 'pos' XR Fragment so we can translate the camera rig (not the camera itself)
- XRF.pos = (xrf,v,opts) => {
- let { mesh, model, camera, scene, renderer, THREE} = opts
- console.log("!pos")
- camera.parent.parent.position.x = v.x
- camera.parent.parent.position.y = v.y
- camera.parent.parent.position.z = v.z
- // xrf(v,opts) // skip threejs handler
+ // override the camera-related XR Fragments so the camera-rig is affected
+ let camOverride = (xrf,v,opts) => {
+ opts.camera = $('[camera]').object3D //parentElement.object3D
+ xrf(v,opts)
}
+
+ XRF.pos = camOverride
+ XRF.rot = camOverride
+ XRF.href = camOverride
- // override the 'rot' XR Fragment so we can translate the camera rig (not the camera itself)
- XRF.rot = (xrf,v,opts) => {
- let { mesh, model, camera, scene, renderer, THREE} = opts
- camera.parent.parent.rotation.x = v.x * Math.PI / 180;
- camera.parent.parent.rotation.y = v.y * Math.PI / 180;
- camera.parent.parent.rotation.z = v.z * Math.PI / 180;
- // xrf(v,opts) // skip threejs handler
- }
- }
-});
+ },
+})
-AFRAME.registerComponent('gltf-to-entity', {
+window.AFRAME.registerComponent('xrf-get', {
schema: {
- from: {default: '', type: 'selector'},
- name: {default: ''},
- duplicate: {type:'boolean'}
+ name: {type: 'string'},
+ duplicate: {type: 'boolean'}
},
init: function () {
- var el = this.el;
- var data = this.data;
- data.from.addEventListener('model-loaded', evt => {
- var model;
- var subset;
- model = evt.detail.model;
- console.dir(this.data.from)
- subset = model.getObjectByName(data.name);
- if (!subset){
- console.error("Sub-object", data.name, "not found in #"+data.from.id);
+ var el = this.el;
+ var meshname = this.data.name || this.data;
+
+ this.el.addEventListener('update', (evt) => {
+
+ let scene = evt.detail.scene
+ let mesh = scene.getObjectByName(meshname);
+ if (!mesh){
+ console.error("mesh with name '"+meshname+"' not found in model")
return;
}
- if( !this.data.duplicate ) subset.parent.remove(subset)
- let clone = subset.clone()
- ////subset.updateMatrixWorld();
- el.object3D.position.setFromMatrixPosition(data.from.object3D.matrixWorld);
- el.object3D.quaternion.setFromRotationMatrix(data.from.object3D.matrixWorld);
-
- el.setObject3D('mesh', clone );
- el.emit('model-loaded', el.getObject3D('mesh'));
+ if( !this.data.duplicate ) mesh.parent.remove(mesh)
+ if( this.mesh ) this.mesh.parent.remove(this.mesh) // cleanup old clone
+ let clone = this.mesh = mesh.clone()
+ ////mesh.updateMatrixWorld();
+ this.el.object3D.position.setFromMatrixPosition(scene.matrixWorld);
+ this.el.object3D.quaternion.setFromRotationMatrix(scene.matrixWorld);
+ this.el.setObject3D('mesh', clone );
+ if( !this.el.id ) this.el.setAttribute("id",`xrf-${clone.name}`)
+
+ })
- });
}
+
});
+
diff --git a/src/3rd/three/InteractiveGroup.js b/src/3rd/three/InteractiveGroup.js
new file mode 100644
index 0000000..40fd1ad
--- /dev/null
+++ b/src/3rd/three/InteractiveGroup.js
@@ -0,0 +1,126 @@
+// wrapper to survive in/outside modules
+
+xrfragment.InteractiveGroup = function(THREE,renderer,camera){
+
+ let {
+ Group,
+ Matrix4,
+ Raycaster,
+ Vector2
+ } = THREE
+
+ const _pointer = new Vector2();
+ const _event = { type: '', data: _pointer };
+
+ class InteractiveGroup extends Group {
+
+ constructor( renderer, camera ) {
+
+ super();
+
+ if( !renderer || !camera ) return
+
+ const scope = this;
+
+ const raycaster = new Raycaster();
+ const tempMatrix = new Matrix4();
+
+ // Pointer Events
+
+ const element = renderer.domElement;
+
+ function onPointerEvent( event ) {
+
+ //event.stopPropagation();
+
+ const rect = renderer.domElement.getBoundingClientRect();
+
+ _pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
+ _pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
+
+ raycaster.setFromCamera( _pointer, camera );
+
+ const intersects = raycaster.intersectObjects( scope.children, false );
+
+ if ( intersects.length > 0 ) {
+
+ const intersection = intersects[ 0 ];
+
+ const object = intersection.object;
+ const uv = intersection.uv;
+
+ _event.type = event.type;
+ _event.data.set( uv.x, 1 - uv.y );
+
+ object.dispatchEvent( _event );
+
+ }
+
+ }
+
+ element.addEventListener( 'pointerdown', onPointerEvent );
+ element.addEventListener( 'pointerup', onPointerEvent );
+ element.addEventListener( 'pointermove', onPointerEvent );
+ element.addEventListener( 'mousedown', onPointerEvent );
+ element.addEventListener( 'mouseup', onPointerEvent );
+ element.addEventListener( 'mousemove', onPointerEvent );
+ element.addEventListener( 'click', onPointerEvent );
+
+ // WebXR Controller Events
+ // TODO: Dispatch pointerevents too
+
+ const events = {
+ 'move': 'mousemove',
+ 'select': 'click',
+ 'selectstart': 'mousedown',
+ 'selectend': 'mouseup'
+ };
+
+ function onXRControllerEvent( event ) {
+
+ const controller = event.target;
+
+ tempMatrix.identity().extractRotation( controller.matrixWorld );
+
+ raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
+ raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
+
+ const intersections = raycaster.intersectObjects( scope.children, false );
+
+ if ( intersections.length > 0 ) {
+
+ const intersection = intersections[ 0 ];
+
+ const object = intersection.object;
+ const uv = intersection.uv;
+
+ _event.type = events[ event.type ];
+ _event.data.set( uv.x, 1 - uv.y );
+ if( _event.type != "mousemove" ){
+ console.log(event.type+" => "+_event.type)
+ }
+
+ object.dispatchEvent( _event );
+
+ }
+
+ }
+
+ const controller1 = renderer.xr.getController( 0 );
+ controller1.addEventListener( 'move', onXRControllerEvent );
+ controller1.addEventListener( 'select', onXRControllerEvent );
+ controller1.addEventListener( 'selectstart', onXRControllerEvent );
+ controller1.addEventListener( 'selectend', onXRControllerEvent );
+
+ const controller2 = renderer.xr.getController( 1 );
+ controller2.addEventListener( 'move', onXRControllerEvent );
+ controller2.addEventListener( 'select', onXRControllerEvent );
+ controller2.addEventListener( 'selectstart', onXRControllerEvent );
+ controller2.addEventListener( 'selectend', onXRControllerEvent );
+
+ }
+
+ }
+
+ return new InteractiveGroup(renderer,camera)
+}
diff --git a/src/3rd/three/index.js b/src/3rd/three/index.js
index d8b1b31..d21ec35 100644
--- a/src/3rd/three/index.js
+++ b/src/3rd/three/index.js
@@ -1,82 +1,141 @@
-xrfragment.xrf = {}
-xrfragment.model = {}
+let xrf = xrfragment
+xrf.frag = {}
+xrf.model = {}
-xrfragment.init = function(opts){
+xrf.init = function(opts){
opts = opts || {}
let XRF = function(){
alert("queries are not implemented (yet)")
}
- for ( let i in opts ) xrfragment[i] = opts[i]
- for ( let i in xrfragment.XRF ) xrfragment.XRF[i] // shortcuts to constants (NAVIGATOR e.g.)
- xrfragment.Parser.debug = xrfragment.debug
- if( opts.loaders ) opts.loaders.map( xrfragment.patchLoader )
- xrfragment.patchRenderer(opts.renderer)
- return xrfragment
+ for ( let i in opts ) xrf[i] = opts[i]
+ for ( let i in xrf.XRF ) xrf.XRF[i] // shortcuts to constants (NAVIGATOR e.g.)
+ xrf.Parser.debug = xrf.debug
+ if( opts.loaders ) Object.values(opts.loaders).map( xrf.patchLoader )
+ xrf.patchRenderer(opts.renderer)
+ xrf.navigate.init()
+ return xrf
}
-xrfragment.patchRenderer = function(renderer){
+xrf.patchRenderer = function(renderer){
+ renderer.xr.addEventListener( 'sessionstart', () => xrf.baseReferenceSpace = renderer.xr.getReferenceSpace() );
+ renderer.xr.enabled = true;
renderer.render = ((render) => function(scene,camera){
- if( xrfragment.getLastModel() && xrfragment.getLastModel().render )
- xrfragment.getLastModel().render(scene,camera)
+ if( xrf.model && xrf.model.render )
+ xrf.model.render(scene,camera)
render(scene,camera)
})(renderer.render.bind(renderer))
}
-xrfragment.patchLoader = function(loader){
+xrf.patchLoader = function(loader){
loader.prototype.load = ((load) => function(url, onLoad, onProgress, onError){
load.call( this,
url,
- (model) => { onLoad(model); xrfragment.parseModel(model,url) },
+ (model) => { onLoad(model); xrf.parseModel(model,url) },
onProgress,
onError)
})(loader.prototype.load)
}
-xrfragment.getFile = (url) => url.split("/").pop().replace(/#.*/,'')
+xrf.getFile = (url) => url.split("/").pop().replace(/#.*/,'')
-xrfragment.parseModel = function(model,url){
- let file = xrfragment.getFile(url)
+xrf.parseModel = function(model,url){
+ let file = xrf.getFile(url)
model.file = file
model.render = function(){}
- xrfragment.model[file] = model
+ model.interactive = xrf.InteractiveGroup( xrf.THREE, xrf.renderer, xrf.camera)
+ model.scene.add(model.interactive)
+
console.log("scanning "+file)
model.scene.traverse( (mesh) => {
- console.log("◎ "+mesh.name)
- if( mesh.userData ){
- let frag = {}
- for( let k in mesh.userData ) xrfragment.Parser.parse( k, mesh.userData[k], frag )
- for( let k in frag ){
- let opts = {frag, mesh, model, camera: xrfragment.camera, scene: xrfragment.scene, renderer: xrfragment.renderer, THREE: xrfragment.THREE }
- xrfragment.evalFragment(k,opts)
- }
- }
+ console.log("◎ "+ (mesh.name||`THREE.${mesh.constructor.name}`))
+ xrf.eval.mesh(mesh,model)
})
}
-xrfragment.evalFragment = (k, opts ) => {
- // call native function (xrf/env.js e.g.), or pass it to user decorator
- let func = xrfragment.xrf[k] || function(){}
- if( xrfragment[k] ) xrfragment[k]( func, opts.frag[k], opts)
- else func( opts.frag[k], opts)
-}
-
-xrfragment.getLastModel = () => Object.values(xrfragment.model)[ Object.values(xrfragment.model).length-1 ]
+xrf.getLastModel = () => xrf.model.last
-xrfragment.eval = function( url, model ){
+xrf.eval = function( url, model ){
let notice = false
- model = model || xrfragment.getLastModel()
- let { THREE, camera } = xrfragment
- let frag = xrfragment.URI.parse( url, xrfragment.XRF.NAVIGATOR )
+ model = model || xrf.model
+ let { THREE, camera } = xrf
+ let frag = xrf.URI.parse( url, xrf.XRF.NAVIGATOR )
let meshes = frag.q ? [] : [camera]
for ( let i in meshes ) {
for ( let k in frag ){
let mesh = meshes[i]
if( !String(k).match(/(pos|rot)/) ) notice = true
- let opts = {frag, mesh, model, camera: xrfragment.camera, scene: xrfragment.scene, renderer: xrfragment.renderer, THREE: xrfragment.THREE }
- xrfragment.evalFragment(k,opts)
+ let opts = {frag, mesh, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
+ xrf.eval.fragment(k,opts)
}
}
if( notice ) alert("only 'pos' and 'rot' XRF.NAVIGATOR-flagged XR fragments are supported (for now)")
}
+
+xrf.eval.mesh = (mesh,model) => {
+ if( mesh.userData ){
+ let frag = {}
+ for( let k in mesh.userData ) xrf.Parser.parse( k, mesh.userData[k], frag )
+ for( let k in frag ){
+ let opts = {frag, mesh, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
+ xrf.eval.fragment(k,opts)
+ }
+ }
+}
+
+xrf.eval.fragment = (k, opts ) => {
+ // call native function (xrf/env.js e.g.), or pass it to user decorator
+ let func = xrf.frag[k] || function(){}
+ if( xrf[k] ) xrf[k]( func, opts.frag[k], opts)
+ else func( opts.frag[k], opts)
+}
+
+xrf.reset = () => {
+ if( !xrf.model.scene ) return
+ xrf.scene.remove( xrf.model.scene )
+ xrf.model.scene.traverse( function(node){
+ if( node instanceof THREE.Mesh ){
+ node.geometry.dispose()
+ node.material.dispose()
+ }
+ })
+}
+
+xrf.navigate = {}
+
+xrf.navigate.to = (url) => {
+ return new Promise( (resolve,reject) => {
+ console.log("xrfragment: navigating to "+url)
+ if( xrf.model && xrf.model.scene ) xrf.model.scene.visible = false
+ const urlObj = new URL( url.match(/:\/\//) ? url : String(`https://fake.com/${url}`).replace(/\/\//,'/') )
+ let dir = url.substring(0, url.lastIndexOf('/') + 1)
+ const file = urlObj.pathname.substring(urlObj.pathname.lastIndexOf('/') + 1);
+ const ext = file.split('.').pop()
+ const Loader = xrf.loaders[ext]
+ if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
+ // force relative path
+ if( dir ) dir = dir[0] == '.' ? dir : `.${dir}`
+ const loader = new Loader().setPath( dir )
+ loader.load( file, function(model){
+ xrf.scene.add( model.scene )
+ xrf.reset()
+ xrf.model = model
+ xrf.navigate.commit( file )
+ resolve(model)
+ })
+ })
+}
+
+xrf.navigate.init = () => {
+ if( xrf.navigate.init.inited ) return
+ window.addEventListener('popstate', function (event){
+ console.dir(event)
+ xrf.navigate.to( document.location.search.substr(1) + document.location.hash )
+ })
+ xrf.navigate.init.inited = true
+}
+
+xrf.navigate.commit = (file) => {
+ window.history.pushState({},null, document.location.pathname + `?${file}${document.location.hash}` )
+}
diff --git a/src/3rd/three/xrf/env.js b/src/3rd/three/xrf/env.js
index cfb8139..c2a133b 100644
--- a/src/3rd/three/xrf/env.js
+++ b/src/3rd/three/xrf/env.js
@@ -1,10 +1,35 @@
-xrfragment.xrf.env = function(v, opts){
+xrf.frag.env = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
let env = mesh.getObjectByName(v.string)
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = env.material.map
- scene.texture = env.material.map
+ //scene.texture = env.material.map
renderer.toneMapping = THREE.ACESFilmicToneMapping;
- renderer.toneMappingExposure = 1;
+ renderer.toneMappingExposure = 2;
+ // apply to meshes *DISABLED* renderer.environment does this
+ const maxAnisotropy = renderer.capabilities.getMaxAnisotropy();
+ setTimeout( () => {
+ scene.traverse( (mesh) => {
+ //if (mesh.material && mesh.material.map && mesh.material.metalness == 1.0) {
+ // mesh.material = new THREE.MeshBasicMaterial({ map: mesh.material.map });
+ // mesh.material.dithering = true
+ // mesh.material.map.anisotropy = maxAnisotropy;
+ // mesh.material.needsUpdate = true;
+ //}
+ //if (mesh.material && mesh.material.metalness == 1.0 ){
+ // mesh.material = new THREE.MeshBasicMaterial({
+ // color:0xffffff,
+ // emissive: mesh.material.map,
+ // envMap: env.material.map,
+ // side: THREE.DoubleSide,
+ // flatShading: true
+ // })
+ // mesh.material.needsUpdate = true
+ // //mesh.material.envMap = env.material.map;
+ // //mesh.material.envMap.intensity = 5;
+ // //mesh.material.needsUpdate = true;
+ //}
+ });
+ },500)
console.log(` └ applied image '${v.string}' as environment map`)
}
diff --git a/src/3rd/three/xrf/href.js b/src/3rd/three/xrf/href.js
index de796eb..433d814 100644
--- a/src/3rd/three/xrf/href.js
+++ b/src/3rd/three/xrf/href.js
@@ -1,19 +1,12 @@
-xrfragment.xrf.href = function(v, opts){
+xrf.frag.href = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
- let size = 5
let texture = mesh.material.map
+ texture.mapping = THREE.ClampToEdgeWrapping
+ texture.needsUpdate = true
+ mesh.material.dispose()
- /*
- texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
- mesh.material = new THREE.MeshStandardMaterial( {
- envMap: texture,
- roughness: 0.0,
- metalness: 1,
- side: THREE.DoubleSide,
- })
- */
-
+ // poor man's equi-portal
mesh.material = new THREE.ShaderMaterial( {
side: THREE.DoubleSide,
uniforms: {
@@ -42,9 +35,58 @@ xrfragment.xrf.href = function(v, opts){
vec3 direction = normalize(vWorldPosition - cameraPosition);
vec2 sampleUV;
sampleUV.y = -clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);
- sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2 + 0.5;
- gl_FragColor = texture2D(pano, sampleUV);
+ sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2;
+ sampleUV.x += 0.33; // adjust focus to AFRAME's $('a-scene').components.screenshot.capture()
+ vec4 color = texture2D(pano, sampleUV);
+ // Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js)
+ float luminance = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
+ vec4 grayscale_color = vec4(vec3(luminance) + vec3(0.33), color.a);
+ gl_FragColor = grayscale_color;
}
- `
+ `,
});
+ mesh.material.needsUpdate = true
+
+ const handleTeleport = (e) => {
+ if( mesh.clicked ) return
+ this.clicked = true
+ let portalArea = 1 // 1 meter
+ const meshWorldPosition = new THREE.Vector3();
+ meshWorldPosition.setFromMatrixPosition(mesh.matrixWorld);
+
+ const cameraDirection = new THREE.Vector3();
+ camera.getWorldPosition(cameraDirection);
+ cameraDirection.sub(meshWorldPosition);
+ cameraDirection.normalize();
+ cameraDirection.multiplyScalar(portalArea); // move away from portal
+ const newPos = meshWorldPosition.clone().add(cameraDirection);
+
+ const positionInFrontOfPortal = () => {
+ camera.position.copy(newPos);
+ camera.lookAt(meshWorldPosition);
+
+ if( xrf.baseReferenceSpace ){ // WebXR VR/AR roomscale reposition
+ const offsetPosition = { x: -newPos.x, y: 0, z: -newPos.z, w: 1 };
+ const offsetRotation = new THREE.Quaternion();
+ const transform = new XRRigidTransform( offsetPosition, offsetRotation );
+ const teleportSpaceOffset = xrf.baseReferenceSpace.getOffsetReferenceSpace( transform );
+ xrf.renderer.xr.setReferenceSpace( teleportSpaceOffset );
+ }
+
+ document.location.hash = `#pos=${camera.position.x},${camera.position.y},${camera.position.z}`;
+ }
+
+ const distance = camera.position.distanceTo(newPos);
+ if( distance > portalArea ) positionInFrontOfPortal()
+ else xrf.navigate.to(v.string) // ok let's surf to HREF!
+
+ setTimeout( () => mesh.clicked = false, 200 ) // prevent double clicks
+ }
+
+ if( !opts.frag.q ) mesh.addEventListener('click', handleTeleport )
+
+ // lazy remove mesh (because we're inside a traverse)
+ setTimeout( () => {
+ model.interactive.add(mesh) // make clickable
+ },200)
}
diff --git a/src/3rd/three/xrf/pos.js b/src/3rd/three/xrf/pos.js
index 87a804c..9adc006 100644
--- a/src/3rd/three/xrf/pos.js
+++ b/src/3rd/three/xrf/pos.js
@@ -1,4 +1,4 @@
-xrfragment.xrf.pos = function(v, opts){
+xrf.frag.pos = function(v, opts){
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
console.log(" └ setting camera position to "+v.string)
camera.position.x = v.x
diff --git a/src/3rd/three/xrf/q.js b/src/3rd/three/xrf/q.js
new file mode 100644
index 0000000..76e7b14
--- /dev/null
+++ b/src/3rd/three/xrf/q.js
@@ -0,0 +1,14 @@
+xrf.frag.q = function(v, opts){
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ console.log(" └ running query ")
+ for ( let i in v.query ) {
+ let target = v.query[i]
+
+ // remove objects if requested
+ if( target.id != undefined && (target.mesh = scene.getObjectByName(i)) ){
+ target.mesh.visible = target.id
+ target.mesh.parent.remove(target.mesh)
+ console.log(` └ removing mesh: ${i}`)
+ }else console.log(` └ mesh not found: ${i}`)
+ }
+}
diff --git a/src/3rd/three/xrf/rot.js b/src/3rd/three/xrf/rot.js
index 0f56f0a..5747979 100644
--- a/src/3rd/three/xrf/rot.js
+++ b/src/3rd/three/xrf/rot.js
@@ -1,4 +1,4 @@
-xrfragment.xrf.rot = function(v, opts){
+xrf.frag.rot = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
camera.rotation.x = v.x * Math.PI / 180;
camera.rotation.y = v.y * Math.PI / 180;
diff --git a/src/3rd/three/xrf/src.js b/src/3rd/three/xrf/src.js
index 882b831..66b573a 100644
--- a/src/3rd/three/xrf/src.js
+++ b/src/3rd/three/xrf/src.js
@@ -1,11 +1,14 @@
-xrfragment.xrf.src = function(v, opts){
+// *TODO* use webgl instancing
+
+xrf.frag.src = function(v, opts){
let { mesh, model, camera, scene, renderer, THREE} = opts
if( v.string[0] == "#" ){ // local
console.log(" └ instancing src")
- let args = xrfragment.URI.parse(v.string)
+ let frag = xrfragment.URI.parse(v.string)
// Get an instance of the original model
const modelInstance = new THREE.Group();
- modelInstance.add(model.scene.clone());
+ let sceneInstance = model.scene.clone()
+ modelInstance.add(sceneInstance)
modelInstance.position.z = mesh.position.z
modelInstance.position.y = mesh.position.y
modelInstance.position.x = mesh.position.x
@@ -13,18 +16,9 @@ xrfragment.xrf.src = function(v, opts){
modelInstance.scale.y = mesh.scale.y
modelInstance.scale.x = mesh.scale.z
// now apply XR Fragments overrides from URI
- // *TODO* move to a central location (pull-up)
- for( var i in args ){
- if( i == "scale" ){
- console.log(" └ setting scale")
- modelInstance.scale.x = args[i].x
- modelInstance.scale.y = args[i].y
- modelInstance.scale.z = args[i].z
- }
- }
+ for( var i in frag )
+ xrf.eval.fragment(i, Object.assign(opts,{frag, model:modelInstance,scene:sceneInstance}))
// Add the instance to the scene
- scene.add(modelInstance);
- console.dir(model)
- console.dir(modelInstance)
+ model.scene.add(modelInstance);
}
}