Public
Edited
Dec 6, 2022
2 forks
Insert cell
Insert cell
{
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo);

return gl.canvas;
}
Insert cell
Insert cell
Insert cell
indices = cube.scene.children[2].geometry.index.array
Insert cell
positions = cube.scene.children[2].geometry.attributes.position.array//num components 3
Insert cell
normals = cube.scene.children[2].geometry.attributes.normal.array //num components 3
Insert cell
uvs = cube.scene.children[2].geometry.attributes.uv.array //num components 2
Insert cell
skinWeights = cube.scene.children[2].geometry.attributes.skinWeight.array //num components 4
Insert cell
skinIndices = cube.scene.children[2].geometry.attributes.skinIndex.array //num components 4
Insert cell
Insert cell
gl = {
const myCanvas = DOM.canvas(720, 480);
const gl = myCanvas.getContext("webgl2");
gl.enable(gl.DEPTH_TEST);
gl.clearColor(.8,.8,1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
return gl;
}
Insert cell
Insert cell
shaders = ({
vs: `#version 300 es
precision mediump float;
in vec3 position;
in vec3 normal;
in vec4 skinWeights;
in uvec4 skinIndices;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 bones[3];
out vec3 fragNormal;
out vec3 fragPosition;
void main () {
vec4 newPosition =
(
bones[skinIndices[0]] * vec4(position,1.0) * skinWeights[0] +
bones[skinIndices[1]] * vec4(position,1.0) * skinWeights[1] +
bones[skinIndices[2]] * vec4(position,1.0) * skinWeights[2] +
bones[skinIndices[3]] * vec4(position,1.0) * skinWeights[3]
);
fragPosition = newPosition.xyz;
gl_Position = projectionMatrix*viewMatrix*modelMatrix*vec4(position,1);
mat4 normalMatrix = transpose(inverse(modelMatrix));
fragNormal = normalize((normalMatrix*vec4(normal,0)).xyz);

// gl_Position = projectionMatrix * viewMatrix * newPosition;
}`,

fs: `#version 300 es
precision mediump float;
in vec3 fragNormal;
in vec3 fragPosition;

uniform vec4 light;
uniform vec4 material;

out vec4 outColor;
void main () {
vec3 N = normalize(fragNormal);
vec3 L;
if(light.w == 0.0){
L = normalize(light.xyz);
}else{
L = normalize(light.xyz - fragPosition);
}

vec3 color = material.rgb * clamp(dot(N,L), 0.0, 1.0);//Compute color
// vec3 color = vec3(1,1,1);
outColor = vec4(color, material.a);

}`
});
Insert cell
errorBlock = html`<textarea style="height : 20px; width : ${width}px; font-size: 0.8em; display: block"></textarea>`
Insert cell
programInfo = {
errorBlock.style.height = "20px";
errorBlock.innerHTML = "Program Shader compilation successful";
return twgl.createProgramInfo(gl, [shaders.vs, shaders.fs], (message) => {
errorBlock.style.height = "400px";
errorBlock.innerHTML = "Program Shader compilation error\n" + message;
});
}
Insert cell
Insert cell
bufferInfo = twgl.createBufferInfoFromArrays(gl, vertexAttributes);
Insert cell
lookAtPosition = [0,0,0]
Insert cell
eyePosition = [-3.37,6.47,1.29]
Insert cell
fov = 55;
Insert cell
uniforms = ({
light: [1,1,3,0],
projectionMatrix: m4.perspective(fov * Math.PI/180, gl.canvas.width/gl.canvas.height, 0.1,20),
viewMatrix: m4.inverse(m4.lookAt(eyePosition, lookAtPosition, [0,1,0])),
modelMatrix: m4.identity(),
eyePosition: eyePosition,
material: [1, 1, 1, 0.5],
bones: bonesUniform
})
Insert cell
Insert cell
Insert cell
// inverseBindMatrices = cube.scene.children[1].skeleton.boneInverses
inverseBindMatrices = ({invBind00, invBind01, invBind02})
Insert cell
invBind00 = cube.scene.children[2].skeleton.boneInverses[0].elements
Insert cell
invBind01 = cube.scene.children[2].skeleton.boneInverses[1].elements
Insert cell
invBind02 = cube.scene.children[2].skeleton.boneInverses[2].elements
Insert cell
// invBind03 = cube.scene.children[2].skeleton.boneInverses[3].elements
Insert cell
Insert cell
cube = {
const gltfLoader = new THREE.GLTFLoader();
const blob = await FileAttachment("cubeRigNoScale002.glb").blob();
const url = URL.createObjectURL(blob);

const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath("https://www.gstatic.com/draco/v1/decoders/")
gltfLoader.setDRACOLoader( dracoLoader );

const gltf = await new Promise( resolve => gltfLoader.load(url, gltf => resolve(gltf) ));
const cube = gltf;

return cube;
}
Insert cell
cubeBend = {
const gltfLoader = new THREE.GLTFLoader();
const blob = await FileAttachment("cubeBend001.glb").blob();
const url = URL.createObjectURL(blob);
const gltf = await new Promise( resolve => gltfLoader.load(url, gltf => resolve(gltf) ));
const cube = gltf;

return cube;
}
Insert cell
Insert cell
cubeSkeleton = cube.scene.children[2].skeleton
Insert cell
cubeInverseBindMatrices = cube.scene.children[2].skeleton.boneInverses

Insert cell
cubeJointListFlat = {
const cubeJointListFlat = []
const numBones = 3;
for(let i = 0; i < numBones; i++){
const currentJoint = createJoint(cubeSkeleton, cubeInverseBindMatrices, i);
cubeJointListFlat.push(currentJoint);
}
return cubeJointListFlat;
}
Insert cell
Insert cell
bonesUniform = [
m4.identity(),
m4.identity(),
m4.identity()
]
Insert cell
Insert cell
rootJoint = createJointTree(cubeJointListFlat)
Insert cell
Insert cell
function updateWorldMatrices(joint, parentWorldMatrix, boneUniformArray) {
if(parentWorldMatrix == null){
m4.copy(joint.localMatrix, joint.worldMatrix);
// m4.copy(joint.parentWorldMatrix, boneUniformArray[joint.index]);
}else{
m4.multiply(parentWorldMatrix, joint.localMatrix, joint.worldMatrix);
}
//insert calculated worldMatrix * invBindPose into bone uniform array
let boneUniformMatrix = m4.multiply(joint.worldMatrix, joint.inverseBindMatrix, boneUniformArray[joint.index]);

let tempParentWorldMatrix = joint.worldMatrix;
// m4.copy(boneUniformMatrix, boneUniformArray[joint.index]);

//process all children
for(const child of joint.children){
updateWorldMatrices(child, tempParentWorldMatrix, boneUniformArray);
}
}
Insert cell
// updateWorldMatrices(rootJoint, null , bonesUniform);
Insert cell
Insert cell
Insert cell
nodeList = {
let nodeList = []
for(let i = 0; i < cubeJointListFlat.length; i++){
nodeList.push(new Node(cubeJointListFlat[i]))
}
return nodeList
}
Insert cell
Insert cell
rootNode = createJointTree(nodeList)
Insert cell
rootNode.updateWorldMatrix();
Insert cell
bonesUniform
Insert cell
{
m4.copy(rootNode.worldMatrix, bonesUniform[0])
m4.copy(rootNode.children[0].worldMatrix, bonesUniform[1])
m4.copy(rootNode.children[0].children[0].worldMatrix, bonesUniform[2])
}
Insert cell
m4.multiply(rootNode.worldMatrix, rootNode.inverseBindMatrix)
Insert cell
class Node {
constructor(joint) {
for (var fld in joint) {
this[fld] = joint[fld];
}
}
setParent(parent) {
if (this.parent) {
this.parent._removeChild(this);
this.parent = null;
}
if (parent) {
parent._addChild(this);
this.parent = parent.name;
}
}
updateWorldMatrix(parentWorldMatrix) {
// const source = this.source;
// if (source) {
// source.getMatrix(this.localMatrix);
// }
 
if (parentWorldMatrix) {
// a matrix was passed in so do the math
m4.multiply(parentWorldMatrix, this.localMatrix, this.worldMatrix);
} else {
// no matrix was passed in so just copy local to world
m4.copy(this.localMatrix, this.worldMatrix);
}
 
// now process all the children
const worldMatrix = this.worldMatrix;
for (const child of this.children) {
child.updateWorldMatrix(worldMatrix);
}
}
getWorldMatrices(uniformBoneArray) {
m4.copy(this.worldMatrix, uniformBoneArray[this.index]);
for (const child of this.children) {
child.getWorldMatrices(uniformBoneArray);
}
}
traverse(fn) {
fn(this);
for (const child of this.children) {
child.traverse(fn);
}
}
_addChild(child) {
this.children.push(child);
}
_removeChild(child) {
const ndx = this.children.indexOf(child);
this.children.splice(ndx, 1);
}
}
Insert cell
Insert cell
Insert cell
THREE = {
const THREE = window.THREE = await require('three');
await require('three/examples/js/loaders/GLTFLoader.js').catch(() => {});
await require('three/examples/js/loaders/DRACOLoader.js').catch(() => {});
return THREE;
}
Insert cell
twgl = require("twgl.js")
Insert cell
m4 = twgl.m4;
Insert cell
import {node, composeMatrix, composeMatrixQuat} from "@dboyd/scene-graph-functions"
Insert cell
function getParentIndex(joint, jointList)
{
for(let i = 0; i < 3; i++){
if(jointList[i].name == joint.futureParent){
return jointList[i].index;
} else {
return null;
}
}
}

Insert cell
function createJoint(skeleton, inverseBindMatrices, i) {
let parent = null;
if(skeleton.bones[i].parent.type == "Bone"){
parent = skeleton.bones[i].parent.name;
}
let currentJoint = ({
index: i,
name: skeleton.bones[i].name,
futureParent: parent,
parent: null,
children: [],
position:
[
cubeSkeleton.bones[i].position.x,
cubeSkeleton.bones[i].position.y,
cubeSkeleton.bones[i].position.z
],
rotation:
[
cubeSkeleton.bones[i].rotation._x,
cubeSkeleton.bones[i].rotation._y,
cubeSkeleton.bones[i].rotation._z
],
rotationQuat:
[
cubeSkeleton.bones[i].quaternion._x,
cubeSkeleton.bones[i].quaternion._y,
cubeSkeleton.bones[i].quaternion._z,
cubeSkeleton.bones[i].quaternion._w
],
scale: [1,1,1],
localMatrix: m4.identity(),
worldMatrix: m4.identity(),
inverseBindMatrix: cubeInverseBindMatrices[i].elements
})

composeMatrixQuat(currentJoint.position, currentJoint.rotationQuat, currentJoint.scale, currentJoint.localMatrix);

return currentJoint;

}
Insert cell
function createJointTree(inputJointList)
{
let jointList = inputJointList;
for(let i = 0; i < jointList.length; i++){
if(getParentIndex(jointList[i], jointList)){
jointList[getParentIndex(jointList[i], jointList)].children.push(jointList[i])
}
}
return jointList[0]
}
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