/** * 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 = []; 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 | 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 = []; private readonly _intArray: Array = []; private readonly _floatArray: Array = []; private readonly _frameIntArray: Array = []; private readonly _frameFloatArray: Array = []; private readonly _frameArray: Array = []; private readonly _timelineArray: Array = []; private readonly _cacheRawMeshes: Array = []; private readonly _cacheMeshes: Array = []; private readonly _actionFrames: Array = []; private readonly _weightSlotPose: Map> = {}; private readonly _weightBonePoses: Map> = {}; private readonly _cacheBones: Map> = {}; private readonly _slotChildActions: Map> = {}; 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, samples: Array): 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; 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; 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; for (const rawSlot of rawSlots) { armature.addSlot(this._parseSlot(rawSlot, zOrder++)); } } if (DataParser.SKIN in rawData) { const rawSkins = rawData[DataParser.SKIN] as Array; 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; 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; 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; const rawUVs = rawData[DataParser.UVS] as Array; const rawTriangles = rawData[DataParser.TRIANGLES] as Array; 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; const rawSlotPose = rawData[DataParser.SLOT_POSE] as Array; const rawBonePoses = rawData[DataParser.BONE_POSE] as Array; const sortedBones = this._armature.sortedBones; const weightBoneIndices = new Array(); 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; const rawMeshes = rawData[DataParser.GLUE_MESHES] as Array; 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; 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; 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; for (const rawTimeline of rawTimelines) { this._parseBoneTimeline(rawTimeline); } } if (DataParser.SURFACE in rawData) { const rawTimelines = rawData[DataParser.SURFACE] as Array; 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; for (const rawTimeline of rawTimelines) { this._parseSlotTimeline(rawTimeline); } } if (DataParser.FFD in rawData) { const rawTimelines = rawData[DataParser.FFD] as Array; 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; 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 | 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; // One more frame than animation. const frameIndices = this._data.frameIndices; let frameIndicesOffset = 0; if (DragonBones.webAssembly) { frameIndicesOffset = (frameIndices as any).size(); (frameIndices as any).resize(frameIndicesOffset + totalFrameCount, 0); } else { frameIndicesOffset = frameIndices.length; frameIndices.length += totalFrameCount; } timeline.frameIndicesOffset = frameIndicesOffset; for ( let i = 0, iK = 0, frameStart = 0, frameCount = 0; i < totalFrameCount; ++i ) { if (frameStart + frameCount <= i && iK < keyFrameCount) { const rawFrame = rawFrames[iK]; frameStart = i; // frame.frameStart; if (iK === keyFrameCount - 1) { frameCount = this._animation.frameCount - frameStart; } else { if (rawFrame instanceof ActionFrame) { frameCount = this._actionFrames[iK + 1].frameStart - frameStart; } else { frameCount = ObjectDataParser._getNumber(rawFrame, DataParser.DURATION, 1); } } this._timelineArray[timelineOffset + BinaryOffset.TimelineFrameOffset + iK] = frameParser.call(this, rawFrame, frameStart, frameCount) - this._animation.frameOffset; iK++; } if (DragonBones.webAssembly) { (frameIndices as any).set(frameIndicesOffset + i, iK - 1); } else { frameIndices[frameIndicesOffset + i] = iK - 1; } } } this._timeline = null as any; // return timeline; } protected _parseBoneTimeline(rawData: any): void { const bone = this._armature.getBone(ObjectDataParser._getString(rawData, DataParser.NAME, "")); if (bone === null) { return; } this._bone = bone; this._slot = this._armature.getSlot(this._bone.name) as any; if (DataParser.TRANSLATE_FRAME in rawData) { const timeline = this._parseTimeline( rawData, null, DataParser.TRANSLATE_FRAME, TimelineType.BoneTranslate, false, true, 2, this._parseBoneTranslateFrame ); if (timeline !== null) { this._animation.addBoneTimeline(bone, timeline); } } if (DataParser.ROTATE_FRAME in rawData) { const timeline = this._parseTimeline( rawData, null, DataParser.ROTATE_FRAME, TimelineType.BoneRotate, false, true, 2, this._parseBoneRotateFrame ); if (timeline !== null) { this._animation.addBoneTimeline(bone, timeline); } } if (DataParser.SCALE_FRAME in rawData) { const timeline = this._parseTimeline( rawData, null, DataParser.SCALE_FRAME, TimelineType.BoneScale, false, true, 2, this._parseBoneScaleFrame ); if (timeline !== null) { this._animation.addBoneTimeline(bone, timeline); } } if (DataParser.FRAME in rawData) { const timeline = this._parseTimeline( rawData, null, DataParser.FRAME, TimelineType.BoneAll, false, true, 6, this._parseBoneAllFrame ); if (timeline !== null) { this._animation.addBoneTimeline(bone, timeline); } } this._bone = null as any; // this._slot = null as any; // } protected _parseSlotTimeline(rawData: any): void { const slot = this._armature.getSlot(ObjectDataParser._getString(rawData, DataParser.NAME, "")); if (slot === null) { return; } this._slot = slot; // Display timeline. let displayTimeline: TimelineData | null = null; if (DataParser.DISPLAY_FRAME in rawData) { displayTimeline = this._parseTimeline( rawData, null, DataParser.DISPLAY_FRAME, TimelineType.SlotDisplay, false, false, 0, this._parseSlotDisplayFrame ); } else { displayTimeline = this._parseTimeline( rawData, null, DataParser.FRAME, TimelineType.SlotDisplay, false, false, 0, this._parseSlotDisplayFrame ); } if (displayTimeline !== null) { this._animation.addSlotTimeline(slot, displayTimeline); } let colorTimeline: TimelineData | null = null; if (DataParser.COLOR_FRAME in rawData) { colorTimeline = this._parseTimeline( rawData, null, DataParser.COLOR_FRAME, TimelineType.SlotColor, true, false, 1, this._parseSlotColorFrame ); } else { colorTimeline = this._parseTimeline( rawData, null, DataParser.FRAME, TimelineType.SlotColor, true, false, 1, this._parseSlotColorFrame ); } if (colorTimeline !== null) { this._animation.addSlotTimeline(slot, colorTimeline); } this._slot = null as any; // } protected _parseFrame(rawData: any, frameStart: number, frameCount: number): number { // tslint:disable-next-line:no-unused-expression rawData; // tslint:disable-next-line:no-unused-expression frameCount; const frameOffset = this._frameArray.length; this._frameArray.length += 1; this._frameArray[frameOffset + BinaryOffset.FramePosition] = frameStart; return frameOffset; } protected _parseTweenFrame(rawData: any, frameStart: number, frameCount: number): number { const frameOffset = this._parseFrame(rawData, frameStart, frameCount); if (frameCount > 0) { if (DataParser.CURVE in rawData) { const sampleCount = frameCount + 1; this._helpArray.length = sampleCount; this._samplingEasingCurve(rawData[DataParser.CURVE], this._helpArray); this._frameArray.length += 1 + 1 + this._helpArray.length; this._frameArray[frameOffset + BinaryOffset.FrameTweenType] = TweenType.Curve; this._frameArray[frameOffset + BinaryOffset.FrameTweenEasingOrCurveSampleCount] = sampleCount; for (let i = 0; i < sampleCount; ++i) { this._frameArray[frameOffset + BinaryOffset.FrameCurveSamples + i] = Math.round(this._helpArray[i] * 10000.0); } } else { const noTween = -2.0; let tweenEasing = noTween; if (DataParser.TWEEN_EASING in rawData) { tweenEasing = ObjectDataParser._getNumber(rawData, DataParser.TWEEN_EASING, noTween); } if (tweenEasing === noTween) { this._frameArray.length += 1; this._frameArray[frameOffset + BinaryOffset.FrameTweenType] = TweenType.None; } else if (tweenEasing === 0.0) { this._frameArray.length += 1; this._frameArray[frameOffset + BinaryOffset.FrameTweenType] = TweenType.Line; } else if (tweenEasing < 0.0) { this._frameArray.length += 1 + 1; this._frameArray[frameOffset + BinaryOffset.FrameTweenType] = TweenType.QuadIn; this._frameArray[frameOffset + BinaryOffset.FrameTweenEasingOrCurveSampleCount] = Math.round(-tweenEasing * 100.0); } else if (tweenEasing <= 1.0) { this._frameArray.length += 1 + 1; this._frameArray[frameOffset + BinaryOffset.FrameTweenType] = TweenType.QuadOut; this._frameArray[frameOffset + BinaryOffset.FrameTweenEasingOrCurveSampleCount] = Math.round(tweenEasing * 100.0); } else { this._frameArray.length += 1 + 1; this._frameArray[frameOffset + BinaryOffset.FrameTweenType] = TweenType.QuadInOut; this._frameArray[frameOffset + BinaryOffset.FrameTweenEasingOrCurveSampleCount] = Math.round(tweenEasing * 100.0 - 100.0); } } } else { this._frameArray.length += 1; this._frameArray[frameOffset + BinaryOffset.FrameTweenType] = TweenType.None; } return frameOffset; } protected _parseActionFrame(frame: ActionFrame, frameStart: number, frameCount: number): number { // tslint:disable-next-line:no-unused-expression frameCount; const frameOffset = this._frameArray.length; const actionCount = frame.actions.length; this._frameArray.length += 1 + 1 + actionCount; this._frameArray[frameOffset + BinaryOffset.FramePosition] = frameStart; this._frameArray[frameOffset + BinaryOffset.FramePosition + 1] = actionCount; // Action count. for (let i = 0; i < actionCount; ++i) { // Action offsets. this._frameArray[frameOffset + BinaryOffset.FramePosition + 2 + i] = frame.actions[i]; } return frameOffset; } protected _parseZOrderFrame(rawData: any, frameStart: number, frameCount: number): number { const frameOffset = this._parseFrame(rawData, frameStart, frameCount); if (DataParser.Z_ORDER in rawData) { const rawZOrder = rawData[DataParser.Z_ORDER] as Array; if (rawZOrder.length > 0) { const slotCount = this._armature.sortedSlots.length; const unchanged = new Array(slotCount - rawZOrder.length / 2); const zOrders = new Array(slotCount); for (let i = 0; i < unchanged.length; ++i) { unchanged[i] = 0; } for (let i = 0; i < slotCount; ++i) { zOrders[i] = -1; } let originalIndex = 0; let unchangedIndex = 0; for (let i = 0, l = rawZOrder.length; i < l; i += 2) { const slotIndex = rawZOrder[i]; const zOrderOffset = rawZOrder[i + 1]; while (originalIndex !== slotIndex) { unchanged[unchangedIndex++] = originalIndex++; } const index = originalIndex + zOrderOffset; zOrders[index] = originalIndex++; } while (originalIndex < slotCount) { unchanged[unchangedIndex++] = originalIndex++; } this._frameArray.length += 1 + slotCount; this._frameArray[frameOffset + 1] = slotCount; let i = slotCount; while (i--) { if (zOrders[i] === -1) { this._frameArray[frameOffset + 2 + i] = unchanged[--unchangedIndex] || 0; } else { this._frameArray[frameOffset + 2 + i] = zOrders[i] || 0; } } return frameOffset; } } this._frameArray.length += 1; this._frameArray[frameOffset + 1] = 0; return frameOffset; } protected _parseBoneAllFrame(rawData: any, frameStart: number, frameCount: number): number { this._helpTransform.identity(); if (DataParser.TRANSFORM in rawData) { this._parseTransform(rawData[DataParser.TRANSFORM], this._helpTransform, 1.0); } // Modify rotation. let rotation = this._helpTransform.rotation; if (frameStart !== 0) { if (this._prevClockwise === 0) { rotation = this._prevRotation + Transform.normalizeRadian(rotation - this._prevRotation); } else { if (this._prevClockwise > 0 ? rotation >= this._prevRotation : rotation <= this._prevRotation) { this._prevClockwise = this._prevClockwise > 0 ? this._prevClockwise - 1 : this._prevClockwise + 1; } rotation = this._prevRotation + rotation - this._prevRotation + Transform.PI_D * this._prevClockwise; } } this._prevClockwise = ObjectDataParser._getNumber(rawData, DataParser.TWEEN_ROTATE, 0.0); this._prevRotation = rotation; // const frameOffset = this._parseTweenFrame(rawData, frameStart, frameCount); let frameFloatOffset = this._frameFloatArray.length; this._frameFloatArray.length += 6; this._frameFloatArray[frameFloatOffset++] = this._helpTransform.x; this._frameFloatArray[frameFloatOffset++] = this._helpTransform.y; this._frameFloatArray[frameFloatOffset++] = rotation; this._frameFloatArray[frameFloatOffset++] = this._helpTransform.skew; this._frameFloatArray[frameFloatOffset++] = this._helpTransform.scaleX; this._frameFloatArray[frameFloatOffset++] = this._helpTransform.scaleY; this._parseActionDataInFrame(rawData, frameStart, this._bone, this._slot); return frameOffset; } protected _parseBoneTranslateFrame(rawData: any, frameStart: number, frameCount: number): number { const frameOffset = this._parseTweenFrame(rawData, frameStart, frameCount); let frameFloatOffset = this._frameFloatArray.length; this._frameFloatArray.length += 2; this._frameFloatArray[frameFloatOffset++] = ObjectDataParser._getNumber(rawData, DataParser.X, 0.0); this._frameFloatArray[frameFloatOffset++] = ObjectDataParser._getNumber(rawData, DataParser.Y, 0.0); return frameOffset; } protected _parseBoneRotateFrame(rawData: any, frameStart: number, frameCount: number): number { // Modify rotation. let rotation = ObjectDataParser._getNumber(rawData, DataParser.ROTATE, 0.0) * Transform.DEG_RAD; if (frameStart !== 0) { if (this._prevClockwise === 0) { rotation = this._prevRotation + Transform.normalizeRadian(rotation - this._prevRotation); } else { if (this._prevClockwise > 0 ? rotation >= this._prevRotation : rotation <= this._prevRotation) { this._prevClockwise = this._prevClockwise > 0 ? this._prevClockwise - 1 : this._prevClockwise + 1; } rotation = this._prevRotation + rotation - this._prevRotation + Transform.PI_D * this._prevClockwise; } } this._prevClockwise = ObjectDataParser._getNumber(rawData, DataParser.CLOCK_WISE, 0); this._prevRotation = rotation; // const frameOffset = this._parseTweenFrame(rawData, frameStart, frameCount); let frameFloatOffset = this._frameFloatArray.length; this._frameFloatArray.length += 2; this._frameFloatArray[frameFloatOffset++] = rotation; this._frameFloatArray[frameFloatOffset++] = ObjectDataParser._getNumber(rawData, DataParser.SKEW, 0.0) * Transform.DEG_RAD; return frameOffset; } protected _parseBoneScaleFrame(rawData: any, frameStart: number, frameCount: number): number { const frameOffset = this._parseTweenFrame(rawData, frameStart, frameCount); let frameFloatOffset = this._frameFloatArray.length; this._frameFloatArray.length += 2; this._frameFloatArray[frameFloatOffset++] = ObjectDataParser._getNumber(rawData, DataParser.X, 1.0); this._frameFloatArray[frameFloatOffset++] = ObjectDataParser._getNumber(rawData, DataParser.Y, 1.0); return frameOffset; } protected _parseSurfaceFrame(rawData: any, frameStart: number, frameCount: number): number { const frameFloatOffset = this._frameFloatArray.length; const frameOffset = this._parseTweenFrame(rawData, frameStart, frameCount); const rawVertices = rawData[DataParser.VERTICES] as Array; const offset = ObjectDataParser._getNumber(rawData, DataParser.OFFSET, 0); // uint const vertexCount = this._surface.vertices.length / 2; // uint let x = 0.0; let y = 0.0; this._frameFloatArray.length += vertexCount * 2; for ( let i = 0; i < vertexCount * 2; i += 2 ) { if (i < offset || i - offset >= rawVertices.length) { x = 0.0; } else { x = rawVertices[i - offset]; } if (i + 1 < offset || i + 1 - offset >= rawVertices.length) { y = 0.0; } else { y = rawVertices[i + 1 - offset]; } this._frameFloatArray[frameFloatOffset + i] = x; this._frameFloatArray[frameFloatOffset + i + 1] = y; } if (frameStart === 0) { const frameIntOffset = this._frameIntArray.length; this._frameIntArray.length += 1 + 1 + 1 + 1 + 1; this._frameIntArray[frameIntOffset + BinaryOffset.DeformMeshOffset] = 0; // this._frameIntArray[frameIntOffset + BinaryOffset.DeformCount] = this._frameFloatArray.length - frameFloatOffset; this._frameIntArray[frameIntOffset + BinaryOffset.DeformValueCount] = this._frameFloatArray.length - frameFloatOffset; this._frameIntArray[frameIntOffset + BinaryOffset.DeformValueOffset] = 0; this._frameIntArray[frameIntOffset + BinaryOffset.DeformFloatOffset] = frameFloatOffset - this._animation.frameFloatOffset; this._timelineArray[this._timeline.offset + BinaryOffset.TimelineFrameValueCount] = frameIntOffset - this._animation.frameIntOffset; } return frameOffset; } protected _parseSlotDisplayFrame(rawData: any, frameStart: number, frameCount: number): number { const frameOffset = this._parseFrame(rawData, frameStart, frameCount); this._frameArray.length += 1; if (DataParser.VALUE in rawData) { this._frameArray[frameOffset + 1] = ObjectDataParser._getNumber(rawData, DataParser.VALUE, 0); } else { this._frameArray[frameOffset + 1] = ObjectDataParser._getNumber(rawData, DataParser.DISPLAY_INDEX, 0); } this._parseActionDataInFrame(rawData, frameStart, this._slot.parent, this._slot); return frameOffset; } protected _parseSlotColorFrame(rawData: any, frameStart: number, frameCount: number): number { const frameOffset = this._parseTweenFrame(rawData, frameStart, frameCount); let colorOffset = -1; if (DataParser.VALUE in rawData || DataParser.COLOR in rawData) { const rawColor = DataParser.VALUE in rawData ? rawData[DataParser.VALUE] : rawData[DataParser.COLOR]; for (let k in rawColor) { // Detects the presence of color. // tslint:disable-next-line:no-unused-expression k; this._parseColorTransform(rawColor, this._helpColorTransform); colorOffset = this._intArray.length; this._intArray.length += 8; this._intArray[colorOffset++] = Math.round(this._helpColorTransform.alphaMultiplier * 100); this._intArray[colorOffset++] = Math.round(this._helpColorTransform.redMultiplier * 100); this._intArray[colorOffset++] = Math.round(this._helpColorTransform.greenMultiplier * 100); this._intArray[colorOffset++] = Math.round(this._helpColorTransform.blueMultiplier * 100); this._intArray[colorOffset++] = Math.round(this._helpColorTransform.alphaOffset); this._intArray[colorOffset++] = Math.round(this._helpColorTransform.redOffset); this._intArray[colorOffset++] = Math.round(this._helpColorTransform.greenOffset); this._intArray[colorOffset++] = Math.round(this._helpColorTransform.blueOffset); colorOffset -= 8; break; } } if (colorOffset < 0) { if (this._defaultColorOffset < 0) { this._defaultColorOffset = colorOffset = this._intArray.length; this._intArray.length += 8; this._intArray[colorOffset++] = 100; this._intArray[colorOffset++] = 100; this._intArray[colorOffset++] = 100; this._intArray[colorOffset++] = 100; this._intArray[colorOffset++] = 0; this._intArray[colorOffset++] = 0; this._intArray[colorOffset++] = 0; this._intArray[colorOffset++] = 0; } colorOffset = this._defaultColorOffset; } const frameIntOffset = this._frameIntArray.length; this._frameIntArray.length += 1; this._frameIntArray[frameIntOffset] = colorOffset; return frameOffset; } protected _parseSlotFFDFrame(rawData: any, frameStart: number, frameCount: number): number { const frameFloatOffset = this._frameFloatArray.length; const frameOffset = this._parseTweenFrame(rawData, frameStart, frameCount); const rawVertices = DataParser.VERTICES in rawData ? rawData[DataParser.VERTICES] as Array : null; const offset = ObjectDataParser._getNumber(rawData, DataParser.OFFSET, 0); // uint const vertexCount = this._intArray[this._mesh.offset + BinaryOffset.MeshVertexCount]; const meshName = this._mesh.parent.name + "_" + this._slot.name + "_" + this._mesh.name; let x = 0.0; let y = 0.0; let iB = 0; let iV = 0; if (this._mesh.weight !== null) { const rawSlotPose = this._weightSlotPose[meshName]; this._helpMatrixA.copyFromArray(rawSlotPose, 0); this._frameFloatArray.length += this._mesh.weight.count * 2; iB = this._mesh.weight.offset + BinaryOffset.WeigthBoneIndices + this._mesh.weight.bones.length; } else { this._frameFloatArray.length += vertexCount * 2; } for ( let i = 0; i < vertexCount * 2; i += 2 ) { if (rawVertices === null) { // Fill 0. x = 0.0; y = 0.0; } else { if (i < offset || i - offset >= rawVertices.length) { x = 0.0; } else { x = rawVertices[i - offset]; } if (i + 1 < offset || i + 1 - offset >= rawVertices.length) { y = 0.0; } else { y = rawVertices[i + 1 - offset]; } } if (this._mesh.weight !== null) { // If mesh is skinned, transform point by bone bind pose. const rawBonePoses = this._weightBonePoses[meshName]; const vertexBoneCount = this._intArray[iB++]; this._helpMatrixA.transformPoint(x, y, this._helpPoint, true); x = this._helpPoint.x; y = this._helpPoint.y; for (let j = 0; j < vertexBoneCount; ++j) { const boneIndex = this._intArray[iB++]; this._helpMatrixB.copyFromArray(rawBonePoses, boneIndex * 7 + 1); this._helpMatrixB.invert(); this._helpMatrixB.transformPoint(x, y, this._helpPoint, true); this._frameFloatArray[frameFloatOffset + iV++] = this._helpPoint.x; this._frameFloatArray[frameFloatOffset + iV++] = this._helpPoint.y; } } else { this._frameFloatArray[frameFloatOffset + i] = x; this._frameFloatArray[frameFloatOffset + i + 1] = y; } } if (frameStart === 0) { const frameIntOffset = this._frameIntArray.length; this._frameIntArray.length += 1 + 1 + 1 + 1 + 1; this._frameIntArray[frameIntOffset + BinaryOffset.DeformMeshOffset] = this._mesh.offset; this._frameIntArray[frameIntOffset + BinaryOffset.DeformCount] = this._frameFloatArray.length - frameFloatOffset; this._frameIntArray[frameIntOffset + BinaryOffset.DeformValueCount] = this._frameFloatArray.length - frameFloatOffset; this._frameIntArray[frameIntOffset + BinaryOffset.DeformValueOffset] = 0; this._frameIntArray[frameIntOffset + BinaryOffset.DeformFloatOffset] = frameFloatOffset - this._animation.frameFloatOffset; this._timelineArray[this._timeline.offset + BinaryOffset.TimelineFrameValueCount] = frameIntOffset - this._animation.frameIntOffset; } return frameOffset; } protected _parseIKConstraintFrame(rawData: any, frameStart: number, frameCount: number): number { const frameOffset = this._parseTweenFrame(rawData, frameStart, frameCount); let frameIntOffset = this._frameIntArray.length; this._frameIntArray.length += 2; this._frameIntArray[frameIntOffset++] = ObjectDataParser._getBoolean(rawData, DataParser.BEND_POSITIVE, true) ? 1 : 0; this._frameIntArray[frameIntOffset++] = Math.round(ObjectDataParser._getNumber(rawData, DataParser.WEIGHT, 1.0) * 100.0); return frameOffset; } protected _parseAnimationFrame(rawData: any, frameStart: number, frameCount: number): number { const frameOffset = this._parseTweenFrame(rawData, frameStart, frameCount); let frameIntOffset = this._frameIntArray.length; this._frameIntArray.length += 2; this._frameIntArray[frameIntOffset++] = ObjectDataParser._getNumber(rawData, DataParser.VALUE, 0); this._frameIntArray[frameIntOffset++] = Math.round(ObjectDataParser._getNumber(rawData, DataParser.WEIGHT, 1.0) * 100.0); return frameOffset; } protected _parseActionData(rawData: any, type: ActionType, bone: BoneData | null, slot: SlotData | null): Array { const actions = new Array(); if (typeof rawData === "string") { const action = BaseObject.borrowObject(ActionData); action.type = type; action.name = rawData; action.bone = bone; action.slot = slot; actions.push(action); } else if (rawData instanceof Array) { for (const rawAction of rawData) { const action = BaseObject.borrowObject(ActionData); if (DataParser.GOTO_AND_PLAY in rawAction) { action.type = ActionType.Play; action.name = ObjectDataParser._getString(rawAction, DataParser.GOTO_AND_PLAY, ""); } else { if (DataParser.TYPE in rawAction && typeof rawAction[DataParser.TYPE] === "string") { action.type = DataParser._getActionType(rawAction[DataParser.TYPE]); } else { action.type = ObjectDataParser._getNumber(rawAction, DataParser.TYPE, type); } action.name = ObjectDataParser._getString(rawAction, DataParser.NAME, ""); } if (DataParser.BONE in rawAction) { const boneName = ObjectDataParser._getString(rawAction, DataParser.BONE, ""); action.bone = this._armature.getBone(boneName); } else { action.bone = bone; } if (DataParser.SLOT in rawAction) { const slotName = ObjectDataParser._getString(rawAction, DataParser.SLOT, ""); action.slot = this._armature.getSlot(slotName); } else { action.slot = slot; } let userData: UserData | null = null; if (DataParser.INTS in rawAction) { if (userData === null) { userData = BaseObject.borrowObject(UserData); } const rawInts = rawAction[DataParser.INTS] as Array; for (const rawValue of rawInts) { userData.addInt(rawValue); } } if (DataParser.FLOATS in rawAction) { if (userData === null) { userData = BaseObject.borrowObject(UserData); } const rawFloats = rawAction[DataParser.FLOATS] as Array; for (const rawValue of rawFloats) { userData.addFloat(rawValue); } } if (DataParser.STRINGS in rawAction) { if (userData === null) { userData = BaseObject.borrowObject(UserData); } const rawStrings = rawAction[DataParser.STRINGS] as Array; for (const rawValue of rawStrings) { userData.addString(rawValue); } } action.data = userData; actions.push(action); } } return actions; } protected _parseTransform(rawData: any, transform: Transform, scale: number): void { transform.x = ObjectDataParser._getNumber(rawData, DataParser.X, 0.0) * scale; transform.y = ObjectDataParser._getNumber(rawData, DataParser.Y, 0.0) * scale; if (DataParser.ROTATE in rawData || DataParser.SKEW in rawData) { transform.rotation = Transform.normalizeRadian(ObjectDataParser._getNumber(rawData, DataParser.ROTATE, 0.0) * Transform.DEG_RAD); transform.skew = Transform.normalizeRadian(ObjectDataParser._getNumber(rawData, DataParser.SKEW, 0.0) * Transform.DEG_RAD); } else if (DataParser.SKEW_X in rawData || DataParser.SKEW_Y in rawData) { transform.rotation = Transform.normalizeRadian(ObjectDataParser._getNumber(rawData, DataParser.SKEW_Y, 0.0) * Transform.DEG_RAD); transform.skew = Transform.normalizeRadian(ObjectDataParser._getNumber(rawData, DataParser.SKEW_X, 0.0) * Transform.DEG_RAD) - transform.rotation; } transform.scaleX = ObjectDataParser._getNumber(rawData, DataParser.SCALE_X, 1.0); transform.scaleY = ObjectDataParser._getNumber(rawData, DataParser.SCALE_Y, 1.0); } protected _parseColorTransform(rawData: any, color: ColorTransform): void { color.alphaMultiplier = ObjectDataParser._getNumber(rawData, DataParser.ALPHA_MULTIPLIER, 100) * 0.01; color.redMultiplier = ObjectDataParser._getNumber(rawData, DataParser.RED_MULTIPLIER, 100) * 0.01; color.greenMultiplier = ObjectDataParser._getNumber(rawData, DataParser.GREEN_MULTIPLIER, 100) * 0.01; color.blueMultiplier = ObjectDataParser._getNumber(rawData, DataParser.BLUE_MULTIPLIER, 100) * 0.01; color.alphaOffset = ObjectDataParser._getNumber(rawData, DataParser.ALPHA_OFFSET, 0); color.redOffset = ObjectDataParser._getNumber(rawData, DataParser.RED_OFFSET, 0); color.greenOffset = ObjectDataParser._getNumber(rawData, DataParser.GREEN_OFFSET, 0); color.blueOffset = ObjectDataParser._getNumber(rawData, DataParser.BLUE_OFFSET, 0); } protected _parseArray(rawData: any): void { // tslint:disable-next-line:no-unused-expression rawData; this._intArray.length = 0; this._floatArray.length = 0; this._frameIntArray.length = 0; this._frameFloatArray.length = 0; this._frameArray.length = 0; this._timelineArray.length = 0; } protected _modifyArray(): void { // Align. if ((this._intArray.length % Int16Array.BYTES_PER_ELEMENT) !== 0) { this._intArray.push(0); } if ((this._frameIntArray.length % Int16Array.BYTES_PER_ELEMENT) !== 0) { this._frameIntArray.push(0); } if ((this._frameArray.length % Int16Array.BYTES_PER_ELEMENT) !== 0) { this._frameArray.push(0); } if ((this._timelineArray.length % Uint16Array.BYTES_PER_ELEMENT) !== 0) { this._timelineArray.push(0); } const l1 = this._intArray.length * Int16Array.BYTES_PER_ELEMENT; const l2 = this._floatArray.length * Float32Array.BYTES_PER_ELEMENT; const l3 = this._frameIntArray.length * Int16Array.BYTES_PER_ELEMENT; const l4 = this._frameFloatArray.length * Float32Array.BYTES_PER_ELEMENT; const l5 = this._frameArray.length * Int16Array.BYTES_PER_ELEMENT; const l6 = this._timelineArray.length * Uint16Array.BYTES_PER_ELEMENT; const lTotal = l1 + l2 + l3 + l4 + l5 + l6; if (DragonBones.webAssembly) { const shareBuffer = webAssemblyModule.HEAP16.buffer; const bufferPointer = webAssemblyModule._malloc(lTotal); const intArray = new Int16Array(shareBuffer, bufferPointer, this._intArray.length); const floatArray = new Float32Array(shareBuffer, bufferPointer + l1, this._floatArray.length); const frameIntArray = new Int16Array(shareBuffer, bufferPointer + l1 + l2, this._frameIntArray.length); const frameFloatArray = new Float32Array(shareBuffer, bufferPointer + l1 + l2 + l3, this._frameFloatArray.length); const frameArray = new Int16Array(shareBuffer, bufferPointer + l1 + l2 + l3 + l4, this._frameArray.length); const timelineArray = new Uint16Array(shareBuffer, bufferPointer + l1 + l2 + l3 + l4 + l5, this._timelineArray.length); for (let i = 0, l = this._intArray.length; i < l; ++i) { intArray[i] = this._intArray[i]; } for (let i = 0, l = this._floatArray.length; i < l; ++i) { floatArray[i] = this._floatArray[i]; } for (let i = 0, l = this._frameIntArray.length; i < l; ++i) { frameIntArray[i] = this._frameIntArray[i]; } for (let i = 0, l = this._frameFloatArray.length; i < l; ++i) { frameFloatArray[i] = this._frameFloatArray[i]; } for (let i = 0, l = this._frameArray.length; i < l; ++i) { frameArray[i] = this._frameArray[i]; } for (let i = 0, l = this._timelineArray.length; i < l; ++i) { timelineArray[i] = this._timelineArray[i]; } webAssemblyModule.setDataBinary(this._data, bufferPointer, l1, l2, l3, l4, l5, l6); } else { const binary = new ArrayBuffer(lTotal); const intArray = new Int16Array(binary, 0, this._intArray.length); const floatArray = new Float32Array(binary, l1, this._floatArray.length); const frameIntArray = new Int16Array(binary, l1 + l2, this._frameIntArray.length); const frameFloatArray = new Float32Array(binary, l1 + l2 + l3, this._frameFloatArray.length); const frameArray = new Int16Array(binary, l1 + l2 + l3 + l4, this._frameArray.length); const timelineArray = new Uint16Array(binary, l1 + l2 + l3 + l4 + l5, this._timelineArray.length); for (let i = 0, l = this._intArray.length; i < l; ++i) { intArray[i] = this._intArray[i]; } for (let i = 0, l = this._floatArray.length; i < l; ++i) { floatArray[i] = this._floatArray[i]; } for (let i = 0, l = this._frameIntArray.length; i < l; ++i) { frameIntArray[i] = this._frameIntArray[i]; } for (let i = 0, l = this._frameFloatArray.length; i < l; ++i) { frameFloatArray[i] = this._frameFloatArray[i]; } for (let i = 0, l = this._frameArray.length; i < l; ++i) { frameArray[i] = this._frameArray[i]; } for (let i = 0, l = this._timelineArray.length; i < l; ++i) { timelineArray[i] = this._timelineArray[i]; } this._data.binary = binary; this._data.intArray = intArray; this._data.floatArray = floatArray; this._data.frameIntArray = frameIntArray; this._data.frameFloatArray = frameFloatArray; this._data.frameArray = frameArray; this._data.timelineArray = timelineArray; } this._defaultColorOffset = -1; } public parseDragonBonesData(rawData: any, scale: number = 1): DragonBonesData | null { console.assert(rawData !== null && rawData !== undefined, "Data error."); const version = ObjectDataParser._getString(rawData, DataParser.VERSION, ""); const compatibleVersion = ObjectDataParser._getString(rawData, DataParser.COMPATIBLE_VERSION, ""); if ( DataParser.DATA_VERSIONS.indexOf(version) >= 0 || DataParser.DATA_VERSIONS.indexOf(compatibleVersion) >= 0 ) { const data = BaseObject.borrowObject(DragonBonesData); data.version = version; data.name = ObjectDataParser._getString(rawData, DataParser.NAME, ""); data.frameRate = ObjectDataParser._getNumber(rawData, DataParser.FRAME_RATE, 24); if (data.frameRate === 0) { // Data error. data.frameRate = 24; } if (DataParser.ARMATURE in rawData) { this._data = data; this._parseArray(rawData); const rawArmatures = rawData[DataParser.ARMATURE] as Array; for (const rawArmature of rawArmatures) { data.addArmature(this._parseArmature(rawArmature, scale)); } if (!this._data.binary) { // DragonBones.webAssembly ? 0 : null; this._modifyArray(); } if (DataParser.STAGE in rawData) { data.stage = data.getArmature(ObjectDataParser._getString(rawData, DataParser.STAGE, "")); } else if (data.armatureNames.length > 0) { data.stage = data.getArmature(data.armatureNames[0]); } this._data = null as any; } if (DataParser.TEXTURE_ATLAS in rawData) { this._rawTextureAtlases = rawData[DataParser.TEXTURE_ATLAS]; } return data; } else { console.assert( false, "Nonsupport data version: " + version + "\n" + "Please convert DragonBones data to support version.\n" + "Read more: https://github.com/DragonBones/Tools/" ); } return null; } public parseTextureAtlasData(rawData: any, textureAtlasData: TextureAtlasData, scale: number = 1.0): boolean { console.assert(rawData !== undefined); if (rawData === null) { if (this._rawTextureAtlases === null || this._rawTextureAtlases.length === 0) { return false; } const rawTextureAtlas = this._rawTextureAtlases[this._rawTextureAtlasIndex++]; this.parseTextureAtlasData(rawTextureAtlas, textureAtlasData, scale); if (this._rawTextureAtlasIndex >= this._rawTextureAtlases.length) { this._rawTextureAtlasIndex = 0; this._rawTextureAtlases = null; } return true; } // Texture format. textureAtlasData.width = ObjectDataParser._getNumber(rawData, DataParser.WIDTH, 0); textureAtlasData.height = ObjectDataParser._getNumber(rawData, DataParser.HEIGHT, 0); textureAtlasData.scale = scale === 1.0 ? (1.0 / ObjectDataParser._getNumber(rawData, DataParser.SCALE, 1.0)) : scale; textureAtlasData.name = ObjectDataParser._getString(rawData, DataParser.NAME, ""); textureAtlasData.imagePath = ObjectDataParser._getString(rawData, DataParser.IMAGE_PATH, ""); if (DataParser.SUB_TEXTURE in rawData) { const rawTextures = rawData[DataParser.SUB_TEXTURE] as Array; for (let i = 0, l = rawTextures.length; i < l; ++i) { const rawTexture = rawTextures[i]; const textureData = textureAtlasData.createTexture(); textureData.rotated = ObjectDataParser._getBoolean(rawTexture, DataParser.ROTATED, false); textureData.name = ObjectDataParser._getString(rawTexture, DataParser.NAME, ""); textureData.region.x = ObjectDataParser._getNumber(rawTexture, DataParser.X, 0.0); textureData.region.y = ObjectDataParser._getNumber(rawTexture, DataParser.Y, 0.0); textureData.region.width = ObjectDataParser._getNumber(rawTexture, DataParser.WIDTH, 0.0); textureData.region.height = ObjectDataParser._getNumber(rawTexture, DataParser.HEIGHT, 0.0); const frameWidth = ObjectDataParser._getNumber(rawTexture, DataParser.FRAME_WIDTH, -1.0); const frameHeight = ObjectDataParser._getNumber(rawTexture, DataParser.FRAME_HEIGHT, -1.0); if (frameWidth > 0.0 && frameHeight > 0.0) { textureData.frame = TextureData.createRectangle(); textureData.frame.x = ObjectDataParser._getNumber(rawTexture, DataParser.FRAME_X, 0.0); textureData.frame.y = ObjectDataParser._getNumber(rawTexture, DataParser.FRAME_Y, 0.0); textureData.frame.width = frameWidth; textureData.frame.height = frameHeight; } textureAtlasData.addTexture(textureData); } } return true; } private static _objectDataParserInstance: ObjectDataParser = null as any; /** * - Deprecated, please refer to {@link dragonBones.BaseFactory#parseDragonBonesData()}. * @deprecated * @language en_US */ /** * - 已废弃,请参考 {@link dragonBones.BaseFactory#parseDragonBonesData()}。 * @deprecated * @language zh_CN */ public static getInstance(): ObjectDataParser { if (ObjectDataParser._objectDataParserInstance === null) { ObjectDataParser._objectDataParserInstance = new ObjectDataParser(); } return ObjectDataParser._objectDataParserInstance; } } /** * @internal * @private */ export class ActionFrame { public frameStart: number = 0; public readonly actions: Array = []; } }