Published
Edited
Oct 24, 2020
Insert cell
Insert cell
Insert cell
threelet = {
const threelet = new Threelet({
canvas: document.getElementById('canvas'),
optCameraPosition: [0, 0.7, 2],
optAxes: false,
optVR: true,
optVRAppendButtonTo: div,
})
// https://github.com/observablehq/stdlib#invalidation
invalidation.then(() => threelet.dispose());

threelet.setup('mod-controls', THREE.OrbitControls);
threelet.setup('mod-sky', THREE.Sky);

threelet.scene.add(createTestHemisphereLight());
threelet.scene.add(createTestDirectionalLight());
threelet.scene.add(new THREE.GridHelper(10, 20));

threelet.enableInteractiveGroup('drag');
const group = threelet.getInteractiveGroup();
const testObjs = createTestObjects();
testObjs.forEach(obj => group.add(obj));
threelet.scene.add(group);
group.add(data.base); // make the base interactive in VR
const addAvatarIfNotYet = () => {
if (data.avatar && ! data.avatar.parent) { // first time only
console.log('@@ adding avatar to scene');
threelet.scene.add(data.avatar);
}
};
threelet.on('mouse-click', (x, y) => {
const isec = threelet.raycastFromMouse(x, y, [data.base, ...testObjs], false);
if (isec && isec.object.name === 'base') {
addAvatarIfNotYet();
updateBase();
}
});
threelet.on('vr-trigger-press-start', (i) => {
const isects = threelet.raycastFromController(
i, [data.base, ...testObjs], false);
if (isects.length > 0 && isects[0].object.name === 'base') {
addAvatarIfNotYet();
updateBase();
}
});

threelet.update = (t, dt) => {
const py = 0.4 * Math.sin(t) + 0.5;
data.base.position.y = py;

if (data.mixer !== null) {
data.mixer.update(dt);
data.avatar.position.y = py;
}
};

const fps = 30;
threelet.updateLoop(fps);
threelet.render(); // first time for the case of fps === 0
return threelet;
}
Insert cell
updateBase = () => {
const { base, avatar } = data;

if (! avatar) return; // still loading??

// (re-)spawn
avatar.visible = base.visible = false;
setTimeout(() => {
console.log('@@ hi threre!!');
const [px, pz] = [4*Math.random()-2, 2*Math.random()-4];
base.position.x = px;
base.position.z = pz;
base.rotation.y = Math.atan(px/pz);
avatar.position.set(...base.position.toArray());
avatar.rotation.set(Math.PI/2, Math.PI, base.rotation.y); // kludge

avatar.visible = base.visible = true;
}, 1000);
}
Insert cell
data = {
const base = new THREE.Mesh(
new THREE.BoxGeometry(1.5, 0.1, 1.5),
new THREE.MeshStandardMaterial({
color: Math.random() * 0xffffff,
roughness: 0.7,
metalness: 0.0}));
base.position.set(2, -0.05, -2);
base.rotation.y = -Math.PI/4;
base.name = 'base';

const _data = {base: base, avatar: null, mixer: null};

const loader = new THREE.ColladaLoader();
loader.load('https://w3reality.github.io/threelet/examples/webvr-interactive/media/stormtrooper.dae', collada => {
const animations = collada.animations;
_data.avatar = collada.scene;
console.log('@@ _data.avatar:', _data.avatar);
_data.avatar.traverse(node => {
// console.log('@@ node:', node);
// console.log('@@ node.type:', node.type);
if (node.isSkinnedMesh) {
node.frustumCulled = false;
// node.material.wireframe = true; // @@ debug
}
});
_data.avatar.scale.set(0.4, 0.4, 0.4);
_data.mixer = new THREE.AnimationMixer(_data.avatar);
const action = _data.mixer.clipAction(animations[0]).play();
});
return _data;
}
Insert cell
Insert cell
Insert cell
Insert cell
THREE = {
const THREE = window.THREE = await require("three@0.112/build/three.min.js");
await require("three@0.112/examples/js/controls/OrbitControls.js").catch(() => {});
await require("three@0.112/examples/js/loaders/ColladaLoader.js").catch(() => {});
await require("three@0.112/examples/js/objects/Sky.js").catch(() => {});
return THREE;
}
Insert cell
Threelet = {
const _THREE = THREE; // this ensures THREE is resolved first.
return (await require(`threelet@0.10.0/dist/threelet.esm.min.js`)).default;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more