Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Animation #183

Merged
28 commits merged into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1356a51
skinning attributes
Feb 25, 2019
cff5070
support for 2 joint/weight sets
Feb 25, 2019
18719ed
skin init
Feb 25, 2019
92dfb25
added animation classes and interpolator
UX3D-labode Feb 26, 2019
712e69e
wip animation
UX3D-labode Feb 26, 2019
e9fd59c
wip node animation
UX3D-labode Feb 26, 2019
8d55c51
fix primitive.vert tangents and normals
UX3D-labode Feb 27, 2019
ff9d06c
wip animation skin
UX3D-labode Feb 27, 2019
91e0edc
wip animation
UX3D-labode Feb 28, 2019
4c60aff
fix getDeinterlacedView
UX3D-labode Mar 1, 2019
10d34a5
fix attributes
UX3D-labode Mar 1, 2019
f10b110
flatten uniform arrays
UX3D-labode Mar 1, 2019
3fa2d44
fix flatten uniform arrays
UX3D-labode Mar 1, 2019
f946c35
added skinning support
UX3D-labode Mar 1, 2019
d2bb7aa
fixed parsing weights
Mar 4, 2019
12ce0a9
refactored skinning into include glsl
Mar 4, 2019
9ecb15c
Morphing wip
Mar 4, 2019
e82b3b0
normalize animated vertex attributes
Mar 4, 2019
19dca27
fixed joint root transform
Mar 4, 2019
4f046f7
fixed depth sorting for camera
Mar 4, 2019
c431f4b
check for max attribute count when adding morph targets
Mar 4, 2019
a6276ff
fixed accumulating vertex defines
Mar 4, 2019
0f1fba4
fixed animation bug for channels with only one keyframe
UX3D-labode Mar 4, 2019
a2c96e8
support for 8 position, 4 normal and 4 tangent morph targets
Mar 5, 2019
8efa316
fixed output length 1 edge case
Mar 5, 2019
48fbd72
fixed scene rescaling during animation
UX3D-labode Mar 5, 2019
abe664c
Merge remote-tracking branch 'origin/reference-viewer' into animation
UX3D-labode Mar 5, 2019
6e4ee24
remove debug log
Mar 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 85 additions & 24 deletions src/accessor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { WebGl } from './webgl.js';
import { GltfObject } from './gltf_object.js';
import { jsToGlSlice} from './utils.js';

class gltfAccessor extends GltfObject
{
Expand All @@ -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
Expand All @@ -33,37 +34,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;
}
}

Expand All @@ -75,6 +77,65 @@ class gltfAccessor extends GltfObject
return this.typedView;
}

getDeinterlacedView(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 = this.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);
func = 'getInt8';
break;
case WebGl.context.UNSIGNED_BYTE:
this.filteredView = new Uint8Array(arrayLength);
func = 'getUint8';
break;
case WebGl.context.SHORT:
this.filteredView = new Int16Array(arrayLength);
func = 'getInt16';
break;
case WebGl.context.UNSIGNED_SHORT:
this.filteredView = new Uint16Array(arrayLength);
func = 'getUint16';
break;
case WebGl.context.UNSIGNED_INT:
this.filteredView = new Uint32Array(arrayLength);
func = 'getUint32';
break;
case WebGl.context.FLOAT:
this.filteredView = new Float32Array(arrayLength);
func = 'getFloat32';
break;
}

for(let i = 0; i < arrayLength; ++i)
{
let offset = Math.floor(i/componentCount) * stride + (i % componentCount) * componentSize;
this.filteredView[i] = dv[func](offset, true);
}
}

return this.filteredView;
}

getComponentCount()
{
return CompononentCount.get(this.type);
Expand Down
71 changes: 71 additions & 0 deletions src/animation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { GltfObject } from './gltf_object.js';
import { objectsFromJsons } from './utils.js';
import { gltfAnimationChannel, InterpolationPath } from './channel.js';
import { gltfAnimationSampler } from './animation_sampler.js';
import { gltfInterpolator } from './interpolator.js';

class gltfAnimation extends GltfObject
{
constructor()
{
super();
this.channels = [];
this.samplers = [];

// not gltf
this.interpolators = [];
}

fromJson(jsonAnimation)
{
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());
}
}

advance(gltf, totalTime)
{
if(this.channels === undefined)
{
return;
}

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.applyTranslation(interpolator.interpolate(gltf, channel, sampler, totalTime, 3));
break;
case InterpolationPath.ROTATION:
node.applyRotation(interpolator.interpolate(gltf, channel, sampler, totalTime, 4));
break;
case InterpolationPath.SCALE:
node.applyScale(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;
}
}
}
}

export { gltfAnimation };
21 changes: 21 additions & 0 deletions src/animation_sampler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GltfObject } from './gltf_object.js';

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 };
10 changes: 8 additions & 2 deletions src/camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -45,6 +46,11 @@ class gltfCamera extends GltfObject
break;
}
}

if(this.node === undefined)
{
console.error("Invalid node for camera " + cameraIndex);
}
}

fromJson(jsonCamera)
Expand All @@ -62,7 +68,7 @@ class gltfCamera extends GltfObject
}
}

sortNodesByDepth(nodes, gltf)
sortNodesByDepth(gltf, nodes)
{
// precompute the distances to avoid their computation during sorting
const sortedNodes = [];
Expand Down
21 changes: 21 additions & 0 deletions src/channel.js
Original file line number Diff line number Diff line change
@@ -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 };
6 changes: 6 additions & 0 deletions src/gltf.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ 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';
import { gltfSkin } from './skin.js';

class glTF extends GltfObject
{
Expand All @@ -33,6 +35,8 @@ class glTF extends GltfObject
this.buffers = [];
this.bufferViews = [];
this.materials = [];
this.animations = [];
this.skins = [];
this.path = file;
}

Expand All @@ -58,6 +62,8 @@ 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.skins = objectsFromJsons(json.skins, gltfSkin);

this.materials.push(gltfMaterial.createDefault());
this.samplers.push(gltfSampler.createDefault());
Expand Down
Loading