Skip to content

Commit

Permalink
[WIP] Feature: NewGenAnim (#9331)
Browse files Browse the repository at this point in the history
* NewGenAnim

* Fix/Optimize simple directional blending

* Class transition by type (#29)

* Transition & Gradient transition (#30)

* The default ExitCondition should be 1.0

* Do correct graph eval

* Fix PoseSubgraph connect() signature order (#31)

* Graph defaults test (#32)

* Conditions (#33)

* Trigger (#34)

* Transition should only be considered when satemachine exited (#35)

* Add layer.name (#36)

* Fix some behaviours; add API to query poses and transition (#37)

Fix single frame exit condition behaviours; Fix pose -> subgraph behaviours

* Support self transition; refactor sub state machine implementation (#38)

* Temp

* Flat whole layer statemachine

* Add test for test reset triggers

* Allow self transition

* Self transition test

* Fix deserialize (#39)

* update animgraph template (#40)

* The inheritance of any state (#41)

* StateMachine events (#42)

* Fix state machine events; Allow multiple transitions between two nodes (#43)

* Transition priority test (#44)

* Optimize variable bind (#45)

* Fix variable bind errors (#46)

* Expose blend algorithm to editor (#47)

* Cont (#48)

* update animgraph template (#49)

* update animgraph template

* update animgraph template

* Pose speed (#50)

* blend tree add EditorExtendable data (#51)

* Correct pose blend duration (#52)

* Export some types (#53)

* animation pose EditorExtendable (#54)

(cherry picked from commit d5dd5f3)

* Implement clone() (#55)

* Fix trigger condition serialization (#56)

* Add variable type: integer (#57)

* Optimize 0 weight case (#58)

* Relative duration (#59)

* update_hdr_skybox (#9230)

* Rename.. (#60)

* Optimize animation blend item export (#61)

* Fix Type (#62)

* modify aniamtion graph template data (#63)

* modify aniamtion graph template data

* add animation graph component template

* Fix serialize issue (#64)

* Optimize (#65)

* Remove MotionState.startRatio, loop (#66)

* Update (#67)

* Fix circular reference (#68)

* CR (#69)

* Fix exit time greater than 1 (#70)

Co-authored-by: 黄森斌 <arsen2010@126.com>
Co-authored-by: linshunjun <49218738+linshunjun@users.noreply.github.com>
  • Loading branch information
3 people committed Oct 11, 2021
1 parent f54c8f5 commit 8488972
Show file tree
Hide file tree
Showing 73 changed files with 6,072 additions and 6 deletions.
4 changes: 4 additions & 0 deletions EngineErrorMap.md
Original file line number Diff line number Diff line change
Expand Up @@ -3141,3 +3141,7 @@ Can not encode CCON binary: lack of text encoder.
### 13104

Can not decode CCON binary: lack of text decoder.

### 14000

Graph update has been interrupted since too many transitions(greater than %s) occurred during one frame.
10 changes: 9 additions & 1 deletion cocos/3d/skeletal-animation/skeletal-animation-blending.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
* @hidden
*/

import { Vec3, Quat } from '../../core/math';
import { DEBUG } from 'internal:constants';
import { Vec3, Quat, approx } from '../../core/math';
import { Node } from '../../core/scene-graph';
import { RuntimeBinding } from '../../core/animation/tracks/track';
import { assertIsTrue } from '../../core/data/utils/asserts';

export class BlendStateBuffer {
private _nodeBlendStates: Map<Node, NodeBlendState> = new Map();
Expand Down Expand Up @@ -161,6 +163,9 @@ class Vec3PropertyBlendState extends PropertyBlendState<Vec3> {
Vec3.scaleAndAdd(blendedValue, blendedValue, value, weight);
}
this.blendedWeight += weight;
if (DEBUG && this.blendedWeight > 1.0) {
assertIsTrue(approx(this.blendedWeight, 1.0, 1e-6));
}
}

public reset () {
Expand All @@ -186,6 +191,9 @@ class QuatPropertyBlendState extends PropertyBlendState<Quat> {
Quat.slerp(blendedValue, blendedValue, value, t);
}
this.blendedWeight += weight;
if (DEBUG && this.blendedWeight > 1.0) {
assertIsTrue(approx(this.blendedWeight, 1.0, 1e-6));
}
}

public reset () {
Expand Down
6 changes: 4 additions & 2 deletions cocos/core/animation/animation-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ import { legacyCC } from '../global-exports';
import { ccenum } from '../value-types/enum';
import { assertIsNonNullable, assertIsTrue } from '../data/utils/asserts';
import { debug } from '../platform/debug';
import { SkeletonMask } from './skeleton-mask';
import { PoseOutput } from './pose-output';
import { BlendStateBuffer } from '../../3d/skeletal-animation/skeletal-animation-blending';

/**
* @en The event type supported by Animation
Expand Down Expand Up @@ -322,7 +324,7 @@ export class AnimationState extends Playable {
return this._curveLoaded;
}

public initialize (root: Node) {
public initialize (root: Node, blendStateBuffer?: BlendStateBuffer, mask?: SkeletonMask) {
if (this._curveLoaded) { return; }
this._curveLoaded = true;
if (this._poseOutput) {
Expand Down Expand Up @@ -352,7 +354,7 @@ export class AnimationState extends Playable {
}

if (!this._doNotCreateEval) {
const pose = legacyCC.director.getAnimationManager()?.blendState ?? null;
const pose = blendStateBuffer ?? legacyCC.director.getAnimationManager()?.blendState ?? null;
if (pose) {
this._poseOutput = new PoseOutput(pose);
}
Expand Down
1 change: 1 addition & 0 deletions cocos/core/animation/animation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ export { QuatTrack } from './tracks/quat-track';
export { ColorTrack } from './tracks/color-track';
export { SizeTrack } from './tracks/size-track';
export { ObjectTrack } from './tracks/object-track';
export * from './marionette';
110 changes: 110 additions & 0 deletions cocos/core/animation/kinematics/ccd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@

import { assertIsTrue } from '../../data/utils/asserts';
import { Quat } from '../../math/quat';
import { clamp } from '../../math/utils';
import { Vec3 } from '../../math/vec3';
import { Node } from '../../scene-graph/node';

const THETA_ERROR = 0.001;
const DUMP_BIAS = 1.0;

enum IterationResult {
UNFINISHED,
DONE,
INTERRUPTED,
}

/**
* The Cyclic Coordinate Descent algorithm.
* @param links The links(limbs).
* @param target Target position.
* @param maxIterations Max iterations.
* @param forward True if use forward iteration(base to leaf), otherwise use backward iteration(leaf to base).
*/
export function ccdIK(
links: Node[],
target: Vec3,
epsilon: number,
maxIterations: number,
forward: boolean,
) {
const nLinks = links.length;
if (nLinks < 2) {
return;
}

const u = new Vec3(); // Vector from end factor to current link
const v = new Vec3(); // Vector from target to current link
const axis = new Vec3(); // Intermediate var
const correctiveRot = new Quat();
const currentPos = new Vec3();
const currentRot = new Quat();
const endFactorPos = new Vec3();

const iEndFactor = links.length - 1;
const endFactor = links[iEndFactor];
if (forward) {
for (let iteration = 0; iteration < maxIterations; ++iteration) {
// Won't run in infinite loop since we have `nLinks >= 2`
for (let iLink = 0; iLink < iEndFactor; ++iLink) {
const result = correct(iLink);
if (result === IterationResult.INTERRUPTED) {
break;
} else if (result === IterationResult.DONE) {
return;
}
}
}
} else {
for (let iteration = 0; iteration < maxIterations; ++iteration) {
// Won't run in infinite loop since we have `nLinks >= 2`
for (let iLink = iEndFactor - 1; iLink >= 0; --iLink) {
const result = correct(iLink);
if (result === IterationResult.INTERRUPTED) {
break;
} else if (result === IterationResult.DONE) {
return;
}
}
}
}

function correct (linkIndex: number): IterationResult {
const current = links[linkIndex];

current.getWorldPosition(currentPos);
endFactor.getWorldPosition(endFactorPos);

Vec3.subtract(u, endFactorPos, currentPos);
Vec3.normalize(u, u);
Vec3.subtract(v, target, currentPos);
Vec3.normalize(v, v);

// TODO: what if axis is zero?
Vec3.cross(axis, u, v);
Vec3.normalize(axis, axis);

const cosTheta = Vec3.dot(u, v);
const theta = Math.acos(cosTheta) * DUMP_BIAS;

// Refresh hierarchy
Quat.fromAxisAngle(correctiveRot, axis, theta);
current.getWorldRotation(currentRot);
Quat.multiply(currentRot, correctiveRot, currentRot);
current.setWorldRotation(currentRot);
endFactor.getWorldPosition(endFactorPos);

// Try
const distance = Vec3.distance(endFactorPos, target);
if (distance < epsilon) {
return IterationResult.DONE;
}

// If the link’s corrective rotations exceeds the tolerance-redo other links.
if (theta > THETA_ERROR) {
return IterationResult.INTERRUPTED;
}

return IterationResult.UNFINISHED;
}
}
168 changes: 168 additions & 0 deletions cocos/core/animation/marionette/__tmp__/get-demo-graphs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { GraphDescription } from './graph-description';
import { AnimationGraph } from '../animation-graph';
import { createGraphFromDescription } from './graph-from-description';

export function __getDemoGraphs () {
return Object.entries(graphDescMap).reduce((result, [name, graphDesc]) => {
result[name] = createGraphFromDescription(graphDesc);
return result;
}, {} as Record<string, AnimationGraph>);
}

const graphDescMap: Record<string, GraphDescription> = {
'any-transition': {
layers: [{
graph: {
type: 'state-machine',
nodes: [{
name: 'Node1',
type: 'animation',
}],
anyTransitions: [{
to: 0,
}],
},
}],
},

vars: {
vars: [
{ name: 'foo', value: 1.0 },
{ name: 'bar', value: false },
],
layers: [{
graph: {
type: 'state-machine',
nodes: [{
name: 'Node1',
type: 'animation',
}, {
name: 'Node2',
type: 'animation',
}],
anyTransitions: [{
to: 0,
}],
transitions: [{
from: 0,
to: 1,
conditions: [{
type: 'binary',
lhs: 'foo',
operator: 'EQUAL',
rhs: 2.0,
}],
}],
},
}],
},

'pose-blend-requires-numbers': {
vars: [{
name: 'v',
value: false,
}],
layers: [{
graph: {
type: 'state-machine',
nodes: [{
name: 'Node1',
type: 'animation',
motion: {
type: 'blend',
children: [{ type: 'clip' }, { type: 'clip' }],
blender: {
type: '1d',
thresholds: [0.0, 1.0],
value: {
name: 'v',
value: 0,
},
},
},
}],
},
}],
},

'successive-satisfaction': {
layers: [{
graph: {
type: 'state-machine',
nodes: [{
name: 'Node1',
type: 'animation',
}, {
name: 'Node2',
type: 'animation',
}],
entryTransitions: [{
to: 0,
}],
transitions: [{
from: 0,
to: 1,
}],
},
}],
},

'unspecified-condition': {
layers: [{
graph: {
type: 'state-machine',
nodes: [{
name: 'asd',
type: 'animation',
}],
entryTransitions: [{
to: 0,
}],
},
}],
},

'variable-not-found-in-condition': {
layers: [{
graph: {
type: 'state-machine',
nodes: [{
type: 'animation',
name: 'Node1',
}],
entryTransitions: [{
to: 0,
conditions: [{
type: 'unary',
operator: 'TRUTHY',
operand: { name: 'asd', value: 0.0 },
}],
}],
},
}],
},

'variable-not-found-in-pose-blend': {
layers: [{
graph: {
type: 'state-machine',
nodes: [{
name: 'Node1',
type: 'animation',
motion: {
type: 'blend',
children: [{ type: 'clip' }, { type: 'clip' }],
blender: {
type: '1d',
thresholds: [0.0, 1.0],
value: {
name: 'asd',
value: 0,
},
},
},
}],
},
}],
},
};
Loading

0 comments on commit 8488972

Please sign in to comment.