Files
cocos-engine/cocos/core/animation/compression/compressed-data.ts
Leslie Leigh (李的序) 2ecf99ad53 Optimize animation document (#10860)
* Optimize animation document

* Update

* Update

* Update
2022-04-26 15:44:38 +08:00

321 lines
10 KiB
TypeScript

import { QuatCurve, RealCurve } from '../../curves';
import { KeySharedQuatCurves, KeySharedRealCurves } from '../../curves/keys-shared-curves';
import { ccclass, serializable } from '../../data/decorators';
import { Quat, Vec2, Vec3, Vec4 } from '../../math';
import { CLASS_NAME_PREFIX_ANIM } from '../define';
import { QuatTrack } from '../tracks/quat-track';
import { RealTrack } from '../tracks/real-track';
import { Binder, RuntimeBinding, TrackBinding, trackBindingTag, TrackPath } from '../tracks/track';
import { VectorTrack } from '../tracks/vector-track';
@ccclass(`${CLASS_NAME_PREFIX_ANIM}CompressedData`)
export class CompressedData {
public compressRealTrack (track: RealTrack) {
const curve = track.channel.curve;
const mayBeCompressed = KeySharedRealCurves.allowedForCurve(curve);
if (!mayBeCompressed) {
return false;
}
this._tracks.push({
type: CompressedDataTrackType.FLOAT,
binding: track[trackBindingTag],
components: [this._addRealCurve(curve)],
});
return true;
}
public compressVectorTrack (vectorTrack: VectorTrack) {
const nComponents = vectorTrack.componentsCount;
const channels = vectorTrack.channels();
const mayBeCompressed = channels.every(({ curve }) => KeySharedRealCurves.allowedForCurve(curve));
if (!mayBeCompressed) {
return false;
}
const components = new Array<CompressedCurvePointer>(nComponents);
for (let i = 0; i < nComponents; ++i) {
const channel = channels[i];
components[i] = this._addRealCurve(channel.curve);
}
this._tracks.push({
type:
nComponents === 2
? CompressedDataTrackType.VEC2
: nComponents === 3
? CompressedDataTrackType.VEC3
: CompressedDataTrackType.VEC4,
binding: vectorTrack[trackBindingTag],
components,
});
return true;
}
public compressQuatTrack (track: QuatTrack) {
const curve = track.channel.curve;
const mayBeCompressed = KeySharedQuatCurves.allowedForCurve(curve);
if (!mayBeCompressed) {
return false;
}
this._quatTracks.push({
binding: track[trackBindingTag],
pointer: this._addQuatCurve(curve),
});
return true;
}
public createEval (binder: Binder) {
const compressedDataEvalStatus: CompressedDataEvalStatus = {
keySharedCurvesEvalStatuses: [],
trackEvalStatuses: [],
keysSharedQuatCurvesEvalStatues: [],
quatTrackEvalStatuses: [],
};
const {
keySharedCurvesEvalStatuses,
trackEvalStatuses,
keysSharedQuatCurvesEvalStatues,
quatTrackEvalStatuses,
} = compressedDataEvalStatus;
for (const curves of this._curves) {
keySharedCurvesEvalStatuses.push({
curves,
result: new Array(curves.curveCount).fill(0.0),
});
}
for (const track of this._tracks) {
const trackTarget = binder(track.binding);
if (!trackTarget) {
continue;
}
let immediate: CompressedTrackImmediate | undefined;
switch (track.type) {
default:
case CompressedDataTrackType.FLOAT:
break;
case CompressedDataTrackType.VEC2:
immediate = new Vec2();
break;
case CompressedDataTrackType.VEC3:
immediate = new Vec3();
break;
case CompressedDataTrackType.VEC4:
immediate = new Vec4();
break;
}
trackEvalStatuses.push({
type: track.type,
target: trackTarget,
curves: track.components,
immediate,
});
}
for (const curves of this._quatCurves) {
keysSharedQuatCurvesEvalStatues.push({
curves,
result: Array.from({ length: curves.curveCount }, () => new Quat()),
});
}
for (const track of this._quatTracks) {
const trackTarget = binder(track.binding);
if (!trackTarget) {
continue;
}
quatTrackEvalStatuses.push({
target: trackTarget,
curve: track.pointer,
});
}
return new CompressedDataEvaluator(compressedDataEvalStatus);
}
public collectAnimatedJoints () {
const joints: string[] = [];
for (const track of this._tracks) {
const trsPath = track.binding.parseTrsPath();
if (trsPath) {
joints.push(trsPath.node);
}
}
return joints;
}
@serializable
private _curves: KeySharedRealCurves[] = [];
@serializable
private _tracks: CompressedTrack[] = [];
@serializable
private _quatCurves: KeySharedQuatCurves[] = [];
@serializable
private _quatTracks: CompressedQuatTrack[] = [];
private _addRealCurve (curve: RealCurve): CompressedCurvePointer {
const times = Array.from(curve.times());
let iKeySharedCurves = this._curves.findIndex((shared) => shared.matchCurve(curve));
if (iKeySharedCurves < 0) {
iKeySharedCurves = this._curves.length;
const keySharedCurves = new KeySharedRealCurves(times);
this._curves.push(keySharedCurves);
}
const iCurve = this._curves[iKeySharedCurves].curveCount;
this._curves[iKeySharedCurves].addCurve(curve);
return {
shared: iKeySharedCurves,
component: iCurve,
};
}
/**
* @internal
*/
public _addQuatCurve (curve: QuatCurve): CompressedQuatCurvePointer {
const times = Array.from(curve.times());
let iKeySharedCurves = this._quatCurves.findIndex((shared) => shared.matchCurve(curve));
if (iKeySharedCurves < 0) {
iKeySharedCurves = this._quatCurves.length;
const keySharedCurves = new KeySharedQuatCurves(times);
this._quatCurves.push(keySharedCurves);
}
const iCurve = this._quatCurves[iKeySharedCurves].curveCount;
this._quatCurves[iKeySharedCurves].addCurve(curve);
return {
shared: iKeySharedCurves,
curve: iCurve,
};
}
public validate () {
return this._tracks.length > 0;
}
}
export class CompressedDataEvaluator {
constructor (compressedDataEvalStatus: CompressedDataEvalStatus) {
this._compressedDataEvalStatus = compressedDataEvalStatus;
}
public evaluate (time: number) {
const {
keySharedCurvesEvalStatuses,
trackEvalStatuses: compressedTrackEvalStatuses,
keysSharedQuatCurvesEvalStatues,
quatTrackEvalStatuses,
} = this._compressedDataEvalStatus;
const getPreEvaluated = (pointer: CompressedCurvePointer) => keySharedCurvesEvalStatuses[pointer.shared].result[pointer.component];
for (const { curves, result } of keySharedCurvesEvalStatuses) {
curves.evaluate(time, result);
}
for (const { type, target, immediate, curves } of compressedTrackEvalStatuses) {
let value: unknown = immediate;
switch (type) {
default:
break;
case CompressedDataTrackType.FLOAT:
value = getPreEvaluated(curves[0]);
break;
case CompressedDataTrackType.VEC2:
Vec2.set(
value as Vec2,
getPreEvaluated(curves[0]),
getPreEvaluated(curves[1]),
);
break;
case CompressedDataTrackType.VEC3:
Vec3.set(
value as Vec3,
getPreEvaluated(curves[0]),
getPreEvaluated(curves[1]),
getPreEvaluated(curves[2]),
);
break;
case CompressedDataTrackType.VEC4:
Vec4.set(
value as Vec4,
getPreEvaluated(curves[0]),
getPreEvaluated(curves[1]),
getPreEvaluated(curves[2]),
getPreEvaluated(curves[4]),
);
break;
}
target.setValue(value);
}
for (const { curves, result } of keysSharedQuatCurvesEvalStatues) {
curves.evaluate(time, result);
}
for (const { target, curve } of quatTrackEvalStatuses) {
target.setValue(keysSharedQuatCurvesEvalStatues[curve.shared].result[curve.curve]);
}
}
private _compressedDataEvalStatus: CompressedDataEvalStatus;
}
interface CompressedTrack {
binding: TrackBinding;
type: CompressedDataTrackType;
components: CompressedCurvePointer[];
}
interface CompressedQuatTrack {
binding: TrackBinding;
pointer: CompressedQuatCurvePointer;
}
enum CompressedDataTrackType {
FLOAT,
VEC2,
VEC3,
VEC4,
}
type CompressedTrackImmediate = Vec2 | Vec3 | Vec4;
interface CompressedDataEvalStatus {
keySharedCurvesEvalStatuses: Array<{
curves: KeySharedRealCurves;
result: number[];
}>;
trackEvalStatuses: Array<{
type: CompressedDataTrackType;
target: RuntimeBinding;
immediate: CompressedTrackImmediate | undefined;
curves: CompressedCurvePointer[];
}>;
keysSharedQuatCurvesEvalStatues: Array<{
curves: KeySharedQuatCurves;
result: Quat[];
}>;
quatTrackEvalStatuses: Array<{
target: RuntimeBinding;
curve: CompressedQuatCurvePointer;
}>;
}
interface CompressedCurvePointer {
shared: number;
component: number;
}
interface CompressedQuatCurvePointer {
shared: number;
curve: number;
}