const allAnimationDataArray = [];
const _timer = new p5wgex.Timer();
let textureLoaded = false;
const gui = new lil.GUI();
gui.add(config, "mode", {wait:0, run:1, walk:2}).name("action");
testGLTF = loadJSON("https://inaridarkfox4231.github.io/resources/soldier_0.gltf");
createCanvas(window.innerWidth, window.innerHeight, WEBGL);
const result = analyzeGLTF(testGLTF);
registBodyTexture(result);
_node = new p5wgex.RenderNode(this._renderer.GL);
const mu = p5wgex.meshUtil;
g.v = result.obj[k].primitives[0].POSITION.data.slice();
g.n = result.obj[k].primitives[0].NORMAL.data.slice();
g.f = result.obj[k].primitives[0].indices.data.slice();
g.uv = result.obj[k].primitives[0].TEXCOORD_0.data.slice();
g.joint = result.obj[k].primitives[0].JOINTS_0.data.slice();
g.weight = result.obj[k].primitives[0].WEIGHTS_0.data.slice();
mu.regist(_node, g, `soldier_${k}`,{otherAttrs:[
{name:"aWeight", data:g.weight, size:4},
{name:"aJoint", data:g.joint, size:4}
let maxBoneNum = -Infinity;
for(const anim of allAnimationDataArray){
maxBoneNum = Math.max(maxBoneNum, anim.boneNum);
LS = new p5wgex.StandardLightingSystem(_node);
LS.initialize({useTexCoord:true});
LS.addAttr("vec4", "aJoint");
LS.addAttr("vec4", "aWeight");
LS.addUniform("mat4", `uBoneMatrices[${maxBoneNum}]`, "vs");
mat4 skinMat = aWeight.x * uBoneMatrices[int(aJoint.x)];
skinMat += aWeight.y * uBoneMatrices[int(aJoint.y)];
skinMat += aWeight.z * uBoneMatrices[int(aJoint.z)];
skinMat += aWeight.w * uBoneMatrices[int(aJoint.w)];
position = (vec4(position, 1.0) * skinMat).xyz;
normal = (vec4(normal, 0.0) * inverse(transpose(skinMat))).xyz;
LS.addUniform("sampler2D", "uBodyTexture", "fs");
color = texture(uBodyTexture, vTexCoord);
LS.registPainter("skinMeshAnimation");
const cam = new p5wgex.PerspectiveCamera({
eye:[150, 150, 150], center:[0, 0, 70],
top:[0,0,1],near:0.1,far:4000});
const CC = new p5wgex.CameraController(this.canvas,{dblclick:true});
CC.registCamera("cam",cam);LS.setController(CC);
_timer.initialize("clock");
const IR = new foxIA.Inspector(this.canvas,{keydown:true});
console.log("eye:" + v.eye.toArray());
console.log("center" + v.center.toArray());
function registAnimation(result){
for(let h = 0; h < 4;h++){
const frameNum = result.animation[h].transform.frameNum;
const bones = result.skins[0];
const tfData = result.animation[h].transform.data;
const boneNum = bones.length;
const deltaMatricesArray = new Array(frameNum);
const jointMatricesArray = new Array(frameNum);
for(let i=0; i<frameNum; i++){
deltaMatricesArray[i] = [];
jointMatricesArray[i] = [];
for(let i=0; i<boneNum; i++){
const nd = result.nodes[bone.node];
const tf = tfData[bone.node];
for(let k=0; k<frameNum;k++){
_t.push(...(nd.translation !== undefined ? nd.translation : [0,0,0]));
for(let k=0; k<frameNum;k++){
_r.push(...(nd.rotation !== undefined ? nd.rotation : [0,0,0,1]));
for(let k=0; k<frameNum;k++){
_s.push(...(nd.scale !== undefined ? nd.scale : [1,1,1]));
for(let k=0; k<frameNum; k++){
deltaMatricesArray[k].push(createTransform({
t:_t.slice(k*3,(k+1)*3), r:_r.slice(k*4,(k+1)*4), s:_s.slice(k*3,(k+1)*3)
const jointMap = new Array(result.nodes.length);
for(let i=0; i<boneNum; i++){
jointMap[bones[i].node] = i;
for(let i=0; i<boneNum; i++){
const nd = result.nodes[bone.node];
const ibm = bone.ibm.copy().transpose();
for(let k=0; k<frameNum; k++){
const deltaMatrices = deltaMatricesArray[k];
const m = ibm.copy().mult(deltaMatrices[i]);
while(currentNode.parent !== undefined){
const nextNode = result.nodes[currentNode.parent];
const jointIndex = jointMap[currentNode.parent];
if(jointIndex === undefined) break;
m.mult(deltaMatrices[jointIndex]);
jointMatricesArray[k].push(...m.m);
allAnimationDataArray.push({
frameNum, boneNum, jointMatricesArray
function registBodyTexture(result){
const img = result.images[1];
getImgData(img.url).then((res) => {
_node.registTexture("soldierBody", {src:res});
const {frameNum, jointMatricesArray} = allAnimationDataArray[config.mode];
_node.usePainter("skinMeshAnimation");
LS.easyLight({color:"white"});
const phaseIndex = _timer.getElapsedDiscrete("clock", 32, frameNum);
_node.setUniform("uBoneMatrices", jointMatricesArray[phaseIndex]);
_node.setTexture("uBodyTexture", "soldierBody");
_node.drawFigure(`soldier_${k}`);
_node.bindIBO(`soldier_${k}IBO`).drawElements("triangles").unbind();
function analyzeGLTF(data){
result.nodes = data.nodes;
for(let i=0; i<data.nodes.length; i++){
const eachNode = data.nodes[i];
if(eachNode.children === undefined)continue;
for(const child of eachNode.children){
data.nodes[child].parent = i;
for(let i=0; i<data.buffers.length; i++){
const bin = data.buffers[i].uri.split(',')[1];
arrayBuffers.push(getArrayBuffer(bin));
const objData = getObjData({
accessors:data.accessors,
bufferViews:data.bufferViews
if(data.animations !== undefined){
const animationData = getAnimationData({
animations:data.animations,
accessors:data.accessors,
bufferViews:data.bufferViews
result.animation = animationData;
if(data.skins !== undefined){
const skinsData = getSkinsData({
accessors:data.accessors,
bufferViews:data.bufferViews
result.skins = skinsData;
if(data.images !== undefined){
const imageData = getImageData({
bufferViews:data.bufferViews
result.images = imageData;
function getImageData(data){
for(const imgInfo of data.images){
const mime = imgInfo.mimeType;
const name = imgInfo.name;
const bufferView = data.bufferViews[imgInfo.bufferView];
const bytes = createArrayData(data.buf[bufferView.buffer], 5121, bufferView.byteOffset, bufferView.byteLength);
for (let i = 0, len = bytes.byteLength; i < len; i++) {
byteString += String.fromCharCode(bytes[i]);
const base64URL = window.btoa(byteString);
result.push({name, url:`data:${mime};base64,${base64URL}`});
function getSkinsData(data){
for(const skin of data.skins){
const skinAccessor = data.accessors[skin.inverseBindMatrices];
const skinArrayData = getArrayData(
data.buf, skinAccessor, data.bufferViews[skinAccessor.bufferView]
for(let i=0; i<skin.joints.length; i++){
ibm:new p5wgex.Mat4(skinArrayData.slice(i*16, (i+1)*16)),
function getObjData(data){
for (const mesh of data.meshes) {
resultMesh.name = mesh.name;
resultMesh.primitives = [];
for (const prim of mesh.primitives) {
const attrs = prim.attributes;
for (const attrName of Object.keys(attrs)) {
const attrAccessor = data.accessors[attrs[attrName]];
const attrArrayData = getArrayData(
data.buf, attrAccessor, data.bufferViews[attrAccessor.bufferView]
data:attrArrayData, info:getInfo(attrAccessor)
const indexAccessor = data.accessors[prim.indices];
const indexArrayData = getArrayData(
data.buf, indexAccessor, data.bufferViews[indexAccessor.bufferView]
data:indexArrayData, info:getInfo(indexAccessor)
if(prim.targets !== undefined){
for(let i=0; i<prim.targets.length; i++){
const eachTarget = prim.targets[i];
const targetAccessor = data.accessors[eachTarget.POSITION];
const targetPositionData = getArrayData(
data.buf, targetAccessor, data.bufferViews[targetAccessor.bufferView]
targetArray.push(targetPositionData);
primitive.targetPositionData = targetArray;
resultMesh.primitives.push(primitive);
function getAnimationData(data){
for (const animation of data.animations) {
const resultAnimation = {};
resultAnimation.useWeight = false;
resultAnimation.name = animation.name;
resultAnimation.data = [];
for(let k=0; k<animation.samplers.length; k++){
const sampler = animation.samplers[k];
const channel = animation.channels[k];
const resultSampler = {};
resultSampler.interpolation = sampler.interpolation;
const inputAccessor = data.accessors[sampler.input];
const outputAccessor = data.accessors[sampler.output];
const inputArrayData = getArrayData(
data.buf, inputAccessor, data.bufferViews[inputAccessor.bufferView]
const outputArrayData = getArrayData(
data.buf, outputAccessor, data.bufferViews[outputAccessor.bufferView]
data:inputArrayData, info:getInfo(inputAccessor)
data:outputArrayData, info:getInfo(outputAccessor)
resultData.sampler = resultSampler;
resultData.channel = channel;
resultData.node = channel.target.node;
resultData.path = channel.target.path;
if(channel.target.path === "weights"){
resultAnimation.useWeight = true;
const animationFrameCount = resultSampler.input.info.count;
const outputArray = resultSampler.output.data;
const weightNum = Math.floor(outputArray.length/animationFrameCount);
for(let i=0; i<outputArray.length; i+=weightNum){
const eachWeight = new Array(weightNum);
for(let k=0; k<weightNum; k++){
eachWeight[k] = outputArray[i+k];
weightArray.push(eachWeight);
resultData.weights = weightArray;
resultAnimation.data.push(resultData);
if(!resultAnimation.useWeight){
const transformData = [];
let maxFrame = -Infinity;
for(const data of resultAnimation.data){
if(transformData[data.node] === undefined){
transformData[data.node] = {scale:[], rotation:[], translation:[]};
maxFrame = Math.max(maxFrame, Math.round(24*data.sampler.input.info.max[0]));
for(const data of resultAnimation.data){
const normalizedArray = [];
const inputArray = data.sampler.input.data;
const outputArray = data.sampler.output.data;
case "scale": dataSize = 3; break;
case "rotation": dataSize = 4; break;
case "translation": dataSize = 3; break;
const minIndex = Math.round(24*inputArray[0]);
const maxIndex = Math.round(24*inputArray[inputArray.length-1]);
for(let i=0; i<=maxFrame; i++){
normalizedArray.push(...outputArray.slice(0, dataSize));
normalizedArray.push(...outputArray.slice(-dataSize, outputArray.length));
const properIndex = Math.floor(((i-minIndex)/(maxIndex-minIndex))*(inputArray.length-1));
normalizedArray.push(...outputArray.slice(properIndex*dataSize, (properIndex+1)*dataSize));
transformData[data.node][data.path] = normalizedArray;
for(const tf of transformData){
if(tf === undefined) continue;
const modelMatArray = [];
const normalMatArray = [];
for(let k=0; k<maxFrame+1; k++){
const _s = (tf.scale.length === 0 ? [1,1,1] : tf.scale.slice(3*k,3*(k+1)));
const _r = (tf.rotation.length === 0 ? [0,0,0,1] : tf.rotation.slice(4*k,4*(k+1)));
const _t = (tf.translation.length === 0 ? [0,0,0] : tf.translation.slice(3*k,3*(k+1)));
const tfMat = createTransform({s:_s, r:_r, t:_t});
modelMatArray.push(tfMat.m);
normalMatArray.push(p5wgex.getInverseTranspose3x3(tfMat.getMat3()));
tf.modelMats = modelMatArray;
tf.normalMats = normalMatArray;
resultAnimation.transform = {
data:transformData, frameNum: maxFrame+1
result.push(resultAnimation);
function getArrayBuffer(bin){
const byteString = atob(bin);
const byteStringLength = byteString.length;
const arrayBuffer = new ArrayBuffer(byteStringLength);
var intArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteStringLength; i++) {
intArray[i] = byteString.charCodeAt(i);
function getArrayData(buf, accessor, bufferView, arrayOutput = true){
const componentType = accessor.componentType;
const _type = accessor.type;
case 5126: _size = 4; break;
case 5123: _size = 2; break;
case 5121: _size = 1; break;
const byteOffset = bufferView.byteOffset;
const byteLength = bufferView.byteLength;
const arrayLength = byteLength / _size;
const resultArray = createArrayData(buf[bufferView.buffer], componentType, byteOffset, arrayLength);
const outputArray = new Array(resultArray.length);
for(let i=0; i<outputArray.length; i++){
outputArray[i] = resultArray[i];
function createArrayData(buf, componentType, byteOffset, arrayLength) {
const f32Array = new Float32Array(buf, byteOffset, arrayLength);
const i16Array = new Uint16Array(buf, byteOffset, arrayLength);
const u8Array = new Uint8Array(buf, byteOffset, arrayLength);
function getComponentType(code){
case 5126: return "float32";
case 5123: return "uint16";
case 5121: return "uint8";
function getInfo(accessor){
componentType:getComponentType(accessor.componentType)
if (accessor.max !== undefined) { result.max = accessor.max; }
if (accessor.min !== undefined) { result.min = accessor.min; }
async function getJSONData(url){
const response = await fetch(url);
throw new Error(`status:${response.status}`);
const json = response.json();
console.error(error.message);
function loadingResources(){
if(resourcesAreLoaded) return;
resourcesAreLoaded = true;
const url = "https://inaridarkfox4231.github.io/resources/Flamingo.gltf";
getJSONData(url).then((result) => {
function createTransform(tf={}){
const s = createMatFromScale(tf.s);
const r = createMatFromRotation(tf.r);
const t = createMatFromTranslate(tf.t);
return s.mult(r).mult(t);
function createMatFromScale(q){
if(typeof q === 'number'){
}else if(Array.isArray(q)){
function createMatFromRotation(q){
if(typeof q === 'number'){
}else if(Array.isArray(q)){
w*w+x*x-y*y-z*z, 2.0*(x*y-z*w), 2.0*(x*z+y*w), 0.0,
2.0*(x*y+z*w), w*w+y*y-x*x-z*z, 2.0*(y*z-x*w), 0.0,
2.0*(x*z-y*w), 2.0*(y*z+x*w), w*w+z*z-x*x-y*y, 0.0,
function createMatFromTranslate(q){
if(typeof q === 'number'){
}else if(Array.isArray(q)){
async function getImgData(url){
const response = await fetch(url);
throw new Error(`レスポンスステータス: ${response.status}`);
const blob = await response.blob();
const dlurl = URL.createObjectURL(blob)
console.error(error.message);