mirror of
https://github.com/cocos/cocos-engine.git
synced 2026-09-26 10:39:17 +08:00
321 lines
10 KiB
TypeScript
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;
|
|
}
|