From 1356a510aaccddab08c6a4d171b82c0dbcddb0eb Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 25 Feb 2019 13:54:51 +0100 Subject: [PATCH 01/27] skinning attributes --- src/primitive.js | 5 +-- src/shaders/primitive.vert | 83 +++++++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/src/primitive.js b/src/primitive.js index 07ad2d62..ffb2677b 100644 --- a/src/primitive.js +++ b/src/primitive.js @@ -63,12 +63,11 @@ class gltfPrimitive extends GltfObject break; case "JOINTS_0": this.defines.push("HAS_JOINTS 1"); - // TODO: implement when we do animations later + this.glAttributes.push({ attribute: attribute, name: "a_Joints", accessor: idx }); break; case "WEIGHTS_0": this.defines.push("HAS_WEIGHTS 1"); - // TODO: implement when we do animations later - + this.glAttributes.push({ attribute: attribute, name: "a_Weights", accessor: idx }); break; default: console.log("Unknown attribute: " + attribute); diff --git a/src/shaders/primitive.vert b/src/shaders/primitive.vert index 2d2243a1..a29d72cd 100644 --- a/src/shaders/primitive.vert +++ b/src/shaders/primitive.vert @@ -17,6 +17,14 @@ varying vec3 v_Normal; #endif #endif +#ifdef HAS_JOINTS +varying vec4 a_Joints; +#endif + +#ifdef HAS_WEIGHTS +varying vec4 a_Weights; +#endif + #ifdef HAS_UV_SET1 attribute vec2 a_UV1; #endif @@ -42,19 +50,84 @@ uniform mat4 u_ViewProjectionMatrix; uniform mat4 u_ModelMatrix; uniform mat4 u_NormalMatrix; +#ifdef USE_SKINNING +uniform mat4 u_jointMatrix[JOINT_COUNT]; +uniform mat4 u_jointNormalMatrix[JOINT_COUNT]; +#endif + +#if defined(USE_SKINNING) && defined(HAS_JOINTS) && defined(HAS_WEIGHTS) +mat4 GetSkinningMatrix() +{ + mat4 skin = + a_Weights.x * u_jointMatrix[int(a_Joints.x)] + + a_Weights.y * u_jointMatrix[int(a_Joints.y)] + + a_Weights.z * u_jointMatrix[int(a_Joints.z)] + + a_Weights.w * u_jointMatrix[int(a_Joints.w)]; + + return skin; +} + +mat4 GetSkinningNormalMatrix() +{ + mat4 skin = + a_Weights.x * u_jointNormalMatrix[int(a_Joints.x)] + + a_Weights.y * u_jointNormalMatrix[int(a_Joints.y)] + + a_Weights.z * u_jointNormalMatrix[int(a_Joints.z)] + + a_Weights.w * u_jointNormalMatrix[int(a_Joints.w)]; + + return skin; +} +#endif + +vec4 getPosition() +{ + vec4 pos = a_Position; + + // TODO: morph before skinning + +#if defined(USE_SKINNING) && defined(HAS_JOINTS) && defined(HAS_WEIGHTS) + pos = GetSkinningMatrix() * pos; +#endif + + return pos; +} + +vec4 getNormal() +{ + vec4 normal = a_Normal; + +#if defined(USE_SKINNING) && defined(HAS_JOINTS) && defined(HAS_WEIGHTS) + normal = GetSkinningNormalMatrix() * normal; +#endif + + return normal; +} + +vec4 getTangent() +{ + vec4 tangent = a_Tangent; + +#if defined(USE_SKINNING) && defined(HAS_JOINTS) && defined(HAS_WEIGHTS) + tangent = GetSkinningNormalMatrix() * tangent; +#endif + + return tangent; +} + void main() { - vec4 pos = u_ModelMatrix * a_Position; + vec4 pos = u_ModelMatrix * getPosition(); v_Position = vec3(pos.xyz) / pos.w; #ifdef HAS_NORMALS #ifdef HAS_TANGENTS - vec3 normalW = normalize(vec3(u_NormalMatrix * vec4(a_Normal.xyz, 0.0))); - vec3 tangentW = normalize(vec3(u_ModelMatrix * vec4(a_Tangent.xyz, 0.0))); - vec3 bitangentW = cross(normalW, tangentW) * a_Tangent.w; + vec4 tangent = getTangent(); + vec3 normalW = normalize(vec3(u_NormalMatrix * vec4(getNormal().xyz, 0.0))); + vec3 tangentW = normalize(vec3(u_ModelMatrix * vec4(tangent.xyz, 0.0))); + vec3 bitangentW = cross(normalW, tangentW) * tangent.w; v_TBN = mat3(tangentW, bitangentW, normalW); #else // !HAS_TANGENTS - v_Normal = normalize(vec3(u_NormalMatrix * vec4(a_Normal.xyz, 0.0))); + v_Normal = normalize(vec3(u_NormalMatrix * vec4(getNormal().xyz, 0.0))); #endif #endif // !HAS_NORMALS From cff50701002f015d5dbd4db7342b5ee2c1ef1660 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 25 Feb 2019 16:20:31 +0100 Subject: [PATCH 02/27] support for 2 joint/weight sets --- src/primitive.js | 16 +++++-- src/renderer.js | 21 +++++++-- src/rendering_parameters.js | 1 + src/shaders/primitive.vert | 94 +++++++++++++++++++++++++++---------- 4 files changed, 100 insertions(+), 32 deletions(-) diff --git a/src/primitive.js b/src/primitive.js index ffb2677b..71cac59e 100644 --- a/src/primitive.js +++ b/src/primitive.js @@ -62,12 +62,20 @@ class gltfPrimitive extends GltfObject } break; case "JOINTS_0": - this.defines.push("HAS_JOINTS 1"); - this.glAttributes.push({ attribute: attribute, name: "a_Joints", accessor: idx }); + this.defines.push("HAS_JOINT_SET1 1"); + this.glAttributes.push({ attribute: attribute, name: "a_Joint1", accessor: idx }); break; case "WEIGHTS_0": - this.defines.push("HAS_WEIGHTS 1"); - this.glAttributes.push({ attribute: attribute, name: "a_Weights", accessor: idx }); + this.defines.push("HAS_WEIGHT_SET1 1"); + this.glAttributes.push({ attribute: attribute, name: "a_Weight1", accessor: idx }); + break; + case "JOINTS_1": + this.defines.push("HAS_JOINT_SET2 1"); + this.glAttributes.push({ attribute: attribute, name: "a_Joint2", accessor: idx }); + break; + case "WEIGHTS_1": + this.defines.push("HAS_WEIGHT_SET2 1"); + this.glAttributes.push({ attribute: attribute, name: "a_Weight2", accessor: idx }); break; default: console.log("Unknown attribute: " + attribute); diff --git a/src/renderer.js b/src/renderer.js index 1e2cd80e..c99c81f3 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -174,11 +174,13 @@ class gltfRenderer //select shader permutation, compile and link program. - let fragDefines = material.getDefines().concat(primitive.getDefines()); - this.pushParameterDefines(fragDefines); + let vertDefines = primitive.getDefines(); + this.pushVertParameterDefines(vertDefines); + let fragDefines = material.getDefines().concat(vertDefines); + this.pushFragParameterDefines(fragDefines); const fragmentHash = this.shaderCache.selectShader(material.getShaderIdentifier(), fragDefines); - const vertexHash = this.shaderCache.selectShader(primitive.getShaderIdentifier(), primitive.getDefines()); + const vertexHash = this.shaderCache.selectShader(primitive.getShaderIdentifier(), vertDefines); if (fragmentHash && vertexHash) { @@ -296,7 +298,18 @@ class gltfRenderer } } - pushParameterDefines(fragDefines) + pushVertParameterDefines(vertDefines) + { + if (this.parameters.playAnimation) + { + // TODO: check for skinning & animations + + // vertDefines.push("USE_SKINNING 1"); + // vertDefines.push("JOINT_COUNT " + node.joints); + } + } + + pushFragParameterDefines(fragDefines) { if (this.parameters.usePunctual) { diff --git a/src/rendering_parameters.js b/src/rendering_parameters.js index c0ffed8e..c36ff892 100644 --- a/src/rendering_parameters.js +++ b/src/rendering_parameters.js @@ -26,6 +26,7 @@ class gltfRenderingParameters this.debugOutput = debugOutput; this.sceneIndex = 0; this.cameraIndex = UserCameraIndex; + this.playAnimation = false; } userCameraActive() diff --git a/src/shaders/primitive.vert b/src/shaders/primitive.vert index a29d72cd..d9a79711 100644 --- a/src/shaders/primitive.vert +++ b/src/shaders/primitive.vert @@ -17,12 +17,20 @@ varying vec3 v_Normal; #endif #endif -#ifdef HAS_JOINTS -varying vec4 a_Joints; +#ifdef HAS_JOINT_SET1 +varying vec4 a_Joint1; #endif -#ifdef HAS_WEIGHTS -varying vec4 a_Weights; +#ifdef HAS_JOINT_SET2 +varying vec4 a_Joint2; +#endif + +#ifdef HAS_WEIGHT_SET1 +varying vec4 a_Weight1; +#endif + +#ifdef HAS_WEIGHT_SET2 +varying vec4 a_Weight2; #endif #ifdef HAS_UV_SET1 @@ -55,29 +63,67 @@ uniform mat4 u_jointMatrix[JOINT_COUNT]; uniform mat4 u_jointNormalMatrix[JOINT_COUNT]; #endif -#if defined(USE_SKINNING) && defined(HAS_JOINTS) && defined(HAS_WEIGHTS) -mat4 GetSkinningMatrix() +#ifdef USE_SKINNING +int getJoint(int index) { - mat4 skin = - a_Weights.x * u_jointMatrix[int(a_Joints.x)] + - a_Weights.y * u_jointMatrix[int(a_Joints.y)] + - a_Weights.z * u_jointMatrix[int(a_Joints.z)] + - a_Weights.w * u_jointMatrix[int(a_Joints.w)]; + int set = index / 4; + int idx = index % 4; + + if(set == 0){ +#ifdef HAS_JOINT_SET1 + return int(a_Joint1[idx]); +#endif + }else if(set == 1){ +#ifdef HAS_JOINT_SET2 + return int(a_Joint2[idx]); +#endif + } + + return 0; +} + +float getWeight(int index) +{ + int set = index / 4; + int idx = index % 4; + + if(set == 0){ +#ifdef HAS_WEIGHT_SET1 + return a_Weight1[idx]; +#endif + }else if(set == 1){ +#ifdef HAS_WEIGHT_SET2 + return a_Weight2[idx]; +#endif + } + + return 0.f; +} + +mat4 getSkinningMatrix() +{ + mat4 skin = mat4(0); + + for(int i = 0; i < JOINT_COUNT; ++i) + { + skin += getWeight(i) * u_jointMatrix[getJoint(i)]; + } return skin; } -mat4 GetSkinningNormalMatrix() +mat4 getSkinningNormalMatrix() { - mat4 skin = - a_Weights.x * u_jointNormalMatrix[int(a_Joints.x)] + - a_Weights.y * u_jointNormalMatrix[int(a_Joints.y)] + - a_Weights.z * u_jointNormalMatrix[int(a_Joints.z)] + - a_Weights.w * u_jointNormalMatrix[int(a_Joints.w)]; + mat4 skin = mat4(0); + + for(int i = 0; i < JOINT_COUNT; ++i) + { + skin += getWeight(i) * u_jointNormalMatrix[getJoint(i)]; + } return skin; } -#endif +#endif // !USE_SKINNING vec4 getPosition() { @@ -85,8 +131,8 @@ vec4 getPosition() // TODO: morph before skinning -#if defined(USE_SKINNING) && defined(HAS_JOINTS) && defined(HAS_WEIGHTS) - pos = GetSkinningMatrix() * pos; +#ifdef USE_SKINNING + pos = getSkinningMatrix() * pos; #endif return pos; @@ -96,8 +142,8 @@ vec4 getNormal() { vec4 normal = a_Normal; -#if defined(USE_SKINNING) && defined(HAS_JOINTS) && defined(HAS_WEIGHTS) - normal = GetSkinningNormalMatrix() * normal; +#ifdef USE_SKINNING + normal = getSkinningNormalMatrix() * normal; #endif return normal; @@ -107,8 +153,8 @@ vec4 getTangent() { vec4 tangent = a_Tangent; -#if defined(USE_SKINNING) && defined(HAS_JOINTS) && defined(HAS_WEIGHTS) - tangent = GetSkinningNormalMatrix() * tangent; +#ifdef USE_SKINNING + tangent = getSkinningMatrix() * tangent; #endif return tangent; From 18719edaeabeaa20b34c90c9861df0f73231a302 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 25 Feb 2019 17:14:18 +0100 Subject: [PATCH 03/27] skin init --- src/gltf.js | 1 + src/skin.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/skin.js diff --git a/src/gltf.js b/src/gltf.js index ebdfb981..4f8fe23c 100644 --- a/src/gltf.js +++ b/src/gltf.js @@ -33,6 +33,7 @@ class glTF extends GltfObject this.buffers = []; this.bufferViews = []; this.materials = []; + this.skins = []; this.path = file; } diff --git a/src/skin.js b/src/skin.js new file mode 100644 index 00000000..3835b33b --- /dev/null +++ b/src/skin.js @@ -0,0 +1,16 @@ +//import { initGlForMembers } from './utils.js'; +//import { WebGl } from './webgl.js'; +import { GltfObject } from './gltf_object.js'; + +class gltfSkin extends GltfObject +{ + constructor() + { + super(); + + this.name = ""; + this.inverseBindMatrices = []; + this.joints = []; + this.skeleton = undefined; + } +} From 92dfb25420c1b2feb7a4f0867b774557533b538e Mon Sep 17 00:00:00 2001 From: David Labode Date: Tue, 26 Feb 2019 14:52:42 +0100 Subject: [PATCH 04/27] added animation classes and interpolator --- src/animation.js | 21 ++++++++ src/animation_sampler.js | 22 +++++++++ src/channel.js | 21 ++++++++ src/interpolater.js | 102 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 src/animation.js create mode 100644 src/animation_sampler.js create mode 100644 src/channel.js create mode 100644 src/interpolater.js diff --git a/src/animation.js b/src/animation.js new file mode 100644 index 00000000..d7f7b538 --- /dev/null +++ b/src/animation.js @@ -0,0 +1,21 @@ +import { GltfObject } from './gltf_object.js'; +import { objectsFromJsons } from './utils.js'; +import { gltfAnimationChannel } from './channel.js'; + +class gltfAnimation extends GltfObject +{ + constructor() + { + super(); + this.channels = []; + this.samplers = []; + } + + fromJson(jsonAnimation) + { + this.channels = objectsFromJsons(jsonAnimation.channels, gltfAnimationChannel); + this.samplers = objectsFromJsons(jsonAnimation.samplers, gltfAnimationSampler); + } +} + +export { gltfAnimation }; diff --git a/src/animation_sampler.js b/src/animation_sampler.js new file mode 100644 index 00000000..9c46cb73 --- /dev/null +++ b/src/animation_sampler.js @@ -0,0 +1,22 @@ +import { GltfObject } from './gltf_object.js'; +import { timingSafeEqual } from 'crypto'; + +class gltfAnimationSampler extends GltfObject +{ + constructor() + { + super(); + this.input = undefined; + this.interpolation = undefined; + this.output = undefined; + } +} + +const InterpolationModes = +{ + LINEAR: "LINEAR", + STEP: "STEP", + CUBICSPLINE: "CUBICSPLINE" +}; + +export { gltfAnimationSampler, InterpolationModes }; diff --git a/src/channel.js b/src/channel.js new file mode 100644 index 00000000..0d53bf68 --- /dev/null +++ b/src/channel.js @@ -0,0 +1,21 @@ +import { GltfObject } from './gltf_object.js'; + +class gltfAnimationChannel extends GltfObject +{ + constructor() + { + super(); + this.target = {node: undefined, path: undefined}; + this.sampler = undefined; + } +} + +const InterpolationPath = +{ + TRANSLATION: "translation", + ROTATION: "rotation", + SCALE: "scale", + WEIGHTS: "weights" +}; + +export { gltfAnimationChannel, InterpolationPath }; diff --git a/src/interpolater.js b/src/interpolater.js new file mode 100644 index 00000000..311ec02f --- /dev/null +++ b/src/interpolater.js @@ -0,0 +1,102 @@ +import { gltfAnimationSampler, InterpolationModes } from './sampler.js'; +import { gltfAnimationChannel, InterpolationPath } from './channel.js'; +import { gltfAccessor } from './accessor.js'; +import { clamp } from './utils.js'; +import { InterpolationPath } from './channel.js'; +import { quat } from 'gl-matrix'; + +class gltfInterpolater +{ + constructor() + { + this.prevKey = 0; + } + + slerpQuat(q1, q2, t) + { + let quatResult = quat.create(); + quat.slerp(quatResult, q1, q2, t); + quat.normalize(quatResult, quatResult); + return quatResult; + } + + linear(a, b, t) + { + return a * (1-t) + b * t; + } + + cubicSpline(prevKey, nextKey, output, keyDelta, t) + { + const prevIndex = prevKey * 3; + const nextIndex = nextKey * 3; + const InTangent = 0; + const Point = 1; + const OutTangent = 2; + + // we assume that the components are layed out like this: in-tangent, point, out-tangent in output + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-c-spline-interpolation + const p0 = output[prevIndex + Point]; + const m0 = keyDelta * output[prevIndex + OutTangent]; + const p1 = output[nextIndex + Point]; + const m1 = keyDelta * output[nextIndex + InTangent]; + + const tSq = t * t; + const tCub = t * t * t; + + return ((2*tCub - 3*tSq + 1) * p0) + ((tCub - 2*tSq + t) * m0) + ((-2*tCub + 3*tSq) * p1) + ((tCub - tSq) * m1); + } + + resetKey() + { + this.prevKey = 0; + } + + interpolate(gltf, channel, sampler, t) + { + const input = gltf.accessors[sampler.input].getTypedView(gltf); + const output = gltf.accessors[sampler.output].getTypedView(gltf); + + if(output.length === 1) // no interpolation for single keyFrame animations + { + return output[0]; + } + + let nextKey = undefined; + const maxKeyTime = input[input.length - 1]; + t = t % maxKeyTime; // loop animation + + for(let i = this.prevKey; i < input.length; ++i) // find current keyframe interval + { + if(t <= input[i]) + { + nextKey = i; + break; + } + } + + if(nextKey === undefined) + { + nextKey = 1; + } + + this.prevKey = clamp(nextKey - 1, 0, nextKey); + + const keyDelta = input[nextKey] - input[this.prevKey]; + t = (t - input[this.prevKey]) / keyDelta; // normalize t to 0..1 + + if(channel.target.path === InterpolationPath.ROTATION) + { + return this.slerpQuat(output[this.prevKey], output[nextKey], t); + } + + switch(sampler.interpolation) + { + case InterpolationModes.STEP: return output[this.prevKey]; // t < 0.5 ? output[preKey] : output[nextKey] + case InterpolationModes.CUBICSPLINE: + { + return this.cubicSpline(this.prevKey, nextKey, output, keyDelta, t); + } + default: return this.linear(output[this.prevKey], output[nextKey], t); + } + } +} From 712e69ef2cb7a554ce7fc51cb5092196fd9f0bef Mon Sep 17 00:00:00 2001 From: David Labode Date: Tue, 26 Feb 2019 15:06:02 +0100 Subject: [PATCH 05/27] wip animation --- src/gltf.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gltf.js b/src/gltf.js index 4f8fe23c..b1bb3caa 100644 --- a/src/gltf.js +++ b/src/gltf.js @@ -13,6 +13,7 @@ import { gltfTexture } from './texture.js'; import { initGlForMembers, objectsFromJsons, objectFromJson } from './utils'; import { gltfAsset } from './asset.js'; import { GltfObject } from './gltf_object.js'; +import { gltfAnimation } from './animation.js'; class glTF extends GltfObject { @@ -33,6 +34,7 @@ class glTF extends GltfObject this.buffers = []; this.bufferViews = []; this.materials = []; + this.animations = []; this.skins = []; this.path = file; } @@ -59,6 +61,7 @@ class glTF extends GltfObject this.nodes = objectsFromJsons(json.nodes, gltfNode); this.lights = objectsFromJsons(getJsonLightsFromExtensions(json.extensions), gltfLight); this.images = objectsFromJsons(json.images, gltfImage); + this.animations = objectsFromJsons(json.animations, gltfAnimation); this.materials.push(gltfMaterial.createDefault()); this.samplers.push(gltfSampler.createDefault()); From e9fd59c713b6f6121a160bcf26c693ccc63531e5 Mon Sep 17 00:00:00 2001 From: David Labode Date: Tue, 26 Feb 2019 17:46:38 +0100 Subject: [PATCH 06/27] wip node animation --- src/animation.js | 41 ++++++++++++- src/animation_sampler.js | 1 - src/interpolater.js | 102 -------------------------------- src/interpolator.js | 123 +++++++++++++++++++++++++++++++++++++++ src/user_interface.js | 7 +++ src/utils.js | 15 ++++- src/viewer.js | 14 +++++ 7 files changed, 198 insertions(+), 105 deletions(-) delete mode 100644 src/interpolater.js create mode 100644 src/interpolator.js diff --git a/src/animation.js b/src/animation.js index d7f7b538..f7eb55b1 100644 --- a/src/animation.js +++ b/src/animation.js @@ -1,6 +1,8 @@ import { GltfObject } from './gltf_object.js'; import { objectsFromJsons } from './utils.js'; -import { gltfAnimationChannel } from './channel.js'; +import { gltfAnimationChannel, InterpolationPath } from './channel.js'; +import { gltfAnimationSampler } from './animation_sampler.js'; +import { gltfInterpolator } from './interpolator.js'; class gltfAnimation extends GltfObject { @@ -9,12 +11,49 @@ class gltfAnimation extends GltfObject super(); this.channels = []; this.samplers = []; + + // not gltf + this.interpolators = []; } fromJson(jsonAnimation) { this.channels = objectsFromJsons(jsonAnimation.channels, gltfAnimationChannel); this.samplers = objectsFromJsons(jsonAnimation.samplers, gltfAnimationSampler); + + for(let i = 0; i < this.channels.length; ++i) + { + this.interpolators.push(new gltfInterpolator()); + } + } + + advance(gltf, totalTime) + { + for(let i = 0; i < this.interpolators.length; ++i) + { + const channel = this.channels[i]; + const sampler = this.samplers[channel.sampler]; + const interpolator = this.interpolators[i]; + + const node = gltf.nodes[channel.target.node]; + + switch(channel.target.path) + { + case InterpolationPath.TRANSLATION: + node.translate(interpolator.interpolate(gltf, channel, sampler, totalTime, 3)); + break; + case InterpolationPath.ROTATION: + node.rotate(interpolator.interpolate(gltf, channel, sampler, totalTime, 4)); + break; + case InterpolationPath.SCALE: + node.scale(interpolator.interpolate(gltf, channel, sampler, totalTime, 3)); + break; + case InterpolationPath.WEIGHTS: + let mesh = gltf.meshes[node.mesh]; + mesh.weights = interpolator.interpolate(gltf, channel, sampler, totalTime, mesh.weights.length); + break; + } + } } } diff --git a/src/animation_sampler.js b/src/animation_sampler.js index 9c46cb73..f3414b7a 100644 --- a/src/animation_sampler.js +++ b/src/animation_sampler.js @@ -1,5 +1,4 @@ import { GltfObject } from './gltf_object.js'; -import { timingSafeEqual } from 'crypto'; class gltfAnimationSampler extends GltfObject { diff --git a/src/interpolater.js b/src/interpolater.js deleted file mode 100644 index 311ec02f..00000000 --- a/src/interpolater.js +++ /dev/null @@ -1,102 +0,0 @@ -import { gltfAnimationSampler, InterpolationModes } from './sampler.js'; -import { gltfAnimationChannel, InterpolationPath } from './channel.js'; -import { gltfAccessor } from './accessor.js'; -import { clamp } from './utils.js'; -import { InterpolationPath } from './channel.js'; -import { quat } from 'gl-matrix'; - -class gltfInterpolater -{ - constructor() - { - this.prevKey = 0; - } - - slerpQuat(q1, q2, t) - { - let quatResult = quat.create(); - quat.slerp(quatResult, q1, q2, t); - quat.normalize(quatResult, quatResult); - return quatResult; - } - - linear(a, b, t) - { - return a * (1-t) + b * t; - } - - cubicSpline(prevKey, nextKey, output, keyDelta, t) - { - const prevIndex = prevKey * 3; - const nextIndex = nextKey * 3; - const InTangent = 0; - const Point = 1; - const OutTangent = 2; - - // we assume that the components are layed out like this: in-tangent, point, out-tangent in output - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-c-spline-interpolation - const p0 = output[prevIndex + Point]; - const m0 = keyDelta * output[prevIndex + OutTangent]; - const p1 = output[nextIndex + Point]; - const m1 = keyDelta * output[nextIndex + InTangent]; - - const tSq = t * t; - const tCub = t * t * t; - - return ((2*tCub - 3*tSq + 1) * p0) + ((tCub - 2*tSq + t) * m0) + ((-2*tCub + 3*tSq) * p1) + ((tCub - tSq) * m1); - } - - resetKey() - { - this.prevKey = 0; - } - - interpolate(gltf, channel, sampler, t) - { - const input = gltf.accessors[sampler.input].getTypedView(gltf); - const output = gltf.accessors[sampler.output].getTypedView(gltf); - - if(output.length === 1) // no interpolation for single keyFrame animations - { - return output[0]; - } - - let nextKey = undefined; - const maxKeyTime = input[input.length - 1]; - t = t % maxKeyTime; // loop animation - - for(let i = this.prevKey; i < input.length; ++i) // find current keyframe interval - { - if(t <= input[i]) - { - nextKey = i; - break; - } - } - - if(nextKey === undefined) - { - nextKey = 1; - } - - this.prevKey = clamp(nextKey - 1, 0, nextKey); - - const keyDelta = input[nextKey] - input[this.prevKey]; - t = (t - input[this.prevKey]) / keyDelta; // normalize t to 0..1 - - if(channel.target.path === InterpolationPath.ROTATION) - { - return this.slerpQuat(output[this.prevKey], output[nextKey], t); - } - - switch(sampler.interpolation) - { - case InterpolationModes.STEP: return output[this.prevKey]; // t < 0.5 ? output[preKey] : output[nextKey] - case InterpolationModes.CUBICSPLINE: - { - return this.cubicSpline(this.prevKey, nextKey, output, keyDelta, t); - } - default: return this.linear(output[this.prevKey], output[nextKey], t); - } - } -} diff --git a/src/interpolator.js b/src/interpolator.js new file mode 100644 index 00000000..b8a8a9a6 --- /dev/null +++ b/src/interpolator.js @@ -0,0 +1,123 @@ +import { gltfAnimationSampler, InterpolationModes } from './animation_sampler.js'; +import { gltfAnimationChannel, InterpolationPath } from './channel.js'; +import { gltfAccessor } from './accessor.js'; +import { clamp, jsToGlSlice } from './utils.js'; +import { quat, vec3 } from 'gl-matrix'; + +class gltfInterpolator +{ + constructor() + { + this.prevKey = 0; + } + + slerpQuat(q1, q2, t) + { + let quatResult = quat.create(); + quat.slerp(quatResult, q1, q2, t); + quat.normalize(quatResult, quatResult); + return quatResult; + } + + linear(prevKey, nextKey, output, t, stride) + { + const result = new glMatrix.ARRAY_TYPE(stride); + + for(let i = 0; i < stride; ++i) + { + result[i] = output[prevKey * stride + i] * (1-t) + output[nextKey * stride + i] * t; + } + + return result; + } + + cubicSpline(prevKey, nextKey, output, keyDelta, t, stride) + { + const prevIndex = prevKey * stride; + const nextIndex = nextKey * stride; + const A = 0; + const V = 1; + const B = 2; + + let result = new glMatrix.ARRAY_TYPE(stride); + const tSq = t * t; + const tCub = t * t * t; + + // we assume that the components are layed out like this: in-tangent, point, out-tangent in output + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-c-spline-interpolation + for(let i = 0; i < stride; ++i) + { + const p0 = output[prevIndex + V + i]; + const m0 = keyDelta * output[prevIndex + B + i]; + const p1 = output[nextIndex + V + i]; + const m1 = keyDelta * output[nextIndex + A + i]; + + result[i] = ((2*tCub - 3*tSq + 1) * p0) + ((tCub - 2*tSq + t) * m0) + ((-2*tCub + 3*tSq) * p1) + ((tCub - tSq) * m1); + } + + return result; + } + + resetKey() + { + this.prevKey = 0; + } + + interpolate(gltf, channel, sampler, t, stride) + { + const input = gltf.accessors[sampler.input].getTypedView(gltf); + const output = gltf.accessors[sampler.output].getTypedView(gltf); + + if(output.length === 1) // no interpolation for single keyFrame animations + { + return output[0]; + } + + let nextKey = undefined; + const maxKeyTime = input[input.length - 1]; + t = t % maxKeyTime; // loop animation + + for(let i = this.prevKey; i < input.length; ++i) // find current keyframe interval + { + if(t <= input[i]) + { + nextKey = i; + break; + } + } + + if(nextKey === undefined) + { + nextKey = 1; + } + + this.prevKey = clamp(nextKey - 1, 0, nextKey); + + const keyDelta = input[nextKey] - input[this.prevKey]; + t = (t - input[this.prevKey]) / keyDelta; // normalize t to 0..1 + + if(channel.target.path === InterpolationPath.ROTATION) + { + return this.slerpQuat(this.getQuat(output, this.prevKey), this.getQuat(output, nextKey), t); + } + + switch(sampler.interpolation) + { + case InterpolationModes.STEP: return jsToGlSlice(output, this.prevKey * stride, stride); // t < 0.5 ? output[preKey] : output[nextKey] + case InterpolationModes.CUBICSPLINE: return this.cubicSpline(this.prevKey, nextKey, output, keyDelta, t, stride); + default: return this.linear(this.prevKey, nextKey, output, t, stride); + } + } + + getQuat(output, index) + { + return quat.fromValues(output[4 * index], output[4 * index + 1], output[4 * index + 2], output[4 * index + 3]); + } + + // getVec3(output, index) + // { + // return vec3.fromValues(output[3 * index], output[3 * index + 1], output[3 * index + 2]); + // } +} + +export { gltfInterpolator }; diff --git a/src/user_interface.js b/src/user_interface.js index 3ade5bf6..94c7f95a 100644 --- a/src/user_interface.js +++ b/src/user_interface.js @@ -53,6 +53,7 @@ class gltfUserInterface this.initializeGltfVersionView(""); this.initializeSceneSelection([]); this.initializeCameraSelection([]); + this.initializeAnimationSettings(); this.gltfFolder.open(); } @@ -118,6 +119,12 @@ class gltfUserInterface .onChange(() => self.renderingParameters.clearColor = self.fromHexColor(self.hexColor)); } + initializeAnimationSettings() + { + const animationFolder = this.gui.addFolder("Animation"); + animationFolder.add(this.renderingParameters, "playAnimation").name("Play"); + } + initializeDebugSettings() { const debugFolder = this.gui.addFolder("Debug"); diff --git a/src/utils.js b/src/utils.js index 11ef8adf..7126ae64 100644 --- a/src/utils.js +++ b/src/utils.js @@ -12,6 +12,18 @@ function jsToGl(array) return tensor; } +function jsToGlSlice(array, offset, stride) +{ + let tensor = new glMatrix.ARRAY_TYPE(stride); + + for (let i = 0; i < stride; ++i) + { + tensor[i] = array[offset + i]; + } + + return tensor; +} + function initGlForMembers(gltfObj, gltf) { for (const name of Object.keys(gltfObj)) @@ -30,7 +42,7 @@ function initGlForMembers(gltfObj, gltf) { for (const element of member) { - if (element.initGl !== undefined) + if (element !== undefined && element.initGl !== undefined) { element.initGl(gltf); } @@ -179,6 +191,7 @@ class Timer export { jsToGl, + jsToGlSlice, objectsFromJsons, objectFromJson, fromKeys, diff --git a/src/viewer.js b/src/viewer.js index 98f9aede..b06c53a1 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -11,6 +11,7 @@ import { jsToGl, getIsGlb, Timer, getContainingFolder } from './utils.js'; import { GlbParser } from './glb_parser.js'; import { gltfEnvironmentLoader } from './environment.js'; import { getScaleFactor } from './gltf_utils.js'; +import { gltfAnimation } from './animation.js'; class gltfViewer { @@ -315,6 +316,11 @@ class gltfViewer prepareSceneForRendering(gltf) { const scene = gltf.scenes[this.renderingParameters.sceneIndex]; + if(this.renderingParameters.playAnimation) + { + this.animate(gltf); + } + scene.applyTransformHierarchy(gltf); const transform = mat4.create(); @@ -327,6 +333,14 @@ class gltfViewer scene.applyTransformHierarchy(gltf, transform); } + animate(gltf) + { + for(const anim of gltf.animations) + { + anim.advance(gltf, new Date().getTime() / 1000); + } + } + initializeGui() { const gui = new gltfUserInterface( From 8d55c515299859cf3e4315917ae4ec43d8661a10 Mon Sep 17 00:00:00 2001 From: David Labode Date: Wed, 27 Feb 2019 10:05:31 +0100 Subject: [PATCH 07/27] fix primitive.vert tangents and normals --- src/node.js | 7 +------ src/shaders/primitive.vert | 4 ++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/node.js b/src/node.js index 90c2889e..5e124fee 100644 --- a/src/node.js +++ b/src/node.js @@ -18,6 +18,7 @@ class gltfNode extends GltfObject this.scale = [1, 1, 1]; this.translation = [0, 0, 0]; this.name = undefined; + this.mesh = undefined; // non gltf this.worldTransform = mat4.create(); @@ -53,12 +54,6 @@ class gltfNode extends GltfObject this.changed = true; } - fromJson(jsonNode) - { - super.fromJson(jsonNode); - this.mesh = jsonNode.mesh; - } - applyMatrix(matrixData) { this.matrix = jsToGl(matrixData); diff --git a/src/shaders/primitive.vert b/src/shaders/primitive.vert index d9a79711..d2e4aa3b 100644 --- a/src/shaders/primitive.vert +++ b/src/shaders/primitive.vert @@ -138,6 +138,7 @@ vec4 getPosition() return pos; } +#ifdef HAS_NORMALS vec4 getNormal() { vec4 normal = a_Normal; @@ -148,7 +149,9 @@ vec4 getNormal() return normal; } +#endif +#ifdef HAS_TANGENTS vec4 getTangent() { vec4 tangent = a_Tangent; @@ -159,6 +162,7 @@ vec4 getTangent() return tangent; } +#endif void main() { From ff9d06c68dc82e91677dbc0a19ace92cb1a6f181 Mon Sep 17 00:00:00 2001 From: David Labode Date: Wed, 27 Feb 2019 17:09:10 +0100 Subject: [PATCH 08/27] wip animation skin --- src/animation.js | 6 ++-- src/gltf.js | 2 ++ src/interpolator.js | 2 +- src/node.js | 7 ++-- src/primitive.js | 6 ++++ src/renderer.js | 37 ++++++++++++++----- src/shader.js | 15 +++++--- src/shaders/primitive.vert | 74 ++++++++++++++++---------------------- src/skin.js | 35 ++++++++++++++++-- src/viewer.js | 29 ++++++++++++--- 10 files changed, 141 insertions(+), 72 deletions(-) diff --git a/src/animation.js b/src/animation.js index f7eb55b1..c979887c 100644 --- a/src/animation.js +++ b/src/animation.js @@ -40,13 +40,13 @@ class gltfAnimation extends GltfObject switch(channel.target.path) { case InterpolationPath.TRANSLATION: - node.translate(interpolator.interpolate(gltf, channel, sampler, totalTime, 3)); + node.applyTranslation(interpolator.interpolate(gltf, channel, sampler, totalTime, 3)); break; case InterpolationPath.ROTATION: - node.rotate(interpolator.interpolate(gltf, channel, sampler, totalTime, 4)); + node.applyRotation(interpolator.interpolate(gltf, channel, sampler, totalTime, 4)); break; case InterpolationPath.SCALE: - node.scale(interpolator.interpolate(gltf, channel, sampler, totalTime, 3)); + node.applyScale(interpolator.interpolate(gltf, channel, sampler, totalTime, 3)); break; case InterpolationPath.WEIGHTS: let mesh = gltf.meshes[node.mesh]; diff --git a/src/gltf.js b/src/gltf.js index b1bb3caa..f24aae3d 100644 --- a/src/gltf.js +++ b/src/gltf.js @@ -14,6 +14,7 @@ import { initGlForMembers, objectsFromJsons, objectFromJson } from './utils'; import { gltfAsset } from './asset.js'; import { GltfObject } from './gltf_object.js'; import { gltfAnimation } from './animation.js'; +import { gltfSkin } from './skin.js'; class glTF extends GltfObject { @@ -62,6 +63,7 @@ class glTF extends GltfObject this.lights = objectsFromJsons(getJsonLightsFromExtensions(json.extensions), gltfLight); this.images = objectsFromJsons(json.images, gltfImage); this.animations = objectsFromJsons(json.animations, gltfAnimation); + this.skins = objectsFromJsons(json.skins, gltfSkin); this.materials.push(gltfMaterial.createDefault()); this.samplers.push(gltfSampler.createDefault()); diff --git a/src/interpolator.js b/src/interpolator.js index b8a8a9a6..6d5c3884 100644 --- a/src/interpolator.js +++ b/src/interpolator.js @@ -2,7 +2,7 @@ import { gltfAnimationSampler, InterpolationModes } from './animation_sampler.js import { gltfAnimationChannel, InterpolationPath } from './channel.js'; import { gltfAccessor } from './accessor.js'; import { clamp, jsToGlSlice } from './utils.js'; -import { quat, vec3 } from 'gl-matrix'; +import { quat, vec3, glMatrix } from 'gl-matrix'; class gltfInterpolator { diff --git a/src/node.js b/src/node.js index 5e124fee..33817a84 100644 --- a/src/node.js +++ b/src/node.js @@ -19,6 +19,7 @@ class gltfNode extends GltfObject this.translation = [0, 0, 0]; this.name = undefined; this.mesh = undefined; + this.skin = undefined; // non gltf this.worldTransform = mat4.create(); @@ -66,21 +67,21 @@ class gltfNode extends GltfObject } // vec3 - translate(translation) + applyTranslation(translation) { this.translation = translation; this.changed = true; } // quat - rotate(rotation) + applyRotation(rotation) { this.rotation = rotation; this.changed = true; } // vec3 - scale(scale) + applyScale(scale) { this.scale = scale; this.changed = true; diff --git a/src/primitive.js b/src/primitive.js index 71cac59e..6952e4aa 100644 --- a/src/primitive.js +++ b/src/primitive.js @@ -16,6 +16,8 @@ class gltfPrimitive extends GltfObject this.glAttributes = []; this.defines = []; this.skip = true; + this.hasWeights = false; + this.hasJoints = false; } initGl(gltf) @@ -62,18 +64,22 @@ class gltfPrimitive extends GltfObject } break; case "JOINTS_0": + this.hasJoints = true; this.defines.push("HAS_JOINT_SET1 1"); this.glAttributes.push({ attribute: attribute, name: "a_Joint1", accessor: idx }); break; case "WEIGHTS_0": + this.hasWeights = true; this.defines.push("HAS_WEIGHT_SET1 1"); this.glAttributes.push({ attribute: attribute, name: "a_Weight1", accessor: idx }); break; case "JOINTS_1": + this.hasJoints = true; this.defines.push("HAS_JOINT_SET2 1"); this.glAttributes.push({ attribute: attribute, name: "a_Joint2", accessor: idx }); break; case "WEIGHTS_1": + this.hasWeights = true; this.defines.push("HAS_WEIGHT_SET2 1"); this.glAttributes.push({ attribute: attribute, name: "a_Weight2", accessor: idx }); break; diff --git a/src/renderer.js b/src/renderer.js index c99c81f3..2cb13aa2 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -160,13 +160,13 @@ class gltfRenderer { for (let primitive of mesh.primitives) { - this.drawPrimitive(gltf, primitive, node.worldTransform, this.viewProjectionMatrix, node.normalMatrix); + this.drawPrimitive(gltf, primitive, node, this.viewProjectionMatrix); } } } // vertices with given material - drawPrimitive(gltf, primitive, modelMatrix, viewProjectionMatrix, normalMatrix) + drawPrimitive(gltf, primitive, node, viewProjectionMatrix) { if (primitive.skip) return; @@ -175,7 +175,7 @@ class gltfRenderer //select shader permutation, compile and link program. let vertDefines = primitive.getDefines(); - this.pushVertParameterDefines(vertDefines); + this.pushVertParameterDefines(vertDefines, gltf, node, primitive); let fragDefines = material.getDefines().concat(vertDefines); this.pushFragParameterDefines(fragDefines); @@ -201,12 +201,14 @@ class gltfRenderer // update model dependant matrices once per node this.shader.updateUniform("u_ViewProjectionMatrix", viewProjectionMatrix); - this.shader.updateUniform("u_ModelMatrix", modelMatrix); - this.shader.updateUniform("u_NormalMatrix", normalMatrix, false); + this.shader.updateUniform("u_ModelMatrix", node.worldTransform); + this.shader.updateUniform("u_NormalMatrix", node.normalMatrix, false); this.shader.updateUniform("u_Gamma", this.parameters.gamma, false); this.shader.updateUniform("u_Exposure", this.parameters.exposure, false); this.shader.updateUniform("u_Camera", this.currentCameraPosition, false); + this.uploadSkin(gltf, node, primitive); + if (material.doubleSided) { WebGl.context.disable(WebGl.context.CULL_FACE); @@ -298,14 +300,31 @@ class gltfRenderer } } - pushVertParameterDefines(vertDefines) + pushVertParameterDefines(vertDefines, gltf, node, primitive) + { + if (this.parameters.playAnimation) + { + if(node.skin !== undefined && primitive.hasWeights && primitive.hasJoints) + { + const skin = gltf.skins[node.skin]; + + vertDefines.push("USE_SKINNING 1"); + vertDefines.push("JOINT_COUNT " + skin.joints.length); + } + } + } + + uploadSkin(gltf, node, primitive) { if (this.parameters.playAnimation) { - // TODO: check for skinning & animations + if(node.skin !== undefined && primitive.hasWeights && primitive.hasJoints) + { + const skin = gltf.skins[node.skin]; - // vertDefines.push("USE_SKINNING 1"); - // vertDefines.push("JOINT_COUNT " + node.joints); + this.shader.updateUniform("u_jointMatrix", skin.jointMatrices); + this.shader.updateUniform("u_jointNormalMatrix", skin.jointNormalMatrices); + } } } diff --git a/src/shader.js b/src/shader.js index 6e7c13f3..067e0b0e 100644 --- a/src/shader.js +++ b/src/shader.js @@ -90,11 +90,18 @@ class gltfShader updateUniformArray(arrayName, array, log) { - for (let i = 0; i < array.length; ++i) + // TODO: check for sturcts tag + if(array[0] instanceof UniformStruct) { - let element = array[i]; - let uniformName = arrayName + "[" + i + "]"; - this.updateUniform(uniformName, element, log); + for (let i = 0; i < array.length; ++i) + { + let element = array[i]; + let uniformName = arrayName + "[" + i + "]"; + this.updateUniform(uniformName, element, log); + } + }else{ + let uniformName = arrayName + "[0]"; + this.updateUniformValue(uniformName, array, log); } } diff --git a/src/shaders/primitive.vert b/src/shaders/primitive.vert index d2e4aa3b..f06fa007 100644 --- a/src/shaders/primitive.vert +++ b/src/shaders/primitive.vert @@ -64,50 +64,25 @@ uniform mat4 u_jointNormalMatrix[JOINT_COUNT]; #endif #ifdef USE_SKINNING -int getJoint(int index) -{ - int set = index / 4; - int idx = index % 4; - - if(set == 0){ -#ifdef HAS_JOINT_SET1 - return int(a_Joint1[idx]); -#endif - }else if(set == 1){ -#ifdef HAS_JOINT_SET2 - return int(a_Joint2[idx]); -#endif - } - - return 0; -} - -float getWeight(int index) -{ - int set = index / 4; - int idx = index % 4; - - if(set == 0){ -#ifdef HAS_WEIGHT_SET1 - return a_Weight1[idx]; -#endif - }else if(set == 1){ -#ifdef HAS_WEIGHT_SET2 - return a_Weight2[idx]; -#endif - } - - return 0.f; -} - mat4 getSkinningMatrix() { mat4 skin = mat4(0); - for(int i = 0; i < JOINT_COUNT; ++i) - { - skin += getWeight(i) * u_jointMatrix[getJoint(i)]; - } + #if defined(HAS_WEIGHT_SET1) && defined(HAS_JOINT_SET1) + skin += + a_Weight1.x * u_jointMatrix[int(a_Joint1.x)] + + a_Weight1.y * u_jointMatrix[int(a_Joint1.y)] + + a_Weight1.z * u_jointMatrix[int(a_Joint1.z)] + + a_Weight1.w * u_jointMatrix[int(a_Joint1.w)]; + #endif + + #if defined(HAS_WEIGHT_SET2) && defined(HAS_JOINT_SET2) + skin += + a_Weight2.x * u_jointMatrix[int(a_Joint2.x)] + + a_Weight2.y * u_jointMatrix[int(a_Joint2.y)] + + a_Weight2.z * u_jointMatrix[int(a_Joint2.z)] + + a_Weight2.w * u_jointMatrix[int(a_Joint2.w)]; + #endif return skin; } @@ -116,10 +91,21 @@ mat4 getSkinningNormalMatrix() { mat4 skin = mat4(0); - for(int i = 0; i < JOINT_COUNT; ++i) - { - skin += getWeight(i) * u_jointNormalMatrix[getJoint(i)]; - } + #if defined(HAS_WEIGHT_SET1) && defined(HAS_JOINT_SET1) + skin += + a_Weight1.x * u_jointNormalMatrix[int(a_Joint1.x)] + + a_Weight1.y * u_jointNormalMatrix[int(a_Joint1.y)] + + a_Weight1.z * u_jointNormalMatrix[int(a_Joint1.z)] + + a_Weight1.w * u_jointNormalMatrix[int(a_Joint1.w)]; + #endif + + #if defined(HAS_WEIGHT_SET2) && defined(HAS_JOINT_SET2) + skin += + a_Weight2.x * u_jointNormalMatrix[int(a_Joint2.x)] + + a_Weight2.y * u_jointNormalMatrix[int(a_Joint2.y)] + + a_Weight2.z * u_jointNormalMatrix[int(a_Joint2.z)] + + a_Weight2.w * u_jointNormalMatrix[int(a_Joint2.w)]; + #endif return skin; } diff --git a/src/skin.js b/src/skin.js index 3835b33b..efeed2e4 100644 --- a/src/skin.js +++ b/src/skin.js @@ -1,6 +1,6 @@ -//import { initGlForMembers } from './utils.js'; -//import { WebGl } from './webgl.js'; +import { jsToGlSlice } from './utils.js'; import { GltfObject } from './gltf_object.js'; +import { mat4 } from 'gl-matrix'; class gltfSkin extends GltfObject { @@ -9,8 +9,37 @@ class gltfSkin extends GltfObject super(); this.name = ""; - this.inverseBindMatrices = []; + this.inverseBindMatrices = undefined; this.joints = []; this.skeleton = undefined; + + // not gltf + this.jointMatrices = []; + this.jointNormalMatrices = []; + } + + computeJoints(gltf) + { + const skeleton = gltf.nodes[this.skeleton]; + const ibmAccessor = gltf.accessors[this.inverseBindMatrices].getTypedView(gltf); + this.jointMatrices = []; + this.jointNormalMatrices = []; + + for(const joint of this.joints) + { + const node = gltf.nodes[joint]; + let jointMatrix = mat4.create(); + let ibm = jsToGlSlice(ibmAccessor, joint * 16, 16); + mat4.mul(jointMatrix, node.worldTransform, ibm); + mat4.mul(jointMatrix, skeleton.inverseWorldTransform, jointMatrix); + this.jointMatrices.push(jointMatrix); + + let normalMatrix = mat4.create(); + mat4.invert(normalMatrix, jointMatrix); + mat4.transpose(normalMatrix, normalMatrix); + this.jointNormalMatrices.push(normalMatrix); + } } } + +export { gltfSkin }; diff --git a/src/viewer.js b/src/viewer.js index b06c53a1..82263046 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -11,7 +11,7 @@ import { jsToGl, getIsGlb, Timer, getContainingFolder } from './utils.js'; import { GlbParser } from './glb_parser.js'; import { gltfEnvironmentLoader } from './environment.js'; import { getScaleFactor } from './gltf_utils.js'; -import { gltfAnimation } from './animation.js'; +import { gltfSkin } from './skin.js'; class gltfViewer { @@ -318,7 +318,7 @@ class gltfViewer const scene = gltf.scenes[this.renderingParameters.sceneIndex]; if(this.renderingParameters.playAnimation) { - this.animate(gltf); + this.animateNode(gltf); } scene.applyTransformHierarchy(gltf); @@ -331,13 +331,32 @@ class gltfViewer } scene.applyTransformHierarchy(gltf, transform); + + if(this.renderingParameters.playAnimation) + { + this.animateSkin(gltf); + } } - animate(gltf) + animateNode(gltf) { - for(const anim of gltf.animations) + if(gltf.animations !== undefined) { - anim.advance(gltf, new Date().getTime() / 1000); + for(const anim of gltf.animations) + { + anim.advance(gltf, new Date().getTime() / 1000); + } + } + } + + animateSkin(gltf) + { + if(gltf.skins !== undefined) + { + for(const skin of gltf.skins) + { + skin.computeJoints(gltf); + } } } From 91e0edce139fd855696763caf2b0edcde81fc722 Mon Sep 17 00:00:00 2001 From: David Labode Date: Thu, 28 Feb 2019 18:14:51 +0100 Subject: [PATCH 09/27] wip animation --- src/accessor.js | 111 ++++++++++++++++++++++++++++-------- src/interpolator.js | 6 +- src/renderer.js | 6 +- src/rendering_parameters.js | 3 +- src/shader.js | 1 - src/skin.js | 7 ++- src/user_interface.js | 5 +- src/utils.js | 52 +++++++++++++++++ src/viewer.js | 32 +++++------ 9 files changed, 170 insertions(+), 53 deletions(-) diff --git a/src/accessor.js b/src/accessor.js index edc3dc58..e317fe3f 100644 --- a/src/accessor.js +++ b/src/accessor.js @@ -1,5 +1,6 @@ import { WebGl } from './webgl.js'; import { GltfObject } from './gltf_object.js'; +import { jsToGlSlice} from './utils.js'; class gltfAccessor extends GltfObject { @@ -14,7 +15,7 @@ class gltfAccessor extends GltfObject this.type = undefined; this.max = undefined; this.min = undefined; - this.sparse = undefined; + this.sparse = undefined; // CURRENTLY UNSUPPORTED this.name = undefined; // non gltf @@ -22,6 +23,8 @@ class gltfAccessor extends GltfObject this.typedView = undefined; } + + getTypedView(gltf) { if (this.typedView !== undefined) @@ -33,37 +36,38 @@ class gltfAccessor extends GltfObject { const bufferView = gltf.bufferViews[this.bufferView]; const buffer = gltf.buffers[bufferView.buffer]; - const byteOffset = bufferView.byteOffset; + const byteOffset = this.byteOffset + bufferView.byteOffset; + const componentSize = this.getComponentSize(); let componentCount = this.getComponentCount(); - if (bufferView.byteStride !== 0) + + if(bufferView.byteStride !== 0) { - componentCount = bufferView.byteStride / this.getComponentSize(); + componentCount = bufferView.byteStride / componentSize; } - const arrayOffsetLength = this.byteOffset / this.getComponentSize(); - const arrayLength = arrayOffsetLength + this.count * componentCount; + const arrayLength = this.count * componentCount; switch (this.componentType) { - case WebGl.context.BYTE: - this.typedView = new Int8Array(buffer.buffer, byteOffset, arrayLength); - break; - case WebGl.context.UNSIGNED_BYTE: - this.typedView = new Uint8Array(buffer.buffer, byteOffset, arrayLength); - break; - case WebGl.context.SHORT: - this.typedView = new Int16Array(buffer.buffer, byteOffset, arrayLength); - break; - case WebGl.context.UNSIGNED_SHORT: - this.typedView = new Uint16Array(buffer.buffer, byteOffset, arrayLength); - break; - case WebGl.context.UNSIGNED_INT: - this.typedView = new Uint32Array(buffer.buffer, byteOffset, arrayLength); - break; - case WebGl.context.FLOAT: - this.typedView = new Float32Array(buffer.buffer, byteOffset, arrayLength); - break; + case WebGl.context.BYTE: + this.typedView = new Int8Array(buffer.buffer, byteOffset, arrayLength); + break; + case WebGl.context.UNSIGNED_BYTE: + this.typedView = new Uint8Array(buffer.buffer, byteOffset, arrayLength); + break; + case WebGl.context.SHORT: + this.typedView = new Int16Array(buffer.buffer, byteOffset, arrayLength); + break; + case WebGl.context.UNSIGNED_SHORT: + this.typedView = new Uint16Array(buffer.buffer, byteOffset, arrayLength); + break; + case WebGl.context.UNSIGNED_INT: + this.typedView = new Uint32Array(buffer.buffer, byteOffset, arrayLength); + break; + case WebGl.context.FLOAT: + this.typedView = new Float32Array(buffer.buffer, byteOffset, arrayLength); + break; } } @@ -75,6 +79,65 @@ class gltfAccessor extends GltfObject return this.typedView; } + getFilteredView(gltf) + { + if (this.filteredView !== undefined) + { + return this.filteredView; + } + + if (this.bufferView !== undefined) + { + const bufferView = gltf.bufferViews[this.bufferView]; + const buffer = gltf.buffers[bufferView.buffer]; + const byteOffset = bufferView.byteOffset; + + const componentSize = this.getComponentSize(); + const componentCount = this.getComponentCount(); + const arrayLength = this.count * componentCount; + + let stride = bufferView.byteStride !== 0 ? bufferView.byteStride : componentCount * componentSize; + let dv = new DataView(buffer.buffer, byteOffset, this.count * stride); + + let func = 'getFloat32'; + switch (this.componentType) + { + case WebGl.context.BYTE: + this.filteredView = new Int8Array(arrayLength); + this.func = 'getInt8'; + break; + case WebGl.context.UNSIGNED_BYTE: + this.filteredView = new Uint8Array(arrayLength); + this.func = 'getUint8'; + break; + case WebGl.context.SHORT: + this.filteredView = new Int16Array(arrayLength); + this.func = 'getInt16'; + break; + case WebGl.context.UNSIGNED_SHORT: + this.filteredView = new Uint16Array(arrayLength); + this.func = 'getUint16'; + break; + case WebGl.context.UNSIGNED_INT: + this.filteredView = new Uint32Array(arrayLength); + this.func = 'getUint32'; + break; + case WebGl.context.FLOAT: + this.filteredView = new Float32Array(arrayLength); + this.func = 'getFloat32'; + break; + } + + for(let i = 0; i < arrayLength; ++i) + { + let offset = Math.floor(i/componentCount) * stride + (i % componentCount) * componentSize; + this.filteredView[i] = dv.getFloat32(offset); + } + } + + return this.filteredView; + } + getComponentCount() { return CompononentCount.get(this.type); diff --git a/src/interpolator.js b/src/interpolator.js index 6d5c3884..09102986 100644 --- a/src/interpolator.js +++ b/src/interpolator.js @@ -13,6 +13,8 @@ class gltfInterpolator slerpQuat(q1, q2, t) { + quat.normalize(q1, q1); + quat.normalize(q2, q2); let quatResult = quat.create(); quat.slerp(quatResult, q1, q2, t); quat.normalize(quatResult, quatResult); @@ -65,8 +67,8 @@ class gltfInterpolator interpolate(gltf, channel, sampler, t, stride) { - const input = gltf.accessors[sampler.input].getTypedView(gltf); - const output = gltf.accessors[sampler.output].getTypedView(gltf); + const input = gltf.accessors[sampler.input].getFilteredView(gltf); + const output = gltf.accessors[sampler.output].getFilteredView(gltf); if(output.length === 1) // no interpolation for single keyFrame animations { diff --git a/src/renderer.js b/src/renderer.js index 2cb13aa2..5b0931d4 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -282,7 +282,7 @@ class gltfRenderer if (drawIndexed) { const indexAccessor = gltf.accessors[primitive.indices]; - WebGl.context.drawElements(primitive.mode, indexAccessor.count, indexAccessor.componentType, indexAccessor.byteOffset); + WebGl.context.drawElements(primitive.mode, indexAccessor.count, indexAccessor.componentType, 0); } else { @@ -302,7 +302,7 @@ class gltfRenderer pushVertParameterDefines(vertDefines, gltf, node, primitive) { - if (this.parameters.playAnimation) + if (!this.parameters.animationTimer.paused) { if(node.skin !== undefined && primitive.hasWeights && primitive.hasJoints) { @@ -316,7 +316,7 @@ class gltfRenderer uploadSkin(gltf, node, primitive) { - if (this.parameters.playAnimation) + if (!this.parameters.animationTimer.paused) { if(node.skin !== undefined && primitive.hasWeights && primitive.hasJoints) { diff --git a/src/rendering_parameters.js b/src/rendering_parameters.js index c36ff892..df8b4327 100644 --- a/src/rendering_parameters.js +++ b/src/rendering_parameters.js @@ -1,4 +1,5 @@ import { ImageMimeType } from "./image"; +import { AnimationTimer } from "./utils"; const UserCameraIndex = "orbit camera"; @@ -26,7 +27,7 @@ class gltfRenderingParameters this.debugOutput = debugOutput; this.sceneIndex = 0; this.cameraIndex = UserCameraIndex; - this.playAnimation = false; + this.animationTimer = new AnimationTimer(); } userCameraActive() diff --git a/src/shader.js b/src/shader.js index 067e0b0e..3ab1308c 100644 --- a/src/shader.js +++ b/src/shader.js @@ -90,7 +90,6 @@ class gltfShader updateUniformArray(arrayName, array, log) { - // TODO: check for sturcts tag if(array[0] instanceof UniformStruct) { for (let i = 0; i < array.length; ++i) diff --git a/src/skin.js b/src/skin.js index efeed2e4..441c9277 100644 --- a/src/skin.js +++ b/src/skin.js @@ -21,17 +21,18 @@ class gltfSkin extends GltfObject computeJoints(gltf) { const skeleton = gltf.nodes[this.skeleton]; - const ibmAccessor = gltf.accessors[this.inverseBindMatrices].getTypedView(gltf); + const ibmAccessor = gltf.accessors[this.inverseBindMatrices].getFilteredView(gltf); this.jointMatrices = []; this.jointNormalMatrices = []; + let i = 0; for(const joint of this.joints) { const node = gltf.nodes[joint]; let jointMatrix = mat4.create(); - let ibm = jsToGlSlice(ibmAccessor, joint * 16, 16); + let ibm = jsToGlSlice(ibmAccessor, i++ * 16, 16); mat4.mul(jointMatrix, node.worldTransform, ibm); - mat4.mul(jointMatrix, skeleton.inverseWorldTransform, jointMatrix); + // mat4.mul(jointMatrix, skeleton.inverseWorldTransform, jointMatrix); this.jointMatrices.push(jointMatrix); let normalMatrix = mat4.create(); diff --git a/src/user_interface.js b/src/user_interface.js index 94c7f95a..0a44f767 100644 --- a/src/user_interface.js +++ b/src/user_interface.js @@ -22,6 +22,8 @@ class gltfUserInterface this.cameraSelection = undefined; this.onModelSelected = undefined; + + this.playAnimation = false; } initialize() @@ -121,8 +123,9 @@ class gltfUserInterface initializeAnimationSettings() { + const self = this; const animationFolder = this.gui.addFolder("Animation"); - animationFolder.add(this.renderingParameters, "playAnimation").name("Play"); + animationFolder.add(self, "playAnimation").name("Play").onChange(() => self.renderingParameters.animationTimer.toggle()); } initializeDebugSettings() diff --git a/src/utils.js b/src/utils.js index 7126ae64..4287244f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -189,6 +189,57 @@ class Timer } } +class AnimationTimer +{ + constructor() + { + this.startTime = 0; + this.paused = true; + this.pausedTime = 0; + } + + elapsed() + { + if(this.paused) + { + return this.pausedTime / 1000; + } + else + { + return (new Date().getTime() - this.startTime) / 1000; + } + } + + toggle() + { + if(this.paused) + { + this.unpause(); + } + else + { + this.pause(); + } + } + + start() + { + this.startTime = new Date().getTime(); + } + + pause() + { + this.pausedTime = new Date().getTime() - this.startTime; + this.paused = true; + } + + unpause() + { + this.startTime += new Date().getTime() - this.startTime - this.pausedTime; + this.paused = false; + } +} + export { jsToGl, jsToGlSlice, @@ -208,5 +259,6 @@ export { combinePaths, UniformStruct, Timer, + AnimationTimer, initGlForMembers }; diff --git a/src/viewer.js b/src/viewer.js index 82263046..5e5e18ab 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -316,42 +316,38 @@ class gltfViewer prepareSceneForRendering(gltf) { const scene = gltf.scenes[this.renderingParameters.sceneIndex]; - if(this.renderingParameters.playAnimation) - { - this.animateNode(gltf); - } + + this.animateNode(gltf); scene.applyTransformHierarchy(gltf); - const transform = mat4.create(); - if (this.renderingParameters.userCameraActive()) - { - const scaleFactor = getScaleFactor(gltf, this.renderingParameters.sceneIndex); - mat4.scale(transform, transform, vec3.fromValues(scaleFactor, scaleFactor, scaleFactor)); - } + // const transform = mat4.create(); + // if (this.renderingParameters.userCameraActive()) + // { + // const scaleFactor = getScaleFactor(gltf, this.renderingParameters.sceneIndex); + // mat4.scale(transform, transform, vec3.fromValues(scaleFactor, scaleFactor, scaleFactor)); + // } - scene.applyTransformHierarchy(gltf, transform); + // scene.applyTransformHierarchy(gltf, transform); - if(this.renderingParameters.playAnimation) - { - this.animateSkin(gltf); - } + this.animateSkin(gltf); } animateNode(gltf) { - if(gltf.animations !== undefined) + if(gltf.animations !== undefined && !this.renderingParameters.animationTimer.paused) { + const t = this.renderingParameters.animationTimer.elapsed(); for(const anim of gltf.animations) { - anim.advance(gltf, new Date().getTime() / 1000); + anim.advance(gltf, t); } } } animateSkin(gltf) { - if(gltf.skins !== undefined) + if(gltf.skins !== undefined && !this.renderingParameters.animationTimer.paused) { for(const skin of gltf.skins) { From 4c60affde5d87c478f28ad3647700855fa1304a7 Mon Sep 17 00:00:00 2001 From: David Labode Date: Fri, 1 Mar 2019 09:33:21 +0100 Subject: [PATCH 10/27] fix getDeinterlacedView --- src/accessor.js | 20 +++++++++----------- src/interpolator.js | 4 ++-- src/skin.js | 2 +- src/webgl.js | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/accessor.js b/src/accessor.js index e317fe3f..5c467152 100644 --- a/src/accessor.js +++ b/src/accessor.js @@ -23,8 +23,6 @@ class gltfAccessor extends GltfObject this.typedView = undefined; } - - getTypedView(gltf) { if (this.typedView !== undefined) @@ -79,7 +77,7 @@ class gltfAccessor extends GltfObject return this.typedView; } - getFilteredView(gltf) + getDeinterlacedView(gltf) { if (this.filteredView !== undefined) { @@ -90,7 +88,7 @@ class gltfAccessor extends GltfObject { const bufferView = gltf.bufferViews[this.bufferView]; const buffer = gltf.buffers[bufferView.buffer]; - const byteOffset = bufferView.byteOffset; + const byteOffset = this.byteOffset + bufferView.byteOffset; const componentSize = this.getComponentSize(); const componentCount = this.getComponentCount(); @@ -104,34 +102,34 @@ class gltfAccessor extends GltfObject { case WebGl.context.BYTE: this.filteredView = new Int8Array(arrayLength); - this.func = 'getInt8'; + func = 'getInt8'; break; case WebGl.context.UNSIGNED_BYTE: this.filteredView = new Uint8Array(arrayLength); - this.func = 'getUint8'; + func = 'getUint8'; break; case WebGl.context.SHORT: this.filteredView = new Int16Array(arrayLength); - this.func = 'getInt16'; + func = 'getInt16'; break; case WebGl.context.UNSIGNED_SHORT: this.filteredView = new Uint16Array(arrayLength); - this.func = 'getUint16'; + func = 'getUint16'; break; case WebGl.context.UNSIGNED_INT: this.filteredView = new Uint32Array(arrayLength); - this.func = 'getUint32'; + func = 'getUint32'; break; case WebGl.context.FLOAT: this.filteredView = new Float32Array(arrayLength); - this.func = 'getFloat32'; + func = 'getFloat32'; break; } for(let i = 0; i < arrayLength; ++i) { let offset = Math.floor(i/componentCount) * stride + (i % componentCount) * componentSize; - this.filteredView[i] = dv.getFloat32(offset); + this.filteredView[i] = dv[func](offset, true); } } diff --git a/src/interpolator.js b/src/interpolator.js index 09102986..e7088fc9 100644 --- a/src/interpolator.js +++ b/src/interpolator.js @@ -67,8 +67,8 @@ class gltfInterpolator interpolate(gltf, channel, sampler, t, stride) { - const input = gltf.accessors[sampler.input].getFilteredView(gltf); - const output = gltf.accessors[sampler.output].getFilteredView(gltf); + const input = gltf.accessors[sampler.input].getDeinterlacedView(gltf); + const output = gltf.accessors[sampler.output].getDeinterlacedView(gltf); if(output.length === 1) // no interpolation for single keyFrame animations { diff --git a/src/skin.js b/src/skin.js index 441c9277..7afbf2bb 100644 --- a/src/skin.js +++ b/src/skin.js @@ -21,7 +21,7 @@ class gltfSkin extends GltfObject computeJoints(gltf) { const skeleton = gltf.nodes[this.skeleton]; - const ibmAccessor = gltf.accessors[this.inverseBindMatrices].getFilteredView(gltf); + const ibmAccessor = gltf.accessors[this.inverseBindMatrices].getDeinterlacedView(gltf); this.jointMatrices = []; this.jointNormalMatrices = []; diff --git a/src/webgl.js b/src/webgl.js index 6d517a69..8b43a680 100644 --- a/src/webgl.js +++ b/src/webgl.js @@ -189,7 +189,7 @@ class gltfWebGl } WebGl.context.vertexAttribPointer(attributeLocation, gltfAccessor.getComponentCount(), gltfAccessor.componentType, - gltfAccessor.normalized, gltfBufferView.byteStride, gltfAccessor.byteOffset); + gltfAccessor.normalized, gltfBufferView.byteStride, 0); WebGl.context.enableVertexAttribArray(attributeLocation); return true; From 10d34a5af82ef312d3fa404255f78de5a1793ca3 Mon Sep 17 00:00:00 2001 From: David Labode Date: Fri, 1 Mar 2019 10:10:14 +0100 Subject: [PATCH 11/27] fix attributes --- src/shaders/primitive.vert | 8 ++++---- src/skin.js | 2 +- src/utils.js | 1 + src/viewer.js | 3 +++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/shaders/primitive.vert b/src/shaders/primitive.vert index f06fa007..1e7c11c1 100644 --- a/src/shaders/primitive.vert +++ b/src/shaders/primitive.vert @@ -18,19 +18,19 @@ varying vec3 v_Normal; #endif #ifdef HAS_JOINT_SET1 -varying vec4 a_Joint1; +attribute vec4 a_Joint1; #endif #ifdef HAS_JOINT_SET2 -varying vec4 a_Joint2; +attribute vec4 a_Joint2; #endif #ifdef HAS_WEIGHT_SET1 -varying vec4 a_Weight1; +attribute vec4 a_Weight1; #endif #ifdef HAS_WEIGHT_SET2 -varying vec4 a_Weight2; +attribute vec4 a_Weight2; #endif #ifdef HAS_UV_SET1 diff --git a/src/skin.js b/src/skin.js index 7afbf2bb..bc2c99da 100644 --- a/src/skin.js +++ b/src/skin.js @@ -32,7 +32,7 @@ class gltfSkin extends GltfObject let jointMatrix = mat4.create(); let ibm = jsToGlSlice(ibmAccessor, i++ * 16, 16); mat4.mul(jointMatrix, node.worldTransform, ibm); - // mat4.mul(jointMatrix, skeleton.inverseWorldTransform, jointMatrix); + mat4.mul(jointMatrix, skeleton.inverseWorldTransform, jointMatrix); this.jointMatrices.push(jointMatrix); let normalMatrix = mat4.create(); diff --git a/src/utils.js b/src/utils.js index 4287244f..b58e5e4e 100644 --- a/src/utils.js +++ b/src/utils.js @@ -225,6 +225,7 @@ class AnimationTimer start() { this.startTime = new Date().getTime(); + this.paused = false; } pause() diff --git a/src/viewer.js b/src/viewer.js index 5e5e18ab..178d3a3f 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -247,6 +247,9 @@ class gltfViewer this.prepareSceneForRendering(gltf); this.userCamera.fitViewToScene(gltf, this.renderingParameters.sceneIndex); + + // FOR DEBUGGING + this.renderingParameters.animationTimer.start(); } render() From f10b11024e5cb04a21c423b80e184d9e54e8bf3b Mon Sep 17 00:00:00 2001 From: David Labode Date: Fri, 1 Mar 2019 13:22:53 +0100 Subject: [PATCH 12/27] flatten uniform arrays --- src/accessor.js | 2 +- src/shader.js | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/accessor.js b/src/accessor.js index 5c467152..06f8e93f 100644 --- a/src/accessor.js +++ b/src/accessor.js @@ -128,7 +128,7 @@ class gltfAccessor extends GltfObject for(let i = 0; i < arrayLength; ++i) { - let offset = Math.floor(i/componentCount) * stride + (i % componentCount) * componentSize; + let offset = Math.floor(i/componentCount) * stride + (i % componentCount) * componentSize; this.filteredView[i] = dv[func](offset, true); } } diff --git a/src/shader.js b/src/shader.js index 3ab1308c..89ec8ac4 100644 --- a/src/shader.js +++ b/src/shader.js @@ -100,7 +100,23 @@ class gltfShader } }else{ let uniformName = arrayName + "[0]"; - this.updateUniformValue(uniformName, array, log); + + if(array[0] instanceof Array || array[0] instanceof TypedArray) + { + let val = new Array(); + for(let i = 0; i < array.length; ++i) + { + const inner = array[i]; + for(let j = 0; j < array.length; ++j) + { + val.push(inner[j]); + } + } + this.updateUniformValue(uniformName, val, log); + }else{ + this.updateUniformValue(uniformName, array, log); + } + } } From 3fa2d44270abe5e3858edbf8434044f310f05742 Mon Sep 17 00:00:00 2001 From: David Labode Date: Fri, 1 Mar 2019 13:55:16 +0100 Subject: [PATCH 13/27] fix flatten uniform arrays --- src/shader.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/shader.js b/src/shader.js index 89ec8ac4..6292efd8 100644 --- a/src/shader.js +++ b/src/shader.js @@ -101,22 +101,14 @@ class gltfShader }else{ let uniformName = arrayName + "[0]"; - if(array[0] instanceof Array || array[0] instanceof TypedArray) + let flat = []; + + for(let i = 0; i < array.length; ++i) { - let val = new Array(); - for(let i = 0; i < array.length; ++i) - { - const inner = array[i]; - for(let j = 0; j < array.length; ++j) - { - val.push(inner[j]); - } - } - this.updateUniformValue(uniformName, val, log); - }else{ - this.updateUniformValue(uniformName, array, log); + flat.push.apply(flat, Array.from(array[i])); } + this.updateUniformValue(uniformName, flat, log); } } From f946c35c1b08089fe858a25d7e1789eb1526c49e Mon Sep 17 00:00:00 2001 From: David Labode Date: Fri, 1 Mar 2019 17:36:49 +0100 Subject: [PATCH 14/27] added skinning support --- src/interpolator.js | 36 ++++++++++++++++-------------------- src/node.js | 7 ++++++- src/renderer.js | 2 +- src/skin.js | 1 + src/viewer.js | 16 ++++++++-------- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/interpolator.js b/src/interpolator.js index e7088fc9..dc9d91e7 100644 --- a/src/interpolator.js +++ b/src/interpolator.js @@ -1,20 +1,18 @@ -import { gltfAnimationSampler, InterpolationModes } from './animation_sampler.js'; -import { gltfAnimationChannel, InterpolationPath } from './channel.js'; -import { gltfAccessor } from './accessor.js'; +import { InterpolationModes } from './animation_sampler.js'; +import { InterpolationPath } from './channel.js'; import { clamp, jsToGlSlice } from './utils.js'; -import { quat, vec3, glMatrix } from 'gl-matrix'; +import { quat, glMatrix } from 'gl-matrix'; class gltfInterpolator { constructor() { this.prevKey = 0; + this.prevT = 0.0; } slerpQuat(q1, q2, t) { - quat.normalize(q1, q1); - quat.normalize(q2, q2); let quatResult = quat.create(); quat.slerp(quatResult, q1, q2, t); quat.normalize(quatResult, quatResult); @@ -43,7 +41,7 @@ class gltfInterpolator let result = new glMatrix.ARRAY_TYPE(stride); const tSq = t * t; - const tCub = t * t * t; + const tCub = tSq * t; // we assume that the components are layed out like this: in-tangent, point, out-tangent in output // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-c-spline-interpolation @@ -72,27 +70,30 @@ class gltfInterpolator if(output.length === 1) // no interpolation for single keyFrame animations { - return output[0]; + return jsToGlSlice(output, 0, stride); } let nextKey = undefined; const maxKeyTime = input[input.length - 1]; t = t % maxKeyTime; // loop animation - for(let i = this.prevKey; i < input.length; ++i) // find current keyframe interval + if(this.prevT > t) { - if(t <= input[i]) + this.prevKey = 0; + } + + this.prevT = t; + + for (let i = this.prevKey; i < input.length; ++i) // find current keyframe interval + { + if (t <= input[i]) { nextKey = i; break; } } - if(nextKey === undefined) - { - nextKey = 1; - } - + nextKey = clamp(nextKey, 1, input.length - 1); this.prevKey = clamp(nextKey - 1, 0, nextKey); const keyDelta = input[nextKey] - input[this.prevKey]; @@ -115,11 +116,6 @@ class gltfInterpolator { return quat.fromValues(output[4 * index], output[4 * index + 1], output[4 * index + 2], output[4 * index + 3]); } - - // getVec3(output, index) - // { - // return vec3.fromValues(output[3 * index], output[3 * index + 1], output[3 * index + 2]); - // } } export { gltfInterpolator }; diff --git a/src/node.js b/src/node.js index 33817a84..95a4fbf4 100644 --- a/src/node.js +++ b/src/node.js @@ -87,7 +87,12 @@ class gltfNode extends GltfObject this.changed = true; } - // TODO: WEIGHTS + resetTransform() + { + this.rotation = [0, 0, 0, 1]; + this.translation = [0, 0, 0]; + this.changed = true; + } getLocalTransform() { diff --git a/src/renderer.js b/src/renderer.js index 5b0931d4..ae04bcfc 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -309,7 +309,7 @@ class gltfRenderer const skin = gltf.skins[node.skin]; vertDefines.push("USE_SKINNING 1"); - vertDefines.push("JOINT_COUNT " + skin.joints.length); + vertDefines.push("JOINT_COUNT " + skin.jointMatrices.length); } } } diff --git a/src/skin.js b/src/skin.js index bc2c99da..be671316 100644 --- a/src/skin.js +++ b/src/skin.js @@ -29,6 +29,7 @@ class gltfSkin extends GltfObject for(const joint of this.joints) { const node = gltf.nodes[joint]; + let jointMatrix = mat4.create(); let ibm = jsToGlSlice(ibmAccessor, i++ * 16, 16); mat4.mul(jointMatrix, node.worldTransform, ibm); diff --git a/src/viewer.js b/src/viewer.js index 178d3a3f..a5ccf4ae 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -324,14 +324,14 @@ class gltfViewer scene.applyTransformHierarchy(gltf); - // const transform = mat4.create(); - // if (this.renderingParameters.userCameraActive()) - // { - // const scaleFactor = getScaleFactor(gltf, this.renderingParameters.sceneIndex); - // mat4.scale(transform, transform, vec3.fromValues(scaleFactor, scaleFactor, scaleFactor)); - // } - - // scene.applyTransformHierarchy(gltf, transform); + const transform = mat4.create(); + if (this.renderingParameters.userCameraActive()) + { + const scaleFactor = getScaleFactor(gltf, this.renderingParameters.sceneIndex); + mat4.scale(transform, transform, vec3.fromValues(scaleFactor, scaleFactor, scaleFactor)); + } + + scene.applyTransformHierarchy(gltf, transform); this.animateSkin(gltf); } From d2bb7aab7b5eea5df2e96b2d9ea57e2e7b2080d0 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 4 Mar 2019 09:03:03 +0100 Subject: [PATCH 15/27] fixed parsing weights --- src/animation.js | 11 +++++++++++ src/mesh.js | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/src/animation.js b/src/animation.js index c979887c..2e86abc2 100644 --- a/src/animation.js +++ b/src/animation.js @@ -21,6 +21,12 @@ class gltfAnimation extends GltfObject this.channels = objectsFromJsons(jsonAnimation.channels, gltfAnimationChannel); this.samplers = objectsFromJsons(jsonAnimation.samplers, gltfAnimationSampler); + if(this.channels === undefined) + { + console.error("No channel data found for skin"); + return; + } + for(let i = 0; i < this.channels.length; ++i) { this.interpolators.push(new gltfInterpolator()); @@ -29,6 +35,11 @@ class gltfAnimation extends GltfObject advance(gltf, totalTime) { + if(this.channels === undefined) + { + return; + } + for(let i = 0; i < this.interpolators.length; ++i) { const channel = this.channels[i]; diff --git a/src/mesh.js b/src/mesh.js index 9259b24e..542a1b90 100644 --- a/src/mesh.js +++ b/src/mesh.js @@ -9,6 +9,7 @@ class gltfMesh extends GltfObject super(); this.primitives = []; this.name = undefined; + this.weights = []; } fromJson(jsonMesh) @@ -19,6 +20,11 @@ class gltfMesh extends GltfObject } this.primitives = objectsFromJsons(jsonMesh.primitives, gltfPrimitive); + + if(jsonMesh.weights !== undefined) + { + this.weights = jsonMesh.weights; + } } } From 12ce0a9e3548b47723114313f7b5ea5b496893d1 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 4 Mar 2019 09:16:29 +0100 Subject: [PATCH 16/27] refactored skinning into include glsl --- src/renderer.js | 3 +- src/shaders/animation.glsl | 68 ++++++++++++++++++++++++++++++++++++ src/shaders/primitive.vert | 71 ++------------------------------------ 3 files changed, 72 insertions(+), 70 deletions(-) create mode 100644 src/shaders/animation.glsl diff --git a/src/renderer.js b/src/renderer.js index ae04bcfc..122377cb 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -2,7 +2,6 @@ import { mat4, vec3 } from 'gl-matrix'; import { gltfLight } from './light.js'; import { gltfTextureInfo } from './texture.js'; import { ShaderCache } from './shader_cache.js'; -import { jsToGl } from './utils.js'; import { WebGl } from './webgl.js'; import { ToneMaps, DebugOutput, Environments } from './rendering_parameters.js'; import { ImageMimeType } from './image.js'; @@ -11,6 +10,7 @@ import primitiveShader from './shaders/primitive.vert'; import texturesShader from './shaders/textures.glsl'; import tonemappingShader from'./shaders/tonemapping.glsl'; import shaderFunctions from './shaders/functions.glsl'; +import animationShader from './shaders/animation.glsl'; class gltfRenderer { @@ -31,6 +31,7 @@ class gltfRenderer shaderSources.set("tonemapping.glsl", tonemappingShader); shaderSources.set("textures.glsl", texturesShader); shaderSources.set("functions.glsl", shaderFunctions); + shaderSources.set("animation.glsl", animationShader); this.shaderCache = new ShaderCache(shaderSources); diff --git a/src/shaders/animation.glsl b/src/shaders/animation.glsl new file mode 100644 index 00000000..ccc7173f --- /dev/null +++ b/src/shaders/animation.glsl @@ -0,0 +1,68 @@ +#ifdef HAS_JOINT_SET1 +attribute vec4 a_Joint1; +#endif + +#ifdef HAS_JOINT_SET2 +attribute vec4 a_Joint2; +#endif + +#ifdef HAS_WEIGHT_SET1 +attribute vec4 a_Weight1; +#endif + +#ifdef HAS_WEIGHT_SET2 +attribute vec4 a_Weight2; +#endif + +#ifdef USE_SKINNING +uniform mat4 u_jointMatrix[JOINT_COUNT]; +uniform mat4 u_jointNormalMatrix[JOINT_COUNT]; +#endif + +#ifdef USE_SKINNING +mat4 getSkinningMatrix() +{ + mat4 skin = mat4(0); + + #if defined(HAS_WEIGHT_SET1) && defined(HAS_JOINT_SET1) + skin += + a_Weight1.x * u_jointMatrix[int(a_Joint1.x)] + + a_Weight1.y * u_jointMatrix[int(a_Joint1.y)] + + a_Weight1.z * u_jointMatrix[int(a_Joint1.z)] + + a_Weight1.w * u_jointMatrix[int(a_Joint1.w)]; + #endif + + #if defined(HAS_WEIGHT_SET2) && defined(HAS_JOINT_SET2) + skin += + a_Weight2.x * u_jointMatrix[int(a_Joint2.x)] + + a_Weight2.y * u_jointMatrix[int(a_Joint2.y)] + + a_Weight2.z * u_jointMatrix[int(a_Joint2.z)] + + a_Weight2.w * u_jointMatrix[int(a_Joint2.w)]; + #endif + + return skin; +} + +mat4 getSkinningNormalMatrix() +{ + mat4 skin = mat4(0); + + #if defined(HAS_WEIGHT_SET1) && defined(HAS_JOINT_SET1) + skin += + a_Weight1.x * u_jointNormalMatrix[int(a_Joint1.x)] + + a_Weight1.y * u_jointNormalMatrix[int(a_Joint1.y)] + + a_Weight1.z * u_jointNormalMatrix[int(a_Joint1.z)] + + a_Weight1.w * u_jointNormalMatrix[int(a_Joint1.w)]; + #endif + + #if defined(HAS_WEIGHT_SET2) && defined(HAS_JOINT_SET2) + skin += + a_Weight2.x * u_jointNormalMatrix[int(a_Joint2.x)] + + a_Weight2.y * u_jointNormalMatrix[int(a_Joint2.y)] + + a_Weight2.z * u_jointNormalMatrix[int(a_Joint2.z)] + + a_Weight2.w * u_jointNormalMatrix[int(a_Joint2.w)]; + #endif + + return skin; +} +#endif // !USE_SKINNING diff --git a/src/shaders/primitive.vert b/src/shaders/primitive.vert index 1e7c11c1..89abc4a9 100644 --- a/src/shaders/primitive.vert +++ b/src/shaders/primitive.vert @@ -1,3 +1,5 @@ +#include + attribute vec4 a_Position; varying vec3 v_Position; @@ -17,22 +19,6 @@ varying vec3 v_Normal; #endif #endif -#ifdef HAS_JOINT_SET1 -attribute vec4 a_Joint1; -#endif - -#ifdef HAS_JOINT_SET2 -attribute vec4 a_Joint2; -#endif - -#ifdef HAS_WEIGHT_SET1 -attribute vec4 a_Weight1; -#endif - -#ifdef HAS_WEIGHT_SET2 -attribute vec4 a_Weight2; -#endif - #ifdef HAS_UV_SET1 attribute vec2 a_UV1; #endif @@ -58,59 +44,6 @@ uniform mat4 u_ViewProjectionMatrix; uniform mat4 u_ModelMatrix; uniform mat4 u_NormalMatrix; -#ifdef USE_SKINNING -uniform mat4 u_jointMatrix[JOINT_COUNT]; -uniform mat4 u_jointNormalMatrix[JOINT_COUNT]; -#endif - -#ifdef USE_SKINNING -mat4 getSkinningMatrix() -{ - mat4 skin = mat4(0); - - #if defined(HAS_WEIGHT_SET1) && defined(HAS_JOINT_SET1) - skin += - a_Weight1.x * u_jointMatrix[int(a_Joint1.x)] + - a_Weight1.y * u_jointMatrix[int(a_Joint1.y)] + - a_Weight1.z * u_jointMatrix[int(a_Joint1.z)] + - a_Weight1.w * u_jointMatrix[int(a_Joint1.w)]; - #endif - - #if defined(HAS_WEIGHT_SET2) && defined(HAS_JOINT_SET2) - skin += - a_Weight2.x * u_jointMatrix[int(a_Joint2.x)] + - a_Weight2.y * u_jointMatrix[int(a_Joint2.y)] + - a_Weight2.z * u_jointMatrix[int(a_Joint2.z)] + - a_Weight2.w * u_jointMatrix[int(a_Joint2.w)]; - #endif - - return skin; -} - -mat4 getSkinningNormalMatrix() -{ - mat4 skin = mat4(0); - - #if defined(HAS_WEIGHT_SET1) && defined(HAS_JOINT_SET1) - skin += - a_Weight1.x * u_jointNormalMatrix[int(a_Joint1.x)] + - a_Weight1.y * u_jointNormalMatrix[int(a_Joint1.y)] + - a_Weight1.z * u_jointNormalMatrix[int(a_Joint1.z)] + - a_Weight1.w * u_jointNormalMatrix[int(a_Joint1.w)]; - #endif - - #if defined(HAS_WEIGHT_SET2) && defined(HAS_JOINT_SET2) - skin += - a_Weight2.x * u_jointNormalMatrix[int(a_Joint2.x)] + - a_Weight2.y * u_jointNormalMatrix[int(a_Joint2.y)] + - a_Weight2.z * u_jointNormalMatrix[int(a_Joint2.z)] + - a_Weight2.w * u_jointNormalMatrix[int(a_Joint2.w)]; - #endif - - return skin; -} -#endif // !USE_SKINNING - vec4 getPosition() { vec4 pos = a_Position; From 9ecb15c35e8680a59c512ff77904ff180d19567f Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 4 Mar 2019 13:59:20 +0100 Subject: [PATCH 17/27] Morphing wip --- src/primitive.js | 34 ++++++++++ src/renderer.js | 25 +++++++- src/shader.js | 48 +++++++++++--- src/shaders/animation.glsl | 124 +++++++++++++++++++++++++++++++++++++ src/shaders/primitive.vert | 12 +++- 5 files changed, 231 insertions(+), 12 deletions(-) diff --git a/src/primitive.js b/src/primitive.js index 6952e4aa..0c9feca6 100644 --- a/src/primitive.js +++ b/src/primitive.js @@ -8,6 +8,7 @@ class gltfPrimitive extends GltfObject { super(); this.attributes = []; + this.targets = []; this.indices = undefined; this.material = undefined; this.mode = WebGl.context.TRIANGLES; @@ -31,6 +32,8 @@ class gltfPrimitive extends GltfObject initGlForMembers(this, gltf); // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes + + // VERTEX ATTRIBUTES for (const attribute of Object.keys(this.attributes)) { const idx = this.attributes[attribute]; @@ -87,6 +90,37 @@ class gltfPrimitive extends GltfObject console.log("Unknown attribute: " + attribute); } } + + // MORPH TARGETS + if (this.targets !== undefined) + { + let i = 0; + for (const target of this.targets) + { + for (const attribute of Object.keys(target)) + { + const idx = target[attribute]; + + switch (attribute) + { + case "POSITION": + this.defines.push("HAS_TARGET_POSITION" + i + " 1"); + this.glAttributes.push({ attribute: attribute, name: "a_Target_Position" + i, accessor: idx }); + break; + case "NORMAL": + this.defines.push("HAS_TARGET_NORMAL" + i + " 1"); + this.glAttributes.push({ attribute: attribute, name: "a_Target_Normal" + i, accessor: idx }); + break; + case "TANGENT": + this.defines.push("HAS_TARGET_TANGENT" + i + " 1"); + this.glAttributes.push({ attribute: attribute, name: "a_Target_Tangent" + i, accessor: idx }); + break; + } + } + + ++i; + } + } } getShaderIdentifier() diff --git a/src/renderer.js b/src/renderer.js index 122377cb..700aa5e0 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -208,7 +208,7 @@ class gltfRenderer this.shader.updateUniform("u_Exposure", this.parameters.exposure, false); this.shader.updateUniform("u_Camera", this.currentCameraPosition, false); - this.uploadSkin(gltf, node, primitive); + this.updateAnimationUniforms(gltf, node, primitive); if (material.doubleSided) { @@ -305,6 +305,7 @@ class gltfRenderer { if (!this.parameters.animationTimer.paused) { + // skinning if(node.skin !== undefined && primitive.hasWeights && primitive.hasJoints) { const skin = gltf.skins[node.skin]; @@ -312,10 +313,21 @@ class gltfRenderer vertDefines.push("USE_SKINNING 1"); vertDefines.push("JOINT_COUNT " + skin.jointMatrices.length); } + + // morphing + if(node.mesh !== undefined && primitive.targets.length > 0) + { + const mesh = gltf.meshes[node.mesh]; + if(mesh.weights !== undefined && mesh.weights.length > 0) + { + vertDefines.push("USE_MORPHING 1"); + vertDefines.push("WEIGHT_COUNT " + mesh.weights.length); + } + } } } - uploadSkin(gltf, node, primitive) + updateAnimationUniforms(gltf, node, primitive) { if (!this.parameters.animationTimer.paused) { @@ -326,6 +338,15 @@ class gltfRenderer this.shader.updateUniform("u_jointMatrix", skin.jointMatrices); this.shader.updateUniform("u_jointNormalMatrix", skin.jointNormalMatrices); } + + if(node.mesh !== undefined && primitive.targets.length > 0) + { + const mesh = gltf.meshes[node.mesh]; + if(mesh.weights !== undefined && mesh.weights.length > 0) + { + this.shader.updateUniformArray("u_morphWeights", mesh.weights); + } + } } } diff --git a/src/shader.js b/src/shader.js index 6292efd8..5980eec7 100644 --- a/src/shader.js +++ b/src/shader.js @@ -74,13 +74,13 @@ class gltfShader updateUniform(objectName, object, log = true) { - if (Array.isArray(object)) + if (object instanceof UniformStruct) { - this.updateUniformArray(objectName, object, log); + this.updateUniformStruct(objectName, object, log); } - else if (object instanceof UniformStruct) + else if (Array.isArray(object)) { - this.updateUniformStruct(objectName, object, log); + this.updateUniformArray(objectName, object, log); } else { @@ -103,9 +103,22 @@ class gltfShader let flat = []; - for(let i = 0; i < array.length; ++i) + if(Array.isArray(array[0]) || array[0].length !== undefined) + { + for (let i = 0; i < array.length; ++i) + { + flat.push.apply(flat, Array.from(array[i])); + } + } + else { - flat.push.apply(flat, Array.from(array[i])); + flat = array; + } + + if(flat.length === 0) + { + console.error("Failed to flatten uniform array " + uniformName); + return; } this.updateUniformValue(uniformName, flat, log); @@ -123,7 +136,6 @@ class gltfShader } // upload the values of a uniform with the given name using type resolve to get correct function call - // vec3 => WebGl.context.uniform3f(value) updateUniformValue(uniformName, value, log) { const uniform = this.uniforms.get(uniformName); @@ -131,12 +143,30 @@ class gltfShader if(uniform !== undefined) { switch (uniform.type) { - case WebGl.context.FLOAT: WebGl.context.uniform1f(uniform.loc, value); break; + case WebGl.context.FLOAT: + { + if(Array.isArray(value) || value instanceof Float32Array) + { + WebGl.context.uniform1fv(uniform.loc, value); + }else{ + WebGl.context.uniform1f(uniform.loc, value); + } + break; + } case WebGl.context.FLOAT_VEC2: WebGl.context.uniform2fv(uniform.loc, value); break; case WebGl.context.FLOAT_VEC3: WebGl.context.uniform3fv(uniform.loc, value); break; case WebGl.context.FLOAT_VEC4: WebGl.context.uniform4fv(uniform.loc, value); break; - case WebGl.context.INT: WebGl.context.uniform1i(uniform.loc, value); break; + case WebGl.context.INT: + { + if(Array.isArray(value) || value instanceof Uint32Array || value instanceof Int32Array) + { + WebGl.context.uniform1iv(uniform.loc, value); + }else{ + WebGl.context.uniform1i(uniform.loc, value); + } + break; + } case WebGl.context.INT_VEC2: WebGl.context.uniform2iv(uniform.loc, value); break; case WebGl.context.INT_VEC3: WebGl.context.uniform3iv(uniform.loc, value); break; case WebGl.context.INT_VEC4: WebGl.context.uniform4iv(uniform.loc, value); break; diff --git a/src/shaders/animation.glsl b/src/shaders/animation.glsl index ccc7173f..616af4b3 100644 --- a/src/shaders/animation.glsl +++ b/src/shaders/animation.glsl @@ -1,3 +1,55 @@ +#ifdef HAS_TARGET_POSITION0 +attribute vec3 a_Target_Position0; +#endif + +#ifdef HAS_TARGET_POSITION1 +attribute vec3 a_Target_Position1; +#endif + +#ifdef HAS_TARGET_POSITION2 +attribute vec3 a_Target_Position2; +#endif + +#ifdef HAS_TARGET_POSITION3 +attribute vec3 a_Target_Position3; +#endif + +#ifdef HAS_TARGET_NORMAL0 +attribute vec3 a_Target_Normal0; +#endif + +#ifdef HAS_TARGET_NORMAL1 +attribute vec3 a_Target_Normal1; +#endif + +#ifdef HAS_TARGET_NORMAL2 +attribute vec3 a_Target_Normal2; +#endif + +#ifdef HAS_TARGET_NORMAL3 +attribute vec3 a_Target_Normal3; +#endif + +#ifdef HAS_TARGET_TANGENT0 +attribute vec3 a_Target_Tangent0; +#endif + +#ifdef HAS_TARGET_TANGENT1 +attribute vec3 a_Target_Tangent1; +#endif + +#ifdef HAS_TARGET_TANGENT2 +attribute vec3 a_Target_Tangent2; +#endif + +#ifdef HAS_TARGET_TANGENT3 +attribute vec3 a_Target_Tangent3; +#endif + +#ifdef USE_MORPHING +uniform float u_morphWeights[WEIGHT_COUNT]; +#endif + #ifdef HAS_JOINT_SET1 attribute vec4 a_Joint1; #endif @@ -66,3 +118,75 @@ mat4 getSkinningNormalMatrix() return skin; } #endif // !USE_SKINNING + +#ifdef USE_MORPHING +vec4 getTargetPosition() +{ + vec4 pos = vec4(0); + +#ifdef HAS_TARGET_POSITION0 + pos.xyz += u_morphWeights[0] * a_Target_Position0; +#endif + +#ifdef HAS_TARGET_POSITION1 + pos.xyz += u_morphWeights[1] * a_Target_Position1; +#endif + +#ifdef HAS_TARGET_POSITION2 + pos.xyz += u_morphWeights[2] * a_Target_Position2; +#endif + +#ifdef HAS_TARGET_POSITION3 + pos.xyz += u_morphWeights[3] * a_Target_Position3; +#endif + + return pos; +} + +vec4 getTargetNormal() +{ + vec4 normal = vec4(0); + +#ifdef HAS_TARGET_NORMAL0 + normal.xyz += u_morphWeights[0] * a_Target_Normal0; +#endif + +#ifdef HAS_TARGET_NORMAL1 + normal.xyz += u_morphWeights[1] * a_Target_Normal1; +#endif + +#ifdef HAS_TARGET_NORMAL2 + normal.xyz += u_morphWeights[2] * a_Target_Normal2; +#endif + +#ifdef HAS_TARGET_NORMAL3 + normal.xyz += u_morphWeights[3] * a_Target_Normal3; +#endif + + return normal; +} + +vec4 getTargetTangent() +{ + vec4 tangent = vec4(0); + +#ifdef HAS_TARGET_TANGENT0 + tangent.xyz += u_morphWeights[0] * a_Target_Tangent0; +#endif + +#ifdef HAS_TARGET_TANGENT1 + tangent.xyz += u_morphWeights[1] * a_Target_Tangent1; +#endif + +#ifdef HAS_TARGET_TANGENT2 + tangent.xyz += u_morphWeights[2] * a_Target_Tangent2; +#endif + +#ifdef HAS_TARGET_TANGENT3 + tangent.xyz += u_morphWeights[3] * a_Target_Tangent3; +#endif + + return tangent; +} + +#endif // !USE_MORPHING diff --git a/src/shaders/primitive.vert b/src/shaders/primitive.vert index 89abc4a9..2142ec7b 100644 --- a/src/shaders/primitive.vert +++ b/src/shaders/primitive.vert @@ -48,7 +48,9 @@ vec4 getPosition() { vec4 pos = a_Position; - // TODO: morph before skinning +#ifdef USE_MORPHING + pos += getTargetPosition(); +#endif #ifdef USE_SKINNING pos = getSkinningMatrix() * pos; @@ -62,6 +64,10 @@ vec4 getNormal() { vec4 normal = a_Normal; +#ifdef USE_MORPHING + normal += getTargetNormal(); +#endif + #ifdef USE_SKINNING normal = getSkinningNormalMatrix() * normal; #endif @@ -75,6 +81,10 @@ vec4 getTangent() { vec4 tangent = a_Tangent; +#ifdef USE_MORPHING + tangent += getTargetTangent(); +#endif + #ifdef USE_SKINNING tangent = getSkinningMatrix() * tangent; #endif From e82b3b06eeafd0cea0dc1f3db91a53279c58f954 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 4 Mar 2019 14:37:33 +0100 Subject: [PATCH 18/27] normalize animated vertex attributes --- src/shaders/primitive.vert | 4 ++-- src/skin.js | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/shaders/primitive.vert b/src/shaders/primitive.vert index 2142ec7b..c14e6f3a 100644 --- a/src/shaders/primitive.vert +++ b/src/shaders/primitive.vert @@ -72,7 +72,7 @@ vec4 getNormal() normal = getSkinningNormalMatrix() * normal; #endif - return normal; + return normalize(normal); } #endif @@ -89,7 +89,7 @@ vec4 getTangent() tangent = getSkinningMatrix() * tangent; #endif - return tangent; + return normalize(tangent); } #endif diff --git a/src/skin.js b/src/skin.js index be671316..586fdab1 100644 --- a/src/skin.js +++ b/src/skin.js @@ -20,7 +20,13 @@ class gltfSkin extends GltfObject computeJoints(gltf) { - const skeleton = gltf.nodes[this.skeleton]; + let inverseRootTransform = mat4.create(); + if(this.skeleton !== undefined) + { + const skeleton = gltf.nodes[this.skeleton]; + inverseRootTransform = skeleton.inverseWorldTransform; + } + const ibmAccessor = gltf.accessors[this.inverseBindMatrices].getDeinterlacedView(gltf); this.jointMatrices = []; this.jointNormalMatrices = []; @@ -33,7 +39,7 @@ class gltfSkin extends GltfObject let jointMatrix = mat4.create(); let ibm = jsToGlSlice(ibmAccessor, i++ * 16, 16); mat4.mul(jointMatrix, node.worldTransform, ibm); - mat4.mul(jointMatrix, skeleton.inverseWorldTransform, jointMatrix); + mat4.mul(jointMatrix, inverseRootTransform, jointMatrix); this.jointMatrices.push(jointMatrix); let normalMatrix = mat4.create(); From 19dca277e22d8403515b5b8d83d31722b105b5e4 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 4 Mar 2019 15:34:57 +0100 Subject: [PATCH 19/27] fixed joint root transform --- src/renderer.js | 22 ++++++++++++++++++---- src/rendering_parameters.js | 2 ++ src/skin.js | 11 ++--------- src/user_interface.js | 2 ++ src/viewer.js | 13 ------------- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/renderer.js b/src/renderer.js index 700aa5e0..ce5fe242 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -159,6 +159,11 @@ class gltfRenderer let mesh = gltf.meshes[node.mesh]; if (mesh !== undefined) { + if(node.skin !== undefined) + { + this.updateSkin(gltf, node); + } + for (let primitive of mesh.primitives) { this.drawPrimitive(gltf, primitive, node, this.viewProjectionMatrix); @@ -166,6 +171,15 @@ class gltfRenderer } } + updateSkin(gltf, node) + { + if(this.parameters.skinning && gltf.skins !== undefined) // && !this.parameters.animationTimer.paused + { + const skin = gltf.skins[node.skin]; + skin.computeJoints(gltf, node); + } + } + // vertices with given material drawPrimitive(gltf, primitive, node, viewProjectionMatrix) { @@ -306,7 +320,7 @@ class gltfRenderer if (!this.parameters.animationTimer.paused) { // skinning - if(node.skin !== undefined && primitive.hasWeights && primitive.hasJoints) + if(this.parameters.skinning && node.skin !== undefined && primitive.hasWeights && primitive.hasJoints) { const skin = gltf.skins[node.skin]; @@ -315,7 +329,7 @@ class gltfRenderer } // morphing - if(node.mesh !== undefined && primitive.targets.length > 0) + if(this.parameters.morphing && node.mesh !== undefined && primitive.targets.length > 0) { const mesh = gltf.meshes[node.mesh]; if(mesh.weights !== undefined && mesh.weights.length > 0) @@ -331,7 +345,7 @@ class gltfRenderer { if (!this.parameters.animationTimer.paused) { - if(node.skin !== undefined && primitive.hasWeights && primitive.hasJoints) + if(this.parameters.skinning && node.skin !== undefined && primitive.hasWeights && primitive.hasJoints) { const skin = gltf.skins[node.skin]; @@ -339,7 +353,7 @@ class gltfRenderer this.shader.updateUniform("u_jointNormalMatrix", skin.jointNormalMatrices); } - if(node.mesh !== undefined && primitive.targets.length > 0) + if(this.parameters.morphing && node.mesh !== undefined && primitive.targets.length > 0) { const mesh = gltf.meshes[node.mesh]; if(mesh.weights !== undefined && mesh.weights.length > 0) diff --git a/src/rendering_parameters.js b/src/rendering_parameters.js index df8b4327..0d025682 100644 --- a/src/rendering_parameters.js +++ b/src/rendering_parameters.js @@ -28,6 +28,8 @@ class gltfRenderingParameters this.sceneIndex = 0; this.cameraIndex = UserCameraIndex; this.animationTimer = new AnimationTimer(); + this.skinning = true; + this.morphing = true; } userCameraActive() diff --git a/src/skin.js b/src/skin.js index 586fdab1..30fa8e1d 100644 --- a/src/skin.js +++ b/src/skin.js @@ -18,15 +18,8 @@ class gltfSkin extends GltfObject this.jointNormalMatrices = []; } - computeJoints(gltf) + computeJoints(gltf, parentNode) { - let inverseRootTransform = mat4.create(); - if(this.skeleton !== undefined) - { - const skeleton = gltf.nodes[this.skeleton]; - inverseRootTransform = skeleton.inverseWorldTransform; - } - const ibmAccessor = gltf.accessors[this.inverseBindMatrices].getDeinterlacedView(gltf); this.jointMatrices = []; this.jointNormalMatrices = []; @@ -39,7 +32,7 @@ class gltfSkin extends GltfObject let jointMatrix = mat4.create(); let ibm = jsToGlSlice(ibmAccessor, i++ * 16, 16); mat4.mul(jointMatrix, node.worldTransform, ibm); - mat4.mul(jointMatrix, inverseRootTransform, jointMatrix); + mat4.mul(jointMatrix, parentNode.inverseWorldTransform, jointMatrix); this.jointMatrices.push(jointMatrix); let normalMatrix = mat4.create(); diff --git a/src/user_interface.js b/src/user_interface.js index 0a44f767..b8c4b0a3 100644 --- a/src/user_interface.js +++ b/src/user_interface.js @@ -126,6 +126,8 @@ class gltfUserInterface const self = this; const animationFolder = this.gui.addFolder("Animation"); animationFolder.add(self, "playAnimation").name("Play").onChange(() => self.renderingParameters.animationTimer.toggle()); + animationFolder.add(self.renderingParameters, "skinning").name("Skinning"); + animationFolder.add(self.renderingParameters, "morphing").name("Morphing"); } initializeDebugSettings() diff --git a/src/viewer.js b/src/viewer.js index a5ccf4ae..11753fd3 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -332,8 +332,6 @@ class gltfViewer } scene.applyTransformHierarchy(gltf, transform); - - this.animateSkin(gltf); } animateNode(gltf) @@ -348,17 +346,6 @@ class gltfViewer } } - animateSkin(gltf) - { - if(gltf.skins !== undefined && !this.renderingParameters.animationTimer.paused) - { - for(const skin of gltf.skins) - { - skin.computeJoints(gltf); - } - } - } - initializeGui() { const gui = new gltfUserInterface( From 4f046f74f9317f4c6bfbb02da3553f11dbe01725 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 4 Mar 2019 15:57:24 +0100 Subject: [PATCH 20/27] fixed depth sorting for camera --- src/camera.js | 12 +++++++++--- src/renderer.js | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/camera.js b/src/camera.js index ef269e99..f0d3a61f 100644 --- a/src/camera.js +++ b/src/camera.js @@ -31,9 +31,10 @@ class gltfCamera extends GltfObject { super.initGl(gltf); + let cameraIndex = undefined; for (let i = 0; i < gltf.nodes.length; i++) { - const cameraIndex = gltf.nodes[i].camera; + cameraIndex = gltf.nodes[i].camera; if (cameraIndex === undefined) { continue; @@ -45,6 +46,11 @@ class gltfCamera extends GltfObject break; } } + + if(this.node === undefined) + { + console.error("Invalid node for camera " + cameraIndex); + } } fromJson(jsonCamera) @@ -62,14 +68,14 @@ class gltfCamera extends GltfObject } } - sortNodesByDepth(nodes) + sortNodesByDepth(gltf, nodes) { // precompute the distances to avoid their computation during sorting const sortedNodes = []; for (const node of nodes) { const modelView = mat4.create(); - mat4.multiply(modelView, this.getViewMatrix(), node.worldTransform); + mat4.multiply(modelView, this.getViewMatrix(gltf), node.worldTransform); const pos = vec3.create(); mat4.getTranslation(pos, modelView); diff --git a/src/renderer.js b/src/renderer.js index ce5fe242..41acdf75 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -126,7 +126,7 @@ class gltfRenderer let nodes = scene.gatherNodes(gltf); if(sortByDepth) { - nodes = currentCamera.sortNodesByDepth(nodes); + nodes = currentCamera.sortNodesByDepth(gltf, nodes); } for (const node of nodes) From c431f4b1f7bc65200ffc80626f4c1bd7b01ef358 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 4 Mar 2019 16:27:45 +0100 Subject: [PATCH 21/27] check for max attribute count when adding morph targets --- src/primitive.js | 14 ++++++++++++++ src/shaders/animation.glsl | 24 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/primitive.js b/src/primitive.js index 0c9feca6..abe40765 100644 --- a/src/primitive.js +++ b/src/primitive.js @@ -31,11 +31,19 @@ class gltfPrimitive extends GltfObject initGlForMembers(this, gltf); + const maxAttributes = WebGl.context.getParameter(WebGl.context.MAX_VERTEX_ATTRIBS); + // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes // VERTEX ATTRIBUTES for (const attribute of Object.keys(this.attributes)) { + if(this.glAttributes.length >= maxAttributes) + { + console.error("To many vertex attributes for this primitive, skipping " + attribute); + break; + } + const idx = this.attributes[attribute]; switch (attribute) { @@ -97,6 +105,12 @@ class gltfPrimitive extends GltfObject let i = 0; for (const target of this.targets) { + if(this.glAttributes.length + 3 > maxAttributes) + { + console.error("To many vertex attributes for this primitive, skipping target " + i); + break; + } + for (const attribute of Object.keys(target)) { const idx = target[attribute]; diff --git a/src/shaders/animation.glsl b/src/shaders/animation.glsl index 616af4b3..a91bf522 100644 --- a/src/shaders/animation.glsl +++ b/src/shaders/animation.glsl @@ -14,6 +14,10 @@ attribute vec3 a_Target_Position2; attribute vec3 a_Target_Position3; #endif +#ifdef HAS_TARGET_POSITION4 +attribute vec3 a_Target_Position4; +#endif + #ifdef HAS_TARGET_NORMAL0 attribute vec3 a_Target_Normal0; #endif @@ -30,6 +34,10 @@ attribute vec3 a_Target_Normal2; attribute vec3 a_Target_Normal3; #endif +#ifdef HAS_TARGET_NORMAL4 +attribute vec3 a_Target_Normal4; +#endif + #ifdef HAS_TARGET_TANGENT0 attribute vec3 a_Target_Tangent0; #endif @@ -46,6 +54,10 @@ attribute vec3 a_Target_Tangent2; attribute vec3 a_Target_Tangent3; #endif +#ifdef HAS_TARGET_TANGENT4 +attribute vec3 a_Target_Tangent4; +#endif + #ifdef USE_MORPHING uniform float u_morphWeights[WEIGHT_COUNT]; #endif @@ -140,6 +152,10 @@ vec4 getTargetPosition() pos.xyz += u_morphWeights[3] * a_Target_Position3; #endif +#ifdef HAS_TARGET_POSITION4 + pos.xyz += u_morphWeights[4] * a_Target_Position4; +#endif + return pos; } @@ -163,6 +179,10 @@ vec4 getTargetNormal() normal.xyz += u_morphWeights[3] * a_Target_Normal3; #endif +#ifdef HAS_TARGET_NORMAL4 + normal.xyz += u_morphWeights[4] * a_Target_Normal4; +#endif + return normal; } @@ -186,6 +206,10 @@ vec4 getTargetTangent() tangent.xyz += u_morphWeights[3] * a_Target_Tangent3; #endif +#ifdef HAS_TARGET_TANGENT4 + tangent.xyz += u_morphWeights[4] * a_Target_Tangent4; +#endif + return tangent; } From a6276ff560e3eebd3671e41ceccf62050858e983 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Mon, 4 Mar 2019 16:59:10 +0100 Subject: [PATCH 22/27] fixed accumulating vertex defines --- src/renderer.js | 4 +++- src/viewer.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/renderer.js b/src/renderer.js index 41acdf75..896d5b78 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -189,8 +189,10 @@ class gltfRenderer //select shader permutation, compile and link program. - let vertDefines = primitive.getDefines(); + let vertDefines = []; this.pushVertParameterDefines(vertDefines, gltf, node, primitive); + vertDefines = primitive.getDefines().concat(vertDefines); + let fragDefines = material.getDefines().concat(vertDefines); this.pushFragParameterDefines(fragDefines); diff --git a/src/viewer.js b/src/viewer.js index 11753fd3..68d83f66 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -249,7 +249,7 @@ class gltfViewer this.userCamera.fitViewToScene(gltf, this.renderingParameters.sceneIndex); // FOR DEBUGGING - this.renderingParameters.animationTimer.start(); + //this.renderingParameters.animationTimer.start(); } render() From 0f1fba461f05d820e381e7cf80295d0581d2b3ed Mon Sep 17 00:00:00 2001 From: David Labode Date: Mon, 4 Mar 2019 19:20:53 +0100 Subject: [PATCH 23/27] fixed animation bug for channels with only one keyframe --- src/interpolator.js | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/interpolator.js b/src/interpolator.js index dc9d91e7..ce00648c 100644 --- a/src/interpolator.js +++ b/src/interpolator.js @@ -74,30 +74,39 @@ class gltfInterpolator } let nextKey = undefined; - const maxKeyTime = input[input.length - 1]; - t = t % maxKeyTime; // loop animation - if(this.prevT > t) + if(input.length !== 1) { - this.prevKey = 0; - } + const maxKeyTime = input[input.length - 1]; + t = t % maxKeyTime; // loop animation - this.prevT = t; + if(this.prevT > t) + { + this.prevKey = 0; + } - for (let i = this.prevKey; i < input.length; ++i) // find current keyframe interval - { - if (t <= input[i]) + this.prevT = t; + + for (let i = this.prevKey; i < input.length; ++i) // find current keyframe interval { - nextKey = i; - break; + if (t <= input[i]) + { + nextKey = i; + break; + } } - } - nextKey = clamp(nextKey, 1, input.length - 1); - this.prevKey = clamp(nextKey - 1, 0, nextKey); + nextKey = clamp(nextKey, 1, input.length - 1); + this.prevKey = clamp(nextKey - 1, 0, nextKey); + + const keyDelta = input[nextKey] - input[this.prevKey]; + t = (t - input[this.prevKey]) / keyDelta; // normalize t to 0..1 - const keyDelta = input[nextKey] - input[this.prevKey]; - t = (t - input[this.prevKey]) / keyDelta; // normalize t to 0..1 + }else{ + t = 0; + nextKey = 0; + this.prevKey = 0; + } if(channel.target.path === InterpolationPath.ROTATION) { From a2c96e816f003d5541d4ffcb8670ea6eb065e747 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Tue, 5 Mar 2019 09:58:31 +0100 Subject: [PATCH 24/27] support for 8 position, 4 normal and 4 tangent morph targets --- src/renderer.js | 2 +- src/shaders/animation.glsl | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/renderer.js b/src/renderer.js index 896d5b78..556dbbb8 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -337,7 +337,7 @@ class gltfRenderer if(mesh.weights !== undefined && mesh.weights.length > 0) { vertDefines.push("USE_MORPHING 1"); - vertDefines.push("WEIGHT_COUNT " + mesh.weights.length); + vertDefines.push("WEIGHT_COUNT " + Math.min(mesh.weights.length, 8)); } } } diff --git a/src/shaders/animation.glsl b/src/shaders/animation.glsl index a91bf522..866d9203 100644 --- a/src/shaders/animation.glsl +++ b/src/shaders/animation.glsl @@ -18,6 +18,18 @@ attribute vec3 a_Target_Position3; attribute vec3 a_Target_Position4; #endif +#ifdef HAS_TARGET_POSITION5 +attribute vec3 a_Target_Position5; +#endif + +#ifdef HAS_TARGET_POSITION6 +attribute vec3 a_Target_Position6; +#endif + +#ifdef HAS_TARGET_POSITION7 +attribute vec3 a_Target_Position7; +#endif + #ifdef HAS_TARGET_NORMAL0 attribute vec3 a_Target_Normal0; #endif @@ -34,10 +46,6 @@ attribute vec3 a_Target_Normal2; attribute vec3 a_Target_Normal3; #endif -#ifdef HAS_TARGET_NORMAL4 -attribute vec3 a_Target_Normal4; -#endif - #ifdef HAS_TARGET_TANGENT0 attribute vec3 a_Target_Tangent0; #endif @@ -54,10 +62,6 @@ attribute vec3 a_Target_Tangent2; attribute vec3 a_Target_Tangent3; #endif -#ifdef HAS_TARGET_TANGENT4 -attribute vec3 a_Target_Tangent4; -#endif - #ifdef USE_MORPHING uniform float u_morphWeights[WEIGHT_COUNT]; #endif From 8efa3163f2e13cdaaf6e266939545a01f8bc07e1 Mon Sep 17 00:00:00 2001 From: Fabian Wahlster Date: Tue, 5 Mar 2019 10:18:02 +0100 Subject: [PATCH 25/27] fixed output length 1 edge case --- src/interpolator.js | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/interpolator.js b/src/interpolator.js index ce00648c..a4239b40 100644 --- a/src/interpolator.js +++ b/src/interpolator.js @@ -68,45 +68,37 @@ class gltfInterpolator const input = gltf.accessors[sampler.input].getDeinterlacedView(gltf); const output = gltf.accessors[sampler.output].getDeinterlacedView(gltf); - if(output.length === 1) // no interpolation for single keyFrame animations + if(output.length === stride) // no interpolation for single keyFrame animations { return jsToGlSlice(output, 0, stride); } let nextKey = undefined; - if(input.length !== 1) - { - const maxKeyTime = input[input.length - 1]; - t = t % maxKeyTime; // loop animation + const maxKeyTime = input[input.length - 1]; + t = t % maxKeyTime; // loop animation - if(this.prevT > t) - { - this.prevKey = 0; - } + if (this.prevT > t) + { + this.prevKey = 0; + } - this.prevT = t; + this.prevT = t; - for (let i = this.prevKey; i < input.length; ++i) // find current keyframe interval + for (let i = this.prevKey; i < input.length; ++i) // find current keyframe interval + { + if (t <= input[i]) { - if (t <= input[i]) - { - nextKey = i; - break; - } + nextKey = i; + break; } + } - nextKey = clamp(nextKey, 1, input.length - 1); - this.prevKey = clamp(nextKey - 1, 0, nextKey); - - const keyDelta = input[nextKey] - input[this.prevKey]; - t = (t - input[this.prevKey]) / keyDelta; // normalize t to 0..1 + nextKey = clamp(nextKey, 1, input.length - 1); + this.prevKey = clamp(nextKey - 1, 0, nextKey); - }else{ - t = 0; - nextKey = 0; - this.prevKey = 0; - } + const keyDelta = input[nextKey] - input[this.prevKey]; + t = (t - input[this.prevKey]) / keyDelta; // normalize t to 0..1 if(channel.target.path === InterpolationPath.ROTATION) { From 48fbd72291df9c14b1fa394bf8b03c4c41284ff7 Mon Sep 17 00:00:00 2001 From: David Labode Date: Tue, 5 Mar 2019 11:27:47 +0100 Subject: [PATCH 26/27] fixed scene rescaling during animation --- src/node.js | 22 ++++++++-------------- src/viewer.js | 29 ++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/node.js b/src/node.js index 95a4fbf4..755a723d 100644 --- a/src/node.js +++ b/src/node.js @@ -14,9 +14,9 @@ class gltfNode extends GltfObject this.camera = undefined; this.children = []; this.matrix = undefined; - this.rotation = [0, 0, 0, 1]; - this.scale = [1, 1, 1]; - this.translation = [0, 0, 0]; + this.rotation = jsToGl([0, 0, 0, 1]); + this.scale = jsToGl([1, 1, 1]); + this.translation = jsToGl([0, 0, 0]); this.name = undefined; this.mesh = undefined; this.skin = undefined; @@ -89,8 +89,9 @@ class gltfNode extends GltfObject resetTransform() { - this.rotation = [0, 0, 0, 1]; - this.translation = [0, 0, 0]; + this.rotation = jsToGl([0, 0, 0, 1]); + this.scale = jsToGl([1, 1, 1]); + this.translation = jsToGl([0, 0, 0]); this.changed = true; } @@ -98,15 +99,8 @@ class gltfNode extends GltfObject { if(this.transform === undefined || this.changed) { - if (this.matrix !== undefined) - { - this.transform = this.matrix; - } - else - { - this.transform = mat4.create(); - mat4.fromRotationTranslationScale(this.transform, this.rotation, this.translation, this.scale); - } + this.transform = mat4.create(); + mat4.fromRotationTranslationScale(this.transform, this.rotation, this.translation, this.scale); this.changed = false; } diff --git a/src/viewer.js b/src/viewer.js index 68d83f66..834e4a43 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -44,6 +44,10 @@ class gltfViewer this.loadingTimer = new Timer(); this.gltf = undefined; + this.scaledSceneIndex = 0; + this.scaledGltfChanged = true; + this.sceneScaleFactor = 1; + this.renderingParameters = new gltfRenderingParameters(environmentMap); this.userCamera = new UserCamera(); this.currentlyRendering = false; @@ -244,12 +248,10 @@ class gltfViewer this.gltf = gltf; this.currentlyRendering = true; + this.scaledGltfChanged = true; this.prepareSceneForRendering(gltf); this.userCamera.fitViewToScene(gltf, this.renderingParameters.sceneIndex); - - // FOR DEBUGGING - //this.renderingParameters.animationTimer.start(); } render() @@ -278,9 +280,6 @@ class gltfViewer const scene = self.gltf.scenes[self.renderingParameters.sceneIndex]; - // if transformations happen at runtime, we need to apply the transform hierarchy here - // scene.applyTransformHierarchy(gltf); - let alphaScene = scene.getSceneWithAlphaMode(self.gltf, 'BLEND'); // get non opaque if (alphaScene.nodes.length > 0) { @@ -325,13 +324,25 @@ class gltfViewer scene.applyTransformHierarchy(gltf); const transform = mat4.create(); - if (this.renderingParameters.userCameraActive()) + + let scaled = false; + if (this.renderingParameters.userCameraActive() && (this.scaledGltfChanged || this.scaledSceneIndex != this.renderingParameters.sceneIndex)) { - const scaleFactor = getScaleFactor(gltf, this.renderingParameters.sceneIndex); - mat4.scale(transform, transform, vec3.fromValues(scaleFactor, scaleFactor, scaleFactor)); + this.sceneScaleFactor = getScaleFactor(gltf, this.renderingParameters.sceneIndex); + + scaled = true; + this.scaledGltfChanged = false; + this.scaledSceneIndex = this.renderingParameters.sceneIndex; + console.log("Rescaled scene " + this.scaledSceneIndex + " by " + this.sceneScaleFactor); } + mat4.scale(transform, transform, vec3.fromValues(this.sceneScaleFactor, this.sceneScaleFactor, this.sceneScaleFactor)); scene.applyTransformHierarchy(gltf, transform); + + if(scaled) + { + this.userCamera.fitViewToScene(gltf, this.renderingParameters.sceneIndex); + } } animateNode(gltf) From 6e4ee2416da88e1baaa505af0bd6e7e43815df4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Schmith=C3=BCsen?= Date: Tue, 5 Mar 2019 15:41:05 +0100 Subject: [PATCH 27/27] remove debug log --- src/viewer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/viewer.js b/src/viewer.js index 834e4a43..7db1fd19 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -333,7 +333,6 @@ class gltfViewer scaled = true; this.scaledGltfChanged = false; this.scaledSceneIndex = this.renderingParameters.sceneIndex; - console.log("Rescaled scene " + this.scaledSceneIndex + " by " + this.sceneScaleFactor); } mat4.scale(transform, transform, vec3.fromValues(this.sceneScaleFactor, this.sceneScaleFactor, this.sceneScaleFactor));