Skip to content

Commit

Permalink
Unified skeleton animation and morph blend to AnimatorComponent
Browse files Browse the repository at this point in the history
  • Loading branch information
Codeboy-cn committed Jun 25, 2024
1 parent c06e5f2 commit d1c57c8
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 36 deletions.
35 changes: 26 additions & 9 deletions samples/animation/Sample_MorphTarget.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { GUIHelp } from "@orillusion/debug/GUIHelp";
import { Object3D, Scene3D, Engine3D, AtmosphericComponent, webGPUContext, HoverCameraController, View3D, DirectLight, KelvinUtil, Vector3, MorphTargetBlender, Entity, CameraUtil } from "@orillusion/core";
import { Object3D, Scene3D, Engine3D, AtmosphericComponent, webGPUContext, HoverCameraController, View3D, DirectLight, KelvinUtil, Vector3, MorphTargetBlender, Entity, CameraUtil, AnimatorComponent } from "@orillusion/core";
import { GUIUtil } from "@samples/utils/GUIUtil";

// Sample of how to control the morphtarget animation
Expand Down Expand Up @@ -55,26 +55,43 @@ export class Sample_MorphTarget {
model.x = -30.0;
this.scene.addChild(model);

let animator = model.addComponent(AnimatorComponent);

GUIHelp.addFolder('morph controller');
// register MorphTargetBlender component
let blendShapeComponent = model.addComponent(MorphTargetBlender);
let targetRenderers = blendShapeComponent.cloneMorphRenderers();

let targetRenderers = animator.cloneMorphRenderers();

// bind influenceData to gui
for (let key in targetRenderers) {
this.influenceData[key] = 0.0;
GUIHelp.add(this.influenceData, key, 0, 1, 0.01).onChange((v) => {
this.influenceData[key] = v;
let list = blendShapeComponent.getMorphRenderersByKey(key);
for (let renderer of list) {
renderer.setMorphInfluence(key, v);
}
animator.updateBlendShape(["blendShape", "blendShape", key], "blendShape.blendShape." + key, v);
});
}

GUIHelp.open();
GUIHelp.endFolder();

// GUIHelp.addFolder('morph controller');
// // register MorphTargetBlender component
// let blendShapeComponent = model.addComponent(MorphTargetBlender);
// let targetRenderers = blendShapeComponent.cloneMorphRenderers();

// // bind influenceData to gui
// for (let key in targetRenderers) {
// this.influenceData[key] = 0.0;
// GUIHelp.add(this.influenceData, key, 0, 1, 0.01).onChange((v) => {
// this.influenceData[key] = v;
// let list = blendShapeComponent.getMorphRenderersByKey(key);
// for (let renderer of list) {
// renderer.setMorphInfluence(key, v);
// }
// });
// }

// GUIHelp.open();
// GUIHelp.endFolder();

// print hierarchy
this.printHierarchy(model);
}
Expand Down
59 changes: 34 additions & 25 deletions src/components/anim/AnimatorComponent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Engine3D, Matrix4, Object3D, PrefabAvatarData, Quaternion, RenderNode, SkinnedMeshRenderer2, StorageGPUBuffer, Time, Vector3, Vector4, View3D } from "../..";
import { Engine3D, Matrix4, MeshRenderer, Object3D, PrefabAvatarData, Quaternion, RenderNode, RendererMask, RendererMaskUtil, SkinnedMeshRenderer2, StorageGPUBuffer, Time, Vector3, Vector4, View3D } from "../..";
import { PropertyAnimationClip } from "../../math/AnimationCurveClip";
import { RegisterComponent } from "../../util/SerializeDecoration";
import { ComponentBase } from "../ComponentBase";
Expand Down Expand Up @@ -38,10 +38,24 @@ export class AnimatorComponent extends ComponentBase {
this._clipsMap = new Map<string, PropertyAnimationClip>();
this._clips = [];
this._clipsState = [];

this._rendererList = this.object3D.getComponentsInChild(SkinnedMeshRenderer2);
let mrs = this.object3D.getComponentsInChild(MeshRenderer);
for (let mr of mrs) {
let o = mr as any;
o.blendShape = mr.morphData;
this._rendererList.push(o);
}
for (const renderer of this._rendererList) {
let hasMorphTarget = RendererMaskUtil.hasMask(renderer.rendererMask, RendererMask.MorphTarget);
if (hasMorphTarget) {
renderer.selfCloneMaterials('MORPH_TARGET_UUID');
}
}
}

public start(): void {
this._rendererList = this.object3D.getComponentsInChild(SkinnedMeshRenderer2);
// this._rendererList = this.object3D.getComponentsInChild(SkinnedMeshRenderer2);
}

private debug() {
Expand Down Expand Up @@ -278,27 +292,7 @@ export class AnimatorComponent extends ComponentBase {

let x = this._currentBlendAnimClip.floatCurves.get(key).getValue(this._blendShapeTime) as number;
let value = x / 100;
for (const renderer of this._rendererList) {
if (renderer.blendShape) {
let property: any = this.propertyCache.get(renderer);
if (property && key in property) {
property[key](value);
} else {
property = renderer;
for (const att of attributes) {
if (!property[att])
break;
property = property[att];
}
if (!property || property == renderer) break;

if (!this.propertyCache.get(renderer))
this.propertyCache.set(renderer, {})
this.propertyCache.get(renderer)[key] = property;
property(value);
}
}
}
this.updateBlendShape(attributes, key, value);
}
}
}
Expand All @@ -313,11 +307,14 @@ export class AnimatorComponent extends ComponentBase {
} else {
property = renderer;
for (const att of attributes) {
if (!property[att])
if (!property[att]) {
property = null;
break;
}
property = property[att];
}
if (!property || property == renderer) break;
if (!property || property == renderer)
continue;

if (!this.propertyCache.get(renderer))
this.propertyCache.set(renderer, {})
Expand Down Expand Up @@ -417,6 +414,18 @@ export class AnimatorComponent extends ComponentBase {
}
return null;
}

public cloneMorphRenderers(): { [key: string]: SkinnedMeshRenderer2[] } {
let dst: { [key: string]: SkinnedMeshRenderer2[] } = {};
for (const renderer of this._rendererList) {
for (const key in renderer.geometry.morphTargetDictionary) {
let renderList = dst[key] || [];
renderList.push(renderer);
dst[key] = renderList;
}
}
return dst;
}
}

export class PropertyAnimationClipState {
Expand Down
2 changes: 1 addition & 1 deletion src/components/renderer/MeshRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class MeshRenderer extends RenderNode {
* Enabling this option allows the grid to display any shadows cast on the grid.
*/
public receiveShadow: boolean;
protected morphData: MorphTargetData;
public morphData: MorphTargetData;

constructor() {
super();
Expand Down
33 changes: 32 additions & 1 deletion src/loader/parser/gltf/GLTFSubParserConverter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AnimatorComponent, LitMaterial, Material, Matrix4, PropertyAnimationClip, SkinnedMeshRenderer2 } from "../../..";
import { AnimatorComponent, BlendShapeData, BlendShapePropertyData, LitMaterial, Material, Matrix4, PropertyAnimationClip, SkinnedMeshRenderer2 } from "../../..";
import { Engine3D } from "../../../Engine3D";
import { SkeletonAnimationComponent } from "../../../components/SkeletonAnimationComponent";
import { DirectLight } from "../../../components/lights/DirectLight";
Expand Down Expand Up @@ -388,6 +388,37 @@ export class GLTFSubParserConverter {
}
}

// BlendShapeData
if (primitive.morphTargetsRelative) {
let blendShapeData = new BlendShapeData();
let targetNames = primitive.targetNames;
if (targetNames && targetNames.length > 0) {
blendShapeData.shapeNames = [];
blendShapeData.shapeIndexs = [];
for (let i = 0; i < targetNames.length; i++) {
blendShapeData.shapeNames.push(targetNames[i])
blendShapeData.shapeIndexs.push(i);
}
}
blendShapeData.vertexCount = attribArrays['position'].data.length / 3;
blendShapeData.blendCount = blendShapeData.shapeNames.length;
blendShapeData.blendShapePropertyDatas = [];
blendShapeData.blendShapeMap = new Map<string, BlendShapePropertyData>();
for (let i = 0; i < blendShapeData.blendCount; i++) {
let propertyData = new BlendShapePropertyData();
propertyData.shapeName = blendShapeData.shapeNames[i];
propertyData.shapeIndex = blendShapeData.shapeIndexs[i];
propertyData.frameCount = 1;
propertyData.blendPositionList = attribArrays[GLTFType.MORPH_POSITION_PREFIX + i].data;
propertyData.blendNormalList = attribArrays[GLTFType.MORPH_NORMAL_PREFIX + i].data;

blendShapeData.blendShapePropertyDatas.push(propertyData);
blendShapeData.blendShapeMap.set(propertyData.shapeName, propertyData);
}

geometry.blendShapeData = blendShapeData;
}

// geometry.geometrySource = new SerializeGeometrySource().setGLTFGeometry(this.initUrl, name);
//morphTarget
geometry.morphTargetsRelative = primitive.morphTargetsRelative;
Expand Down

0 comments on commit d1c57c8

Please sign in to comment.