Skip to content

Instantly share code, notes, and snippets.

@brianpeiris
Last active December 3, 2018 22:41
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save brianpeiris/f8170581ea9c0de17057f7a96e191c87 to your computer and use it in GitHub Desktop.
Save brianpeiris/f8170581ea9c0de17057f7a96e191c87 to your computer and use it in GitHub Desktop.
Exokit Magic Leap fast feedback
node_modules
package-lock.json

A fast feedback cycle with exokit on the Magic Leap One.

Double-tap the trigger to reload app.js

/* global THREE */
(() => {
let scene, controller, gamepads, terrain;
const raycaster = new THREE.Raycaster();
const updown = new THREE.Vector3(0, -1, 0);
const frontback = new THREE.Vector3(0, 0, -1);
const side = new THREE.Vector3(1, 0, 0);
const casts = [{ dir: updown, offset: "y" }, { dir: frontback, offset: "z" }, { dir: side, offset: "x" }];
const meshParticleGeo = new THREE.BoxBufferGeometry(0.02, 0.02, 0.02);
const meshParticles = [];
function setup(_scene, _terrain, _gamepads) {
scene = _scene;
gamepads = _gamepads;
terrain = _terrain;
controller = new THREE.Mesh(new THREE.SphereBufferGeometry(0.02), new THREE.MeshLambertMaterial());
controller.updateMatrixWorld(true);
controller.matrixAutoUpdate = false;
controller.frustumCulled = false;
scene.add(controller);
}
function addMeshParticle(position) {
let meshParticle;
if (meshParticles.length > 10) {
meshParticle = meshParticles.shift();
} else {
meshParticle = new THREE.Mesh(meshParticleGeo);
meshParticle.frustumCulled = false;
scene.add(meshParticle);
}
meshParticles.push(meshParticle);
meshParticle.position.copy(position);
}
function animate(frame, inputSources) {
const inputSource = inputSources[0];
const pose = frame.getInputPose(inputSource);
controller.matrix.fromArray(pose.pointerMatrix);
controller.matrix.decompose(controller.position, controller.quaternion, controller.scale);
controller.translateZ(-0.6 - gamepads[0].axes[1] / 2);
controller.updateMatrix();
controller.updateMatrixWorld(true);
raycaster.far = 0.05;
for (const meshParticle of meshParticles) {
raycaster.set(meshParticle.position, updown);
if (raycaster.intersectObjects(terrain).length === 0) {
meshParticle.rotation.y += 0.5;
} else {
meshParticle.rotation.y += Math.random() - 0.5;
}
meshParticle.translateZ(0.005);
}
raycaster.far = 0.1;
for (const cast of casts) {
raycaster.set(controller.position, cast.dir);
raycaster.ray.origin[cast.offset] += 0.015;
const intersects = raycaster.intersectObjects(terrain);
if (intersects.length) {
const point = intersects[0].point
point.y += 0.01;
addMeshParticle(intersects[0].point);
}
}
}
window.setup = setup;
window.animate = animate;
})();
<html>
<head>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<script src="https://cdn.rawgit.com/webmixedreality/exokit/v0.0.446/examples/three.js"></script>
<script id="app" src="app.js"></script>
<script src="index.js"></script>
</body>
</html>
/* global THREE, setup, animate */
let container,
renderer,
scene,
camera,
display,
appScene,
terrain = [];
function init() {
container = document.createElement("div");
document.body.appendChild(container);
scene = new THREE.Scene();
appScene = new THREE.Object3D();
scene.add(appScene);
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.lookAt(new THREE.Vector3());
const ambientLight = new THREE.AmbientLight(0x808080);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
scene.add(directionalLight);
initTerrain();
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
}
function initTerrain() {
const terrainMaterial = new THREE.MeshBasicMaterial({
color: 0x000000,
wireframe: false
});
const terrainMeshes = {};
const _getTerrainMesh = id => {
let terrainMesh = terrainMeshes[id];
if (!terrainMesh) {
terrainMesh = _makeTerrainMesh();
terrainMeshes[id] = terrainMesh;
terrain.push(terrainMesh);
scene.add(terrainMesh);
}
return terrainMesh;
};
const _makeTerrainMesh = () => {
const geometry = new THREE.BufferGeometry();
const positions = Float32Array.from([0, 0, 0, 0, 1, 0, 1, 1, 0]);
geometry.addAttribute("position", new THREE.BufferAttribute(positions, 3));
const normals = Float32Array.from([0, 0, 1, 0, 0, 1, 0, 0, 1]);
geometry.addAttribute("normal", new THREE.BufferAttribute(normals, 3));
const indices = Uint32Array.from([0, 1, 2]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
const material = terrainMaterial;
const mesh = new THREE.Mesh(geometry, material);
mesh.frustumCulled = false;
return mesh;
};
const _loadTerrainMesh = (terrainMesh, { positions, normals, indices }) => {
const newPositions = new Float32Array(positions.length);
newPositions.set(positions);
terrainMesh.geometry.addAttribute("position", new THREE.BufferAttribute(newPositions, 3));
const newNormals = new Float32Array(normals.length);
newNormals.set(normals);
terrainMesh.geometry.addAttribute("normal", new THREE.BufferAttribute(newNormals, 3));
const newIndices = new Uint16Array(indices.length);
newIndices.set(indices);
terrainMesh.geometry.setIndex(new THREE.BufferAttribute(newIndices, 1));
};
const _onMesh = updates => {
for (let i = 0; i < updates.length; i++) {
const update = updates[i];
const terrainMesh = _getTerrainMesh(update.id);
_loadTerrainMesh(terrainMesh, update);
}
};
window.browser.nativeMl.RequestMesh(_onMesh);
}
let reloading = false;
function _animate(time, frame) {
const inputSources = display.session.getInputSources();
if (!reloading) {
try {
animate(frame, inputSources);
} catch (e) {
console.log(e);
}
}
renderer.render(scene, renderer.vr.enabled ? renderer.vr.getCamera(camera) : camera);
}
init();
(async () => {
display = await navigator.xr.requestDevice();
const session = await display.requestSession({
exclusive: true
});
display.session = session;
const gamepads = navigator.getGamepads();
let lastSelect = performance.now();
session.onselect = () => {
if (performance.now() - lastSelect < 500) {
console.log("reloading");
appScene.children.slice(0).forEach(child => appScene.remove(child));
document.getElementById("app").remove();
const app = document.createElement("script");
app.id = "app";
reloading = true;
app.addEventListener(
"load",
() => {
console.log("reloaded");
try {
setup(appScene, terrain, gamepads);
reloading = false;
} catch (e) {
console.log(e);
}
},
{ once: true }
);
document.body.appendChild(app);
app.src = `app.js?${Date.now()}`;
}
lastSelect = performance.now();
};
session.requestAnimationFrame((timestamp, frame) => {
renderer.vr.setSession(session, {
frameOfReferenceType: "stage"
});
const viewport = session.baseLayer.getViewport(frame.views[0]);
const width = viewport.width;
const height = viewport.height;
renderer.setSize(width * 2, height);
renderer.setAnimationLoop(null);
renderer.vr.enabled = true;
renderer.vr.setDevice(display);
setup(appScene, terrain, gamepads);
renderer.vr.setAnimationLoop(_animate);
});
})();
{
"description": "A fast feedback cycle in exokit",
"devDependencies": {
"eslint": "^5.4.0",
"prettier": "^1.14.2"
},
"prettier": {
"printWidth": 120
},
"eslintConfig": {
"parserOptions": { "ecmaVersion": 8 },
"env": { "browser": true, "es6": true },
"extends": "eslint:recommended",
"rules": {
"no-console": "off"
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment