dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
1,091 lines (924 loc) • 97.9 kB
text/typescript
/**
* The MIT License (MIT)
*
* Copyright (c) 2012-2017 DragonBones team and other contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace dragonBones {
/**
* @internal
* @private
*/
export class ObjectDataParser extends DataParser {
protected static _getBoolean(rawData: any, key: string, defaultValue: boolean): boolean {
if (key in rawData) {
const value = rawData[key];
const type = typeof value;
if (type === "boolean") {
return value;
}
else if (type === "string") {
switch (value) {
case "0":
case "NaN":
case "":
case "false":
case "null":
case "undefined":
return false;
default:
return true;
}
}
else {
return !!value;
}
}
return defaultValue;
}
protected static _getNumber(rawData: any, key: string, defaultValue: number): number {
if (key in rawData) {
const value = rawData[key];
if (value === null || value === "NaN") {
return defaultValue;
}
return +value || 0;
}
return defaultValue;
}
protected static _getString(rawData: any, key: string, defaultValue: string): string {
if (key in rawData) {
const value = rawData[key];
const type = typeof value;
if (type === "string") {
if (DragonBones.webAssembly) {
for (let i = 0, l = (value as string).length; i < l; ++i) {
if ((value as string).charCodeAt(i) > 255) {
return encodeURI(value);
}
}
}
return value;
}
return String(value);
}
return defaultValue;
}
protected _rawTextureAtlasIndex: number = 0;
protected readonly _rawBones: Array<BoneData> = [];
protected _data: DragonBonesData = null as any; //
protected _armature: ArmatureData = null as any; //
protected _bone: BoneData = null as any; //
protected _surface: SurfaceData = null as any; //
protected _slot: SlotData = null as any; //
protected _skin: SkinData = null as any; //
protected _mesh: MeshDisplayData = null as any; //
protected _animation: AnimationData = null as any; //
protected _timeline: TimelineData = null as any; //
protected _rawTextureAtlases: Array<any> | null = null;
private _defaultColorOffset: number = -1;
private _prevClockwise: number = 0;
private _prevRotation: number = 0.0;
private readonly _helpMatrixA: Matrix = new Matrix();
private readonly _helpMatrixB: Matrix = new Matrix();
private readonly _helpTransform: Transform = new Transform();
private readonly _helpColorTransform: ColorTransform = new ColorTransform();
private readonly _helpPoint: Point = new Point();
private readonly _helpArray: Array<number> = [];
private readonly _intArray: Array<number> = [];
private readonly _floatArray: Array<number> = [];
private readonly _frameIntArray: Array<number> = [];
private readonly _frameFloatArray: Array<number> = [];
private readonly _frameArray: Array<number> = [];
private readonly _timelineArray: Array<number> = [];
private readonly _cacheRawMeshes: Array<any> = [];
private readonly _cacheMeshes: Array<MeshDisplayData> = [];
private readonly _actionFrames: Array<ActionFrame> = [];
private readonly _weightSlotPose: Map<Array<number>> = {};
private readonly _weightBonePoses: Map<Array<number>> = {};
private readonly _cacheBones: Map<Array<BoneData>> = {};
private readonly _slotChildActions: Map<Array<ActionData>> = {};
private _getCurvePoint(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, t: number, result: Point): void {
const l_t = 1.0 - t;
const powA = l_t * l_t;
const powB = t * t;
const kA = l_t * powA;
const kB = 3.0 * t * powA;
const kC = 3.0 * l_t * powB;
const kD = t * powB;
result.x = kA * x1 + kB * x2 + kC * x3 + kD * x4;
result.y = kA * y1 + kB * y2 + kC * y3 + kD * y4;
}
private _samplingEasingCurve(curve: Array<number>, samples: Array<number>): void {
const curveCount = curve.length;
let stepIndex = -2;
for (let i = 0, l = samples.length; i < l; ++i) {
let t = (i + 1) / (l + 1); // float
while ((stepIndex + 6 < curveCount ? curve[stepIndex + 6] : 1) < t) { // stepIndex + 3 * 2
stepIndex += 6;
}
const isInCurve = stepIndex >= 0 && stepIndex + 6 < curveCount;
const x1 = isInCurve ? curve[stepIndex] : 0.0;
const y1 = isInCurve ? curve[stepIndex + 1] : 0.0;
const x2 = curve[stepIndex + 2];
const y2 = curve[stepIndex + 3];
const x3 = curve[stepIndex + 4];
const y3 = curve[stepIndex + 5];
const x4 = isInCurve ? curve[stepIndex + 6] : 1.0;
const y4 = isInCurve ? curve[stepIndex + 7] : 1.0;
let lower = 0.0;
let higher = 1.0;
while (higher - lower > 0.0001) {
const percentage = (higher + lower) * 0.5;
this._getCurvePoint(x1, y1, x2, y2, x3, y3, x4, y4, percentage, this._helpPoint);
if (t - this._helpPoint.x > 0.0) {
lower = percentage;
}
else {
higher = percentage;
}
}
samples[i] = this._helpPoint.y;
}
}
private _parseActionDataInFrame(rawData: any, frameStart: number, bone: BoneData | null, slot: SlotData | null): void {
if (DataParser.EVENT in rawData) {
this._mergeActionFrame(rawData[DataParser.EVENT], frameStart, ActionType.Frame, bone, slot);
}
if (DataParser.SOUND in rawData) {
this._mergeActionFrame(rawData[DataParser.SOUND], frameStart, ActionType.Sound, bone, slot);
}
if (DataParser.ACTION in rawData) {
this._mergeActionFrame(rawData[DataParser.ACTION], frameStart, ActionType.Play, bone, slot);
}
if (DataParser.EVENTS in rawData) {
this._mergeActionFrame(rawData[DataParser.EVENTS], frameStart, ActionType.Frame, bone, slot);
}
if (DataParser.ACTIONS in rawData) {
this._mergeActionFrame(rawData[DataParser.ACTIONS], frameStart, ActionType.Play, bone, slot);
}
}
private _mergeActionFrame(rawData: any, frameStart: number, type: ActionType, bone: BoneData | null, slot: SlotData | null): void {
const actionOffset = DragonBones.webAssembly ? (this._armature.actions as any).size() : this._armature.actions.length;
const actions = this._parseActionData(rawData, type, bone, slot);
let frameIndex = 0;
let frame: ActionFrame | null = null;
for (const action of actions) {
this._armature.addAction(action, false);
}
if (this._actionFrames.length === 0) { // First frame.
frame = new ActionFrame();
frame.frameStart = 0;
this._actionFrames.push(frame);
frame = null;
}
for (const eachFrame of this._actionFrames) { // Get same frame.
if (eachFrame.frameStart === frameStart) {
frame = eachFrame;
break;
}
else if (eachFrame.frameStart > frameStart) {
break;
}
frameIndex++;
}
if (frame === null) { // Create and cache frame.
frame = new ActionFrame();
frame.frameStart = frameStart;
this._actionFrames.splice(frameIndex + 1, 0, frame);
}
for (let i = 0; i < actions.length; ++i) { // Cache action offsets.
frame.actions.push(actionOffset + i);
}
}
protected _parseArmature(rawData: any, scale: number): ArmatureData {
const armature = BaseObject.borrowObject(ArmatureData);
armature.name = ObjectDataParser._getString(rawData, DataParser.NAME, "");
armature.frameRate = ObjectDataParser._getNumber(rawData, DataParser.FRAME_RATE, this._data.frameRate);
armature.scale = scale;
if (DataParser.TYPE in rawData && typeof rawData[DataParser.TYPE] === "string") {
armature.type = DataParser._getArmatureType(rawData[DataParser.TYPE]);
}
else {
armature.type = ObjectDataParser._getNumber(rawData, DataParser.TYPE, ArmatureType.Armature);
}
if (armature.frameRate === 0) { // Data error.
armature.frameRate = 24;
}
this._armature = armature;
if (DataParser.CANVAS in rawData) {
const rawCanvas = rawData[DataParser.CANVAS];
const canvas = BaseObject.borrowObject(CanvasData);
if (DataParser.COLOR in rawCanvas) {
canvas.hasBackground = true;
}
else {
canvas.hasBackground = false;
}
canvas.color = ObjectDataParser._getNumber(rawCanvas, DataParser.COLOR, 0);
canvas.x = ObjectDataParser._getNumber(rawCanvas, DataParser.X, 0) * armature.scale;
canvas.y = ObjectDataParser._getNumber(rawCanvas, DataParser.Y, 0) * armature.scale;
canvas.width = ObjectDataParser._getNumber(rawCanvas, DataParser.WIDTH, 0) * armature.scale;
canvas.height = ObjectDataParser._getNumber(rawCanvas, DataParser.HEIGHT, 0) * armature.scale;
armature.canvas = canvas;
}
if (DataParser.AABB in rawData) {
const rawAABB = rawData[DataParser.AABB];
armature.aabb.x = ObjectDataParser._getNumber(rawAABB, DataParser.X, 0.0) * armature.scale;
armature.aabb.y = ObjectDataParser._getNumber(rawAABB, DataParser.Y, 0.0) * armature.scale;
armature.aabb.width = ObjectDataParser._getNumber(rawAABB, DataParser.WIDTH, 0.0) * armature.scale;
armature.aabb.height = ObjectDataParser._getNumber(rawAABB, DataParser.HEIGHT, 0.0) * armature.scale;
}
if (DataParser.BONE in rawData) {
const rawBones = rawData[DataParser.BONE] as Array<any>;
for (const rawBone of rawBones) {
const parentName = ObjectDataParser._getString(rawBone, DataParser.PARENT, "");
const bone = this._parseBone(rawBone);
if (parentName.length > 0) { // Get bone parent.
const parent = armature.getBone(parentName);
if (parent !== null) {
bone.parent = parent;
}
else { // Cache.
if (!(parentName in this._cacheBones)) {
this._cacheBones[parentName] = [];
}
this._cacheBones[parentName].push(bone);
}
}
if (bone.name in this._cacheBones) {
for (const child of this._cacheBones[bone.name]) {
child.parent = bone;
}
delete this._cacheBones[bone.name];
}
armature.addBone(bone);
this._rawBones.push(bone); // Cache raw bones sort.
}
}
if (DataParser.IK in rawData) {
const rawIKS = rawData[DataParser.IK] as Array<any>;
for (const rawIK of rawIKS) {
const constraint = this._parseIKConstraint(rawIK);
if (constraint) {
armature.addConstraint(constraint);
}
}
}
armature.sortBones();
if (DataParser.SLOT in rawData) {
let zOrder = 0;
const rawSlots = rawData[DataParser.SLOT] as Array<any>;
for (const rawSlot of rawSlots) {
armature.addSlot(this._parseSlot(rawSlot, zOrder++));
}
}
if (DataParser.SKIN in rawData) {
const rawSkins = rawData[DataParser.SKIN] as Array<any>;
for (const rawSkin of rawSkins) {
armature.addSkin(this._parseSkin(rawSkin));
}
}
for (let i = 0, l = this._cacheRawMeshes.length; i < l; ++i) { // Link glue mesh.
const rawData = this._cacheRawMeshes[i];
if (!(DataParser.GLUE_WEIGHTS in rawData) || !(DataParser.GLUE_MESHES in rawData)) {
continue;
}
this._parseMeshGlue(rawData, this._cacheMeshes[i]);
}
for (let i = 0, l = this._cacheRawMeshes.length; i < l; ++i) { // Link mesh.
const rawData = this._cacheRawMeshes[i];
const shareName = ObjectDataParser._getString(rawData, DataParser.SHARE, "");
if (shareName.length === 0) {
continue;
}
let skinName = ObjectDataParser._getString(rawData, DataParser.SKIN, DataParser.DEFAULT_NAME);
if (skinName.length === 0) { //
skinName = DataParser.DEFAULT_NAME;
}
const shareMesh = armature.getMesh(skinName, "", shareName) as MeshDisplayData | null; // TODO slot;
if (shareMesh === null) {
continue; // Error.
}
const mesh = this._cacheMeshes[i];
mesh.offset = shareMesh.offset;
mesh.weight = shareMesh.weight;
mesh.glue = shareMesh.glue;
}
if (DataParser.ANIMATION in rawData) {
const rawAnimations = rawData[DataParser.ANIMATION] as Array<any>;
for (const rawAnimation of rawAnimations) {
const animation = this._parseAnimation(rawAnimation);
armature.addAnimation(animation);
}
}
if (DataParser.DEFAULT_ACTIONS in rawData) {
const actions = this._parseActionData(rawData[DataParser.DEFAULT_ACTIONS], ActionType.Play, null, null);
for (const action of actions) {
armature.addAction(action, true);
if (action.type === ActionType.Play) { // Set default animation from default action.
const animation = armature.getAnimation(action.name);
if (animation !== null) {
armature.defaultAnimation = animation;
}
}
}
}
if (DataParser.ACTIONS in rawData) {
const actions = this._parseActionData(rawData[DataParser.ACTIONS], ActionType.Play, null, null);
for (const action of actions) {
armature.addAction(action, false);
}
}
// Clear helper.
this._rawBones.length = 0;
this._cacheRawMeshes.length = 0;
this._cacheMeshes.length = 0;
this._armature = null as any;
for (let k in this._weightSlotPose) {
delete this._weightSlotPose[k];
}
for (let k in this._weightBonePoses) {
delete this._weightBonePoses[k];
}
for (let k in this._cacheBones) {
delete this._cacheBones[k];
}
for (let k in this._slotChildActions) {
delete this._slotChildActions[k];
}
return armature;
}
protected _parseBone(rawData: any): BoneData {
let type: BoneType = BoneType.Bone;
const scale = this._armature.scale;
if (DataParser.TYPE in rawData && typeof rawData[DataParser.TYPE] === "string") {
type = DataParser._getBoneType(rawData[DataParser.TYPE]);
}
else {
type = ObjectDataParser._getNumber(rawData, DataParser.TYPE, BoneType.Bone);
}
if (type === BoneType.Bone) {
const bone = BaseObject.borrowObject(BoneData);
bone.inheritTranslation = ObjectDataParser._getBoolean(rawData, DataParser.INHERIT_TRANSLATION, true);
bone.inheritRotation = ObjectDataParser._getBoolean(rawData, DataParser.INHERIT_ROTATION, true);
bone.inheritScale = ObjectDataParser._getBoolean(rawData, DataParser.INHERIT_SCALE, true);
bone.inheritReflection = ObjectDataParser._getBoolean(rawData, DataParser.INHERIT_REFLECTION, true);
bone.length = ObjectDataParser._getNumber(rawData, DataParser.LENGTH, 0) * scale;
bone.name = ObjectDataParser._getString(rawData, DataParser.NAME, "");
if (DataParser.TRANSFORM in rawData) {
this._parseTransform(rawData[DataParser.TRANSFORM], bone.transform, scale);
}
return bone;
}
const surface = BaseObject.borrowObject(SurfaceData);
surface.name = ObjectDataParser._getString(rawData, DataParser.NAME, "");
surface.segmentX = ObjectDataParser._getNumber(rawData, DataParser.SEGMENT_X, 0);
surface.segmentY = ObjectDataParser._getNumber(rawData, DataParser.SEGMENT_Y, 0);
surface.vertices.length = (surface.segmentX + 1) * (surface.segmentY + 1) * 2;
if (DataParser.VERTICES in rawData) {
const rawVertices = rawData[DataParser.VERTICES] as Array<number>;
for (let i = 0, l = surface.vertices.length; i < l; ++i) {
if (i < rawVertices.length) {
surface.vertices[i] = rawVertices[i] * scale;
}
else {
surface.vertices[i] = 0.0;
}
}
}
return surface;
}
protected _parseIKConstraint(rawData: any): ConstraintData | null {
const bone = this._armature.getBone(ObjectDataParser._getString(rawData, DataParser.BONE, ""));
if (bone === null) {
return null;
}
const target = this._armature.getBone(ObjectDataParser._getString(rawData, DataParser.TARGET, ""));
if (target === null) {
return null;
}
const constraint = BaseObject.borrowObject(IKConstraintData);
constraint.scaleEnabled = ObjectDataParser._getBoolean(rawData, DataParser.SCALE, false);
constraint.bendPositive = ObjectDataParser._getBoolean(rawData, DataParser.BEND_POSITIVE, true);
constraint.weight = ObjectDataParser._getNumber(rawData, DataParser.WEIGHT, 1.0);
constraint.name = ObjectDataParser._getString(rawData, DataParser.NAME, "");
constraint.target = target;
const chain = ObjectDataParser._getNumber(rawData, DataParser.CHAIN, 0);
if (chain > 0 && bone.parent !== null) {
constraint.root = bone.parent;
constraint.bone = bone;
}
else {
constraint.root = bone;
constraint.bone = null;
}
return constraint;
}
protected _parseSlot(rawData: any, zOrder: number): SlotData {
const slot = BaseObject.borrowObject(SlotData);
slot.displayIndex = ObjectDataParser._getNumber(rawData, DataParser.DISPLAY_INDEX, 0);
slot.zOrder = zOrder;
slot.name = ObjectDataParser._getString(rawData, DataParser.NAME, "");
slot.parent = this._armature.getBone(ObjectDataParser._getString(rawData, DataParser.PARENT, "")) as any; //
if (DataParser.BLEND_MODE in rawData && typeof rawData[DataParser.BLEND_MODE] === "string") {
slot.blendMode = DataParser._getBlendMode(rawData[DataParser.BLEND_MODE]);
}
else {
slot.blendMode = ObjectDataParser._getNumber(rawData, DataParser.BLEND_MODE, BlendMode.Normal);
}
if (DataParser.COLOR in rawData) {
slot.color = SlotData.createColor();
this._parseColorTransform(rawData[DataParser.COLOR], slot.color);
}
else {
slot.color = SlotData.DEFAULT_COLOR;
}
if (DataParser.ACTIONS in rawData) {
this._slotChildActions[slot.name] = this._parseActionData(rawData[DataParser.ACTIONS], ActionType.Play, null, null);
}
return slot;
}
protected _parseSkin(rawData: any): SkinData {
const skin = BaseObject.borrowObject(SkinData);
skin.name = ObjectDataParser._getString(rawData, DataParser.NAME, DataParser.DEFAULT_NAME);
if (skin.name.length === 0) {
skin.name = DataParser.DEFAULT_NAME;
}
if (DataParser.SLOT in rawData) {
const rawSlots = rawData[DataParser.SLOT];
this._skin = skin;
for (const rawSlot of rawSlots) {
const slotName = ObjectDataParser._getString(rawSlot, DataParser.NAME, "");
const slot = this._armature.getSlot(slotName);
if (slot !== null) {
this._slot = slot;
if (DataParser.DISPLAY in rawSlot) {
const rawDisplays = rawSlot[DataParser.DISPLAY];
for (const rawDisplay of rawDisplays) {
if (rawDisplay) {
skin.addDisplay(slotName, this._parseDisplay(rawDisplay));
}
else {
skin.addDisplay(slotName, null);
}
}
}
this._slot = null as any; //
}
}
this._skin = null as any; //
}
return skin;
}
protected _parseDisplay(rawData: any): DisplayData | null {
const name = ObjectDataParser._getString(rawData, DataParser.NAME, "");
const path = ObjectDataParser._getString(rawData, DataParser.PATH, "");
let type = DisplayType.Image;
let display: DisplayData | null = null;
if (DataParser.TYPE in rawData && typeof rawData[DataParser.TYPE] === "string") {
type = DataParser._getDisplayType(rawData[DataParser.TYPE]);
}
else {
type = ObjectDataParser._getNumber(rawData, DataParser.TYPE, type);
}
switch (type) {
case DisplayType.Image:
const imageDisplay = display = BaseObject.borrowObject(ImageDisplayData);
imageDisplay.name = name;
imageDisplay.path = path.length > 0 ? path : name;
this._parsePivot(rawData, imageDisplay);
break;
case DisplayType.Armature:
const armatureDisplay = display = BaseObject.borrowObject(ArmatureDisplayData);
armatureDisplay.name = name;
armatureDisplay.path = path.length > 0 ? path : name;
armatureDisplay.inheritAnimation = true;
if (DataParser.ACTIONS in rawData) {
const actions = this._parseActionData(rawData[DataParser.ACTIONS], ActionType.Play, null, null);
for (const action of actions) {
armatureDisplay.addAction(action);
}
}
else if (this._slot.name in this._slotChildActions) {
const displays = this._skin.getDisplays(this._slot.name);
if (displays === null ? this._slot.displayIndex === 0 : this._slot.displayIndex === displays.length) {
for (const action of this._slotChildActions[this._slot.name]) {
armatureDisplay.addAction(action);
}
delete this._slotChildActions[this._slot.name];
}
}
break;
case DisplayType.Mesh:
const meshDisplay = display = BaseObject.borrowObject(MeshDisplayData);
meshDisplay.inheritDeform = ObjectDataParser._getBoolean(rawData, DataParser.INHERIT_DEFORM, true);
meshDisplay.name = name;
meshDisplay.path = path.length > 0 ? path : name;
if (DataParser.SHARE in rawData) {
this._cacheRawMeshes.push(rawData);
this._cacheMeshes.push(meshDisplay);
}
else {
this._parseMesh(rawData, meshDisplay);
}
if ((DataParser.GLUE_WEIGHTS in rawData) && (DataParser.GLUE_MESHES in rawData)) {
this._cacheRawMeshes.push(rawData);
this._cacheMeshes.push(meshDisplay);
}
break;
case DisplayType.BoundingBox:
const boundingBox = this._parseBoundingBox(rawData);
if (boundingBox !== null) {
const boundingBoxDisplay = display = BaseObject.borrowObject(BoundingBoxDisplayData);
boundingBoxDisplay.name = name;
boundingBoxDisplay.path = path.length > 0 ? path : name;
boundingBoxDisplay.boundingBox = boundingBox;
}
break;
}
if (display !== null && DataParser.TRANSFORM in rawData) {
this._parseTransform(rawData[DataParser.TRANSFORM], display.transform, this._armature.scale);
}
return display;
}
protected _parsePivot(rawData: any, display: ImageDisplayData): void {
if (DataParser.PIVOT in rawData) {
const rawPivot = rawData[DataParser.PIVOT];
display.pivot.x = ObjectDataParser._getNumber(rawPivot, DataParser.X, 0.0);
display.pivot.y = ObjectDataParser._getNumber(rawPivot, DataParser.Y, 0.0);
}
else {
display.pivot.x = 0.5;
display.pivot.y = 0.5;
}
}
protected _parseMesh(rawData: any, mesh: MeshDisplayData): void {
const rawVertices = rawData[DataParser.VERTICES] as Array<number>;
const rawUVs = rawData[DataParser.UVS] as Array<number>;
const rawTriangles = rawData[DataParser.TRIANGLES] as Array<number>;
const vertexCount = Math.floor(rawVertices.length / 2); // uint
const triangleCount = Math.floor(rawTriangles.length / 3); // uint
const vertexOffset = this._floatArray.length;
const uvOffset = vertexOffset + vertexCount * 2;
const meshOffset = this._intArray.length;
const meshName = this._skin.name + "_" + this._slot.name + "_" + mesh.name; // Cache pose data.
mesh.offset = meshOffset;
this._intArray.length += 1 + 1 + 1 + 1 + triangleCount * 3;
this._intArray[meshOffset + BinaryOffset.MeshVertexCount] = vertexCount;
this._intArray[meshOffset + BinaryOffset.MeshTriangleCount] = triangleCount;
this._intArray[meshOffset + BinaryOffset.MeshFloatOffset] = vertexOffset;
for (let i = 0, l = triangleCount * 3; i < l; ++i) {
this._intArray[meshOffset + BinaryOffset.MeshVertexIndices + i] = rawTriangles[i];
}
this._floatArray.length += vertexCount * 2 + vertexCount * 2;
for (let i = 0, l = vertexCount * 2; i < l; ++i) {
this._floatArray[vertexOffset + i] = rawVertices[i];
this._floatArray[uvOffset + i] = rawUVs[i];
}
if (DataParser.WEIGHTS in rawData) {
const rawWeights = rawData[DataParser.WEIGHTS] as Array<number>;
const rawSlotPose = rawData[DataParser.SLOT_POSE] as Array<number>;
const rawBonePoses = rawData[DataParser.BONE_POSE] as Array<number>;
const sortedBones = this._armature.sortedBones;
const weightBoneIndices = new Array<number>();
const weightBoneCount = Math.floor(rawBonePoses.length / 7); // uint
const floatOffset = this._floatArray.length;
const weightCount = Math.floor(rawWeights.length - vertexCount) / 2; // uint
const weightOffset = this._intArray.length;
const weight = BaseObject.borrowObject(WeightData);
weight.count = weightCount;
weight.offset = weightOffset;
weightBoneIndices.length = weightBoneCount;
this._intArray.length += 1 + 1 + weightBoneCount + vertexCount + weightCount;
this._intArray[weightOffset + BinaryOffset.WeigthFloatOffset] = floatOffset;
for (let i = 0; i < weightBoneCount; ++i) {
const rawBoneIndex = rawBonePoses[i * 7]; // uint
const bone = this._rawBones[rawBoneIndex];
weight.addBone(bone);
weightBoneIndices[i] = rawBoneIndex;
this._intArray[weightOffset + BinaryOffset.WeigthBoneIndices + i] = sortedBones.indexOf(bone);
}
this._floatArray.length += weightCount * 3;
this._helpMatrixA.copyFromArray(rawSlotPose, 0);
for (
let i = 0, iW = 0, iB = weightOffset + BinaryOffset.WeigthBoneIndices + weightBoneCount, iV = floatOffset;
i < vertexCount;
++i
) {
const iD = i * 2;
const vertexBoneCount = this._intArray[iB++] = rawWeights[iW++]; // uint
let x = this._floatArray[vertexOffset + iD];
let y = this._floatArray[vertexOffset + iD + 1];
this._helpMatrixA.transformPoint(x, y, this._helpPoint);
x = this._helpPoint.x;
y = this._helpPoint.y;
for (let j = 0; j < vertexBoneCount; ++j) {
const rawBoneIndex = rawWeights[iW++]; // uint
const boneIndex = weightBoneIndices.indexOf(rawBoneIndex);
this._helpMatrixB.copyFromArray(rawBonePoses, boneIndex * 7 + 1);
this._helpMatrixB.invert();
this._helpMatrixB.transformPoint(x, y, this._helpPoint);
this._intArray[iB++] = boneIndex;
this._floatArray[iV++] = rawWeights[iW++];
this._floatArray[iV++] = this._helpPoint.x;
this._floatArray[iV++] = this._helpPoint.y;
}
}
mesh.weight = weight;
this._weightSlotPose[meshName] = rawSlotPose;
this._weightBonePoses[meshName] = rawBonePoses;
}
}
protected _parseMeshGlue(rawData: any, mesh: MeshDisplayData): void {
const rawWeights = rawData[DataParser.GLUE_WEIGHTS] as Array<number>;
const rawMeshes = rawData[DataParser.GLUE_MESHES] as Array<string>;
mesh.glue = BaseObject.borrowObject(GlueData);
mesh.glue.weights.length = rawWeights.length;
for (let i = 0, l = rawWeights.length; i < l; ++i) {
mesh.glue.weights[i] = rawWeights[i];
}
for (let i = 0, l = rawMeshes.length; i < l; i += 3) {
const glueMesh = this._armature.getMesh(rawMeshes[i], rawMeshes[i + 1], rawMeshes[i + 2]);
mesh.glue.addMesh(glueMesh);
}
}
protected _parseBoundingBox(rawData: any): BoundingBoxData | null {
let boundingBox: BoundingBoxData | null = null;
let type = BoundingBoxType.Rectangle;
if (DataParser.SUB_TYPE in rawData && typeof rawData[DataParser.SUB_TYPE] === "string") {
type = DataParser._getBoundingBoxType(rawData[DataParser.SUB_TYPE]);
}
else {
type = ObjectDataParser._getNumber(rawData, DataParser.SUB_TYPE, type);
}
switch (type) {
case BoundingBoxType.Rectangle:
boundingBox = BaseObject.borrowObject(RectangleBoundingBoxData);
break;
case BoundingBoxType.Ellipse:
boundingBox = BaseObject.borrowObject(EllipseBoundingBoxData);
break;
case BoundingBoxType.Polygon:
boundingBox = this._parsePolygonBoundingBox(rawData);
break;
}
if (boundingBox !== null) {
boundingBox.color = ObjectDataParser._getNumber(rawData, DataParser.COLOR, 0x000000);
if (boundingBox.type === BoundingBoxType.Rectangle || boundingBox.type === BoundingBoxType.Ellipse) {
boundingBox.width = ObjectDataParser._getNumber(rawData, DataParser.WIDTH, 0.0);
boundingBox.height = ObjectDataParser._getNumber(rawData, DataParser.HEIGHT, 0.0);
}
}
return boundingBox;
}
protected _parsePolygonBoundingBox(rawData: any): PolygonBoundingBoxData {
const polygonBoundingBox = BaseObject.borrowObject(PolygonBoundingBoxData);
if (DataParser.VERTICES in rawData) {
const scale = this._armature.scale;
const rawVertices = rawData[DataParser.VERTICES] as Array<number>;
const vertices = polygonBoundingBox.vertices;
if (DragonBones.webAssembly) {
(vertices as any).resize(rawVertices.length, 0.0);
}
else {
vertices.length = rawVertices.length;
}
for (let i = 0, l = rawVertices.length; i < l; i += 2) {
let x = rawVertices[i] * scale;
let y = rawVertices[i + 1] * scale;
if (DragonBones.webAssembly) {
(vertices as any).set(i, x);
(vertices as any).set(i + 1, y);
}
else {
vertices[i] = x;
vertices[i + 1] = y;
}
// AABB.
if (i === 0) {
polygonBoundingBox.x = x;
polygonBoundingBox.y = y;
polygonBoundingBox.width = x;
polygonBoundingBox.height = y;
}
else {
if (x < polygonBoundingBox.x) {
polygonBoundingBox.x = x;
}
else if (x > polygonBoundingBox.width) {
polygonBoundingBox.width = x;
}
if (y < polygonBoundingBox.y) {
polygonBoundingBox.y = y;
}
else if (y > polygonBoundingBox.height) {
polygonBoundingBox.height = y;
}
}
}
polygonBoundingBox.width -= polygonBoundingBox.x;
polygonBoundingBox.height -= polygonBoundingBox.y;
}
else {
console.warn("Data error.\n Please reexport DragonBones Data to fixed the bug.");
}
return polygonBoundingBox;
}
protected _parseAnimation(rawData: any): AnimationData {
const animation = BaseObject.borrowObject(AnimationData);
animation.frameCount = Math.max(ObjectDataParser._getNumber(rawData, DataParser.DURATION, 1), 1);
animation.playTimes = ObjectDataParser._getNumber(rawData, DataParser.PLAY_TIMES, 1);
animation.duration = animation.frameCount / this._armature.frameRate; // float
animation.fadeInTime = ObjectDataParser._getNumber(rawData, DataParser.FADE_IN_TIME, 0.0);
animation.scale = ObjectDataParser._getNumber(rawData, DataParser.SCALE, 1.0);
animation.name = ObjectDataParser._getString(rawData, DataParser.NAME, DataParser.DEFAULT_NAME);
if (animation.name.length === 0) {
animation.name = DataParser.DEFAULT_NAME;
}
animation.frameIntOffset = this._frameIntArray.length;
animation.frameFloatOffset = this._frameFloatArray.length;
animation.frameOffset = this._frameArray.length;
this._animation = animation;
if (DataParser.FRAME in rawData) {
const rawFrames = rawData[DataParser.FRAME] as Array<any>;
const keyFrameCount = rawFrames.length;
if (keyFrameCount > 0) {
for (let i = 0, frameStart = 0; i < keyFrameCount; ++i) {
const rawFrame = rawFrames[i];
this._parseActionDataInFrame(rawFrame, frameStart, null, null);
frameStart += ObjectDataParser._getNumber(rawFrame, DataParser.DURATION, 1);
}
}
}
if (DataParser.Z_ORDER in rawData) {
this._animation.zOrderTimeline = this._parseTimeline(
rawData[DataParser.Z_ORDER], null, DataParser.FRAME, TimelineType.ZOrder,
false, false, 0,
this._parseZOrderFrame
);
}
if (DataParser.BONE in rawData) {
const rawTimelines = rawData[DataParser.BONE] as Array<any>;
for (const rawTimeline of rawTimelines) {
this._parseBoneTimeline(rawTimeline);
}
}
if (DataParser.SURFACE in rawData) {
const rawTimelines = rawData[DataParser.SURFACE] as Array<any>;
for (const rawTimeline of rawTimelines) {
const surfaceName = ObjectDataParser._getString(rawTimeline, DataParser.NAME, "");
this._surface = this._armature.getBone(surfaceName) as SurfaceData;
if (this._surface === null) {
continue;
}
const timeline = this._parseTimeline(
rawTimeline, null, DataParser.FRAME, TimelineType.Surface,
false, true, 0,
this._parseSurfaceFrame
);
if (timeline !== null) {
this._animation.addSurfaceTimeline(this._surface, timeline);
}
this._surface = null as any; //
}
}
if (DataParser.SLOT in rawData) {
const rawTimelines = rawData[DataParser.SLOT] as Array<any>;
for (const rawTimeline of rawTimelines) {
this._parseSlotTimeline(rawTimeline);
}
}
if (DataParser.FFD in rawData) {
const rawTimelines = rawData[DataParser.FFD] as Array<any>;
for (const rawTimeline of rawTimelines) {
let skinName = ObjectDataParser._getString(rawTimeline, DataParser.SKIN, DataParser.DEFAULT_NAME);
const slotName = ObjectDataParser._getString(rawTimeline, DataParser.SLOT, "");
const displayName = ObjectDataParser._getString(rawTimeline, DataParser.NAME, "");
if (skinName.length === 0) { //
skinName = DataParser.DEFAULT_NAME;
}
this._slot = this._armature.getSlot(slotName) as any;
this._mesh = this._armature.getMesh(skinName, slotName, displayName) as any;
if (this._slot === null || this._mesh === null) {
continue;
}
const timeline = this._parseTimeline(
rawTimeline, null, DataParser.FRAME, TimelineType.SlotFFD,
false, true, 0,
this._parseSlotFFDFrame
);
if (timeline !== null) {
this._animation.addSlotTimeline(this._slot, timeline);
}
this._slot = null as any; //
this._mesh = null as any; //
}
}
if (DataParser.IK in rawData) {
const rawTimelines = rawData[DataParser.IK] as Array<any>;
for (const rawTimeline of rawTimelines) {
const constraintName = ObjectDataParser._getString(rawTimeline, DataParser.NAME, "");
const constraint = this._armature.getConstraint(constraintName);
if (constraint === null) {
continue;
}
const timeline = this._parseTimeline(
rawTimeline, null, DataParser.FRAME, TimelineType.IKConstraint,
true, false, 2,
this._parseIKConstraintFrame
);
if (timeline !== null) {
this._animation.addConstraintTimeline(constraint, timeline);
}
}
}
if (DataParser.ANIMATION in rawData) {
const rawTimelines = rawData[DataParser.ANIMATION];
for (const rawTimeline of rawTimelines) {
const animationName = ObjectDataParser._getString(rawTimeline, DataParser.NAME, "");
const timeline = this._parseTimeline(
rawTimeline, null, DataParser.FRAME, TimelineType.AnimationTime,
true, false, 2,
this._parseAnimationFrame
);
if (timeline !== null) {
this._animation.addAnimationTimeline(animationName, timeline);
}
}
}
if (this._actionFrames.length > 0) {
this._animation.actionTimeline = this._parseTimeline(
null, this._actionFrames, "", TimelineType.Action,
false, false, 0,
this._parseActionFrame
);
this._actionFrames.length = 0;
}
this._animation = null as any; //
return animation;
}
protected _parseTimeline(
rawData: any, rawFrames: Array<any> | null, framesKey: string, type: TimelineType,
addIntOffset: boolean, addFloatOffset: boolean, frameValueCount: number,
frameParser: (rawData: any, frameStart: number, frameCount: number) => number
): TimelineData | null {
if (rawData !== null && framesKey.length > 0 && framesKey in rawData) {
rawFrames = rawData[framesKey];
}
if (rawFrames === null) {
return null;
}
const keyFrameCount = rawFrames.length;
if (keyFrameCount === 0) {
return null;
}
const frameIntArrayLength = this._frameIntArray.length;
const frameFloatArrayLength = this._frameFloatArray.length;
const timeline = BaseObject.borrowObject(TimelineData);
const timelineOffset = this._timelineArray.length;
this._timelineArray.length += 1 + 1 + 1 + 1 + 1 + keyFrameCount;
if (rawData !== null) {
this._timelineArray[timelineOffset + BinaryOffset.TimelineScale] = Math.round(ObjectDataParser._getNumber(rawData, DataParser.SCALE, 1.0) * 100);
this._timelineArray[timelineOffset + BinaryOffset.TimelineOffset] = Math.round(ObjectDataParser._getNumber(rawData, DataParser.OFFSET, 0.0) * 100);
}
else {
this._timelineArray[timelineOffset + BinaryOffset.TimelineScale] = 100;
this._timelineArray[timelineOffset + BinaryOffset.TimelineOffset] = 0;
}
this._timelineArray[timelineOffset + BinaryOffset.TimelineKeyFrameCount] = keyFrameCount;
this._timelineArray[timelineOffset + BinaryOffset.TimelineFrameValueCount] = frameValueCount;
if (addIntOffset) {
this._timelineArray[timelineOffset + BinaryOffset.TimelineFrameValueOffset] = frameIntArrayLength - this._animation.frameIntOffset;
}
else if (addFloatOffset) {
this._timelineArray[timelineOffset + BinaryOffset.TimelineFrameValueOffset] = frameFloatArrayLength - this._animation.frameFloatOffset;
}
else {
this._timelineArray[timelineOffset + BinaryOffset.TimelineFrameValueOffset] = 0;
}
this._timeline = timeline;
timeline.type = type;
timeline.offset = timelineOffset;
if (keyFrameCount === 1) { // Only one frame.
timeline.frameIndicesOffset = -1;
this._timelineArray[timelineOffset + BinaryOffset.TimelineFrameOffset + 0] = frameParser.call(this, rawFrames[0], 0, 0) - this._animation.frameOffset;
}
else {
const totalFrameCount = this._animation.frameCount + 1; // O