Last active
October 10, 2020 12:17
-
-
Save tchaumeny/48307d5692c1c3c63b549f00d37be9ac to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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