Skip to content

Instantly share code, notes, and snippets.

@tchaumeny
Last active October 10, 2020 12:17
Show Gist options
  • Save tchaumeny/48307d5692c1c3c63b549f00d37be9ac to your computer and use it in GitHub Desktop.
Save tchaumeny/48307d5692c1c3c63b549f00d37be9ac to your computer and use it in GitHub Desktop.
// RotatingCube React component, used in https://lipsum.dev/2020-09-1-rotations/
import * as BABYLON from 'babylonjs'
import * as GUI from 'babylonjs-gui'
import React, { useEffect, useRef, useState } from 'react'
const canvasWidth = 500;
const canvasHeight = 300;
function initScene(canvas, rotationCb) {
const engine = new BABYLON.Engine(canvas);
const scene = new BABYLON.Scene(engine);
scene.clearColor = BABYLON.Color3.White();
// Camera
const camera = new BABYLON.UniversalCamera("UniversalCamera", new BABYLON.Vector3(1, 1, -2), scene);
camera.setTarget(BABYLON.Vector3.Zero());
// Lights
const hl1 = new BABYLON.HemisphericLight("HemiLight1", new BABYLON.Vector3(0, 1, 0), scene);
const hl2 = new BABYLON.HemisphericLight("HemiLight2", new BABYLON.Vector3(0, 0, -1), scene);
hl1.intensity = hl2.intensity = 0.6;
// Mesh
const faceColors = [
BABYLON.Color3.Blue(),
BABYLON.Color3.Red(),
BABYLON.Color3.Green(),
BABYLON.Color3.Purple(),
BABYLON.Color3.Black(),
BABYLON.Color3.Yellow()
];
const options = {
width: 1,
height: 1,
depth: 1,
faceColors: faceColors
};
const mesh = BABYLON.MeshBuilder.CreateBox("Box", options, scene, true);
mesh.rotationQuaternion = BABYLON.Quaternion.Identity();
rotationCb(mesh.rotationQuaternion);
// GUI
const advancedTexture = GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
const btnSize = 30;
const btnCommon = {
width: `${btnSize-1}px`,
height: `${btnSize-1}px`,
color: "black",
background: "white",
};
[
{
label: "←",
left: Math.floor(canvasWidth / 2) - 3 * btnSize,
top: Math.floor(canvasHeight / 2) - btnSize,
rotAxis: BABYLON.Axis.Y,
rotAngle: Math.PI/2
},
{
label: "↓",
left: Math.floor(canvasWidth / 2) - 2 * btnSize,
top: Math.floor(canvasHeight / 2) - btnSize,
rotAxis: BABYLON.Axis.X,
rotAngle: -Math.PI/2
},
{
label: "→",
left: Math.floor(canvasWidth / 2) - btnSize,
top: Math.floor(canvasHeight / 2) - btnSize,
rotAxis: BABYLON.Axis.Y,
rotAngle: -Math.PI/2
},
{
label: "↑",
left: Math.floor(canvasWidth / 2) - 2 * btnSize,
top: Math.floor(canvasHeight / 2) - 2 * btnSize,
rotAxis: BABYLON.Axis.X,
rotAngle: Math.PI/2
},
].forEach((btnConf, idx) => {
const btn = GUI.Button.CreateSimpleButton(`btn_${idx}`, btnConf.label);
btn.left = `${btnConf.left}px`;
btn.top = `${btnConf.top}px`;
Object.assign(btn, btnCommon);
advancedTexture.addControl(btn);
btn.onPointerClickObservable.add(() => {
animateRotation(BABYLON.Quaternion.RotationAxis(btnConf.rotAxis, btnConf.rotAngle));
})
});
scene.onKeyboardObservable.add((kbInfo) => {
if (kbInfo.type != BABYLON.KeyboardEventTypes.KEYDOWN) return;
switch (kbInfo.event.key) {
case "Left": // IE/Edge specific value
case "ArrowLeft":
animateRotation(BABYLON.Quaternion.RotationAxis(BABYLON.Axis.Y, Math.PI/2));
break;
case "Up": // IE/Edge specific value
case "ArrowUp":
animateRotation(BABYLON.Quaternion.RotationAxis(BABYLON.Axis.X, Math.PI/2));
break;
case "Right": // IE/Edge specific value
case "ArrowRight":
animateRotation(BABYLON.Quaternion.RotationAxis(BABYLON.Axis.Y, -Math.PI/2));
break;
case "Down": // IE/Edge specific value
case "ArrowDown":
animateRotation(BABYLON.Quaternion.RotationAxis(BABYLON.Axis.X, -Math.PI/2));
break;
}
});
var busy = false;
function animateRotation(rotationQ, initQ, targetQ, steps=5, done=0, duration=500) {
if (done === 0 && busy) return;
else if (done === steps) {
rotationCb(mesh.rotationQuaternion);
busy = false;
return;
};
busy = true;
if (!(initQ instanceof BABYLON.Quaternion)) initQ = mesh.rotationQuaternion;
if (!(targetQ instanceof BABYLON.Quaternion)) targetQ = rotationQ.multiply(initQ);
mesh.rotationQuaternion = BABYLON.Quaternion.Slerp(initQ, targetQ, (done + 1) / steps);
setTimeout(animateRotation.bind(null, rotationQ, initQ, targetQ, steps, done + 1, duration), duration / steps);
}
engine.runRenderLoop(function () { scene.render() });
}
const coordLabels = ['w', 'x', 'y', 'z'];
function RotationQuaternion(props) {
function format(v, i) {
return `${coordLabels[i]}: ${(v === null) ? "—" : v.toFixed(2)}`;
}
return <div style={{fontSize: "0.7rem", fontStyle: "italic", textAlign: "center", marginBottom: "1rem"}}>
Quaternion : {props.coords.map(format).join(", ")}
</div>
}
function RotatingCube () {
const canvasRef = useRef(null);
const [coords, setCoords] = useState(coordLabels.map(() => null));
useEffect(() => {
const canvas = canvasRef.current
initScene(canvas, function (q) {
console.log(q);
if (q !== null) setCoords(coordLabels.map(l => q[l]));
});
}, []);
return <div>
<canvas ref={canvasRef} width={canvasWidth} height={canvasHeight}></canvas>
<RotationQuaternion coords={coords} />
</div>
}
export { RotatingCube }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment