private void ReadAnimation(Dictionary <String, Object> map, String name, SkeletonData skeletonData) { var scale = this.Scale; var timelines = new ExposedList <Timeline>(); float duration = 0; // Slot timelines. if (map.ContainsKey("slots")) { foreach (KeyValuePair <String, Object> entry in (Dictionary <String, Object>)map["slots"]) { String slotName = entry.Key; int slotIndex = skeletonData.FindSlotIndex(slotName); var timelineMap = (Dictionary <String, Object>)entry.Value; foreach (KeyValuePair <String, Object> timelineEntry in timelineMap) { var values = (List <Object>)timelineEntry.Value; var timelineName = (String)timelineEntry.Key; if (timelineName == "color") { var timeline = new ColorTimeline(values.Count); timeline.slotIndex = slotIndex; int frameIndex = 0; foreach (Dictionary <String, Object> valueMap in values) { float time = (float)valueMap["time"]; String c = (String)valueMap["color"]; timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3)); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]); } else if (timelineName == "attachment") { var timeline = new AttachmentTimeline(values.Count); timeline.slotIndex = slotIndex; int frameIndex = 0; foreach (Dictionary <String, Object> valueMap in values) { float time = (float)valueMap["time"]; timeline.SetFrame(frameIndex++, time, (String)valueMap["name"]); } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); } else { throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); } } } } // Bone timelines. if (map.ContainsKey("bones")) { foreach (KeyValuePair <String, Object> entry in (Dictionary <String, Object>)map["bones"]) { String boneName = entry.Key; int boneIndex = skeletonData.FindBoneIndex(boneName); if (boneIndex == -1) { throw new Exception("Bone not found: " + boneName); } var timelineMap = (Dictionary <String, Object>)entry.Value; foreach (KeyValuePair <String, Object> timelineEntry in timelineMap) { var values = (List <Object>)timelineEntry.Value; var timelineName = (String)timelineEntry.Key; if (timelineName == "rotate") { var timeline = new RotateTimeline(values.Count); timeline.boneIndex = boneIndex; int frameIndex = 0; foreach (Dictionary <String, Object> valueMap in values) { timeline.SetFrame(frameIndex, (float)valueMap["time"], (float)valueMap["angle"]); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * RotateTimeline.ENTRIES]); } else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") { TranslateTimeline timeline; float timelineScale = 1; if (timelineName == "scale") { timeline = new ScaleTimeline(values.Count); } else if (timelineName == "shear") { timeline = new ShearTimeline(values.Count); } else { timeline = new TranslateTimeline(values.Count); timelineScale = scale; } timeline.boneIndex = boneIndex; int frameIndex = 0; foreach (Dictionary <String, Object> valueMap in values) { float time = (float)valueMap["time"]; float x = GetFloat(valueMap, "x", 0); float y = GetFloat(valueMap, "y", 0); timeline.SetFrame(frameIndex, time, x * timelineScale, y * timelineScale); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TranslateTimeline.ENTRIES]); } else { throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); } } } } // IK constraint timelines. if (map.ContainsKey("ik")) { foreach (KeyValuePair <String, Object> constraintMap in (Dictionary <String, Object>)map["ik"]) { IkConstraintData constraint = skeletonData.FindIkConstraint(constraintMap.Key); var values = (List <Object>)constraintMap.Value; var timeline = new IkConstraintTimeline(values.Count); timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint); int frameIndex = 0; foreach (Dictionary <String, Object> valueMap in values) { float time = (float)valueMap["time"]; float mix = GetFloat(valueMap, "mix", 1); bool bendPositive = GetBoolean(valueMap, "bendPositive", true); timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * IkConstraintTimeline.ENTRIES]); } } // Transform constraint timelines. if (map.ContainsKey("transform")) { foreach (KeyValuePair <String, Object> constraintMap in (Dictionary <String, Object>)map["transform"]) { TransformConstraintData constraint = skeletonData.FindTransformConstraint(constraintMap.Key); var values = (List <Object>)constraintMap.Value; var timeline = new TransformConstraintTimeline(values.Count); timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint); int frameIndex = 0; foreach (Dictionary <String, Object> valueMap in values) { float time = (float)valueMap["time"]; float rotateMix = GetFloat(valueMap, "rotateMix", 1); float translateMix = GetFloat(valueMap, "translateMix", 1); float scaleMix = GetFloat(valueMap, "scaleMix", 1); float shearMix = GetFloat(valueMap, "shearMix", 1); timeline.SetFrame(frameIndex, time, rotateMix, translateMix, scaleMix, shearMix); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TransformConstraintTimeline.ENTRIES]); } } // Path constraint timelines. if (map.ContainsKey("paths")) { foreach (KeyValuePair <String, Object> constraintMap in (Dictionary <String, Object>)map["paths"]) { int index = skeletonData.FindPathConstraintIndex(constraintMap.Key); if (index == -1) { throw new Exception("Path constraint not found: " + constraintMap.Key); } PathConstraintData data = skeletonData.pathConstraints.Items[index]; var timelineMap = (Dictionary <String, Object>)constraintMap.Value; foreach (KeyValuePair <String, Object> timelineEntry in timelineMap) { var values = (List <Object>)timelineEntry.Value; var timelineName = (String)timelineEntry.Key; if (timelineName == "position" || timelineName == "spacing") { PathConstraintPositionTimeline timeline; float timelineScale = 1; if (timelineName == "spacing") { timeline = new PathConstraintSpacingTimeline(values.Count); if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) { timelineScale = scale; } } else { timeline = new PathConstraintPositionTimeline(values.Count); if (data.positionMode == PositionMode.Fixed) { timelineScale = scale; } } timeline.pathConstraintIndex = index; int frameIndex = 0; foreach (Dictionary <String, Object> valueMap in values) { timeline.SetFrame(frameIndex, (float)valueMap["time"], GetFloat(valueMap, timelineName, 0) * timelineScale); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * PathConstraintPositionTimeline.ENTRIES]); } else if (timelineName == "mix") { PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(values.Count); timeline.pathConstraintIndex = index; int frameIndex = 0; foreach (Dictionary <String, Object> valueMap in values) { timeline.SetFrame(frameIndex, (float)valueMap["time"], GetFloat(valueMap, "rotateMix", 1), GetFloat(valueMap, "translateMix", 1)); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * PathConstraintMixTimeline.ENTRIES]); } } } } // Deform timelines. if (map.ContainsKey("deform")) { foreach (KeyValuePair <String, Object> deformMap in (Dictionary <String, Object>)map["deform"]) { Skin skin = skeletonData.FindSkin(deformMap.Key); foreach (KeyValuePair <String, Object> slotMap in (Dictionary <String, Object>)deformMap.Value) { int slotIndex = skeletonData.FindSlotIndex(slotMap.Key); if (slotIndex == -1) { throw new Exception("Slot not found: " + slotMap.Key); } foreach (KeyValuePair <String, Object> timelineMap in (Dictionary <String, Object>)slotMap.Value) { var values = (List <Object>)timelineMap.Value; VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, timelineMap.Key); if (attachment == null) { throw new Exception("Deform attachment not found: " + timelineMap.Key); } bool weighted = attachment.bones != null; float[] vertices = attachment.vertices; int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length; var timeline = new DeformTimeline(values.Count); timeline.slotIndex = slotIndex; timeline.attachment = attachment; int frameIndex = 0; foreach (Dictionary <String, Object> valueMap in values) { float[] deform; if (!valueMap.ContainsKey("vertices")) { deform = weighted ? new float[deformLength] : vertices; } else { deform = new float[deformLength]; int start = GetInt(valueMap, "offset", 0); float[] verticesValue = GetFloatArray(valueMap, "vertices", 1); Array.Copy(verticesValue, 0, deform, start, verticesValue.Length); if (scale != 1) { for (int i = start, n = i + verticesValue.Length; i < n; i++) { deform[i] *= scale; } } if (!weighted) { for (int i = 0; i < deformLength; i++) { deform[i] += vertices[i]; } } } timeline.SetFrame(frameIndex, (float)valueMap["time"], deform); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); } } } } // Draw order timeline. if (map.ContainsKey("drawOrder") || map.ContainsKey("draworder")) { var values = (List <Object>)map[map.ContainsKey("drawOrder") ? "drawOrder" : "draworder"]; var timeline = new DrawOrderTimeline(values.Count); int slotCount = skeletonData.slots.Count; int frameIndex = 0; foreach (Dictionary <String, Object> drawOrderMap in values) { int[] drawOrder = null; if (drawOrderMap.ContainsKey("offsets")) { drawOrder = new int[slotCount]; for (int i = slotCount - 1; i >= 0; i--) { drawOrder[i] = -1; } var offsets = (List <Object>)drawOrderMap["offsets"]; int[] unchanged = new int[slotCount - offsets.Count]; int originalIndex = 0, unchangedIndex = 0; foreach (Dictionary <String, Object> offsetMap in offsets) { int slotIndex = skeletonData.FindSlotIndex((String)offsetMap["slot"]); if (slotIndex == -1) { throw new Exception("Slot not found: " + offsetMap["slot"]); } // Collect unchanged items. while (originalIndex != slotIndex) { unchanged[unchangedIndex++] = originalIndex++; } // Set changed items. int index = originalIndex + (int)(float)offsetMap["offset"]; drawOrder[index] = originalIndex++; } // Collect remaining unchanged items. while (originalIndex < slotCount) { unchanged[unchangedIndex++] = originalIndex++; } // Fill in unchanged items. for (int i = slotCount - 1; i >= 0; i--) { if (drawOrder[i] == -1) { drawOrder[i] = unchanged[--unchangedIndex]; } } } timeline.SetFrame(frameIndex++, (float)drawOrderMap["time"], drawOrder); } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); } // Event timeline. if (map.ContainsKey("events")) { var eventsMap = (List <Object>)map["events"]; var timeline = new EventTimeline(eventsMap.Count); int frameIndex = 0; foreach (Dictionary <String, Object> eventMap in eventsMap) { EventData eventData = skeletonData.FindEvent((String)eventMap["name"]); if (eventData == null) { throw new Exception("Event not found: " + eventMap["name"]); } var e = new Event((float)eventMap["time"], eventData); e.Int = GetInt(eventMap, "int", eventData.Int); e.Float = GetFloat(eventMap, "float", eventData.Float); e.String = GetString(eventMap, "string", eventData.String); timeline.SetFrame(frameIndex++, e); } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); } timelines.TrimExcess(); skeletonData.animations.Add(new Animation(name, timelines, duration)); }
public SkeletonData ReadSkeletonData(TextReader reader) { if (reader == null) { throw new ArgumentNullException("reader", "reader cannot be null."); } var scale = this.Scale; var skeletonData = new SkeletonData(); var root = Json.Deserialize(reader) as Dictionary <String, Object>; if (root == null) { throw new Exception("Invalid JSON."); } // Skeleton. if (root.ContainsKey("skeleton")) { var skeletonMap = (Dictionary <String, Object>)root["skeleton"]; skeletonData.hash = (String)skeletonMap["hash"]; skeletonData.version = (String)skeletonMap["spine"]; skeletonData.width = GetFloat(skeletonMap, "width", 0); skeletonData.height = GetFloat(skeletonMap, "height", 0); } // Bones. foreach (Dictionary <String, Object> boneMap in (List <Object>)root["bones"]) { BoneData parent = null; if (boneMap.ContainsKey("parent")) { parent = skeletonData.FindBone((String)boneMap["parent"]); if (parent == null) { throw new Exception("Parent bone not found: " + boneMap["parent"]); } } var data = new BoneData(skeletonData.Bones.Count, (String)boneMap["name"], parent); data.length = GetFloat(boneMap, "length", 0) * scale; data.x = GetFloat(boneMap, "x", 0) * scale; data.y = GetFloat(boneMap, "y", 0) * scale; data.rotation = GetFloat(boneMap, "rotation", 0); data.scaleX = GetFloat(boneMap, "scaleX", 1); data.scaleY = GetFloat(boneMap, "scaleY", 1); data.shearX = GetFloat(boneMap, "shearX", 0); data.shearY = GetFloat(boneMap, "shearY", 0); data.inheritRotation = GetBoolean(boneMap, "inheritRotation", true); data.inheritScale = GetBoolean(boneMap, "inheritScale", true); skeletonData.bones.Add(data); } // Slots. if (root.ContainsKey("slots")) { foreach (Dictionary <String, Object> slotMap in (List <Object>)root["slots"]) { var slotName = (String)slotMap["name"]; var boneName = (String)slotMap["bone"]; BoneData boneData = skeletonData.FindBone(boneName); if (boneData == null) { throw new Exception("Slot bone not found: " + boneName); } var data = new SlotData(skeletonData.Slots.Count, slotName, boneData); if (slotMap.ContainsKey("color")) { var color = (String)slotMap["color"]; data.r = ToColor(color, 0); data.g = ToColor(color, 1); data.b = ToColor(color, 2); data.a = ToColor(color, 3); } data.attachmentName = GetString(slotMap, "attachment", null); if (slotMap.ContainsKey("blend")) { data.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (String)slotMap["blend"], false); } else { data.blendMode = BlendMode.normal; } skeletonData.slots.Add(data); } } // IK constraints. if (root.ContainsKey("ik")) { foreach (Dictionary <String, Object> constraintMap in (List <Object>)root["ik"]) { IkConstraintData data = new IkConstraintData((String)constraintMap["name"]); foreach (String boneName in (List <Object>)constraintMap["bones"]) { BoneData bone = skeletonData.FindBone(boneName); if (bone == null) { throw new Exception("IK constraint bone not found: " + boneName); } data.bones.Add(bone); } String targetName = (String)constraintMap["target"]; data.target = skeletonData.FindBone(targetName); if (data.target == null) { throw new Exception("Target bone not found: " + targetName); } data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1; data.mix = GetFloat(constraintMap, "mix", 1); skeletonData.ikConstraints.Add(data); } } // Transform constraints. if (root.ContainsKey("transform")) { foreach (Dictionary <String, Object> constraintMap in (List <Object>)root["transform"]) { TransformConstraintData data = new TransformConstraintData((String)constraintMap["name"]); foreach (String boneName in (List <Object>)constraintMap["bones"]) { BoneData bone = skeletonData.FindBone(boneName); if (bone == null) { throw new Exception("Transform constraint bone not found: " + boneName); } data.bones.Add(bone); } String targetName = (String)constraintMap["target"]; data.target = skeletonData.FindBone(targetName); if (data.target == null) { throw new Exception("Target bone not found: " + targetName); } data.offsetRotation = GetFloat(constraintMap, "rotation", 0); data.offsetX = GetFloat(constraintMap, "x", 0) * scale; data.offsetY = GetFloat(constraintMap, "y", 0) * scale; data.offsetScaleX = GetFloat(constraintMap, "scaleX", 0); data.offsetScaleY = GetFloat(constraintMap, "scaleY", 0); data.offsetShearY = GetFloat(constraintMap, "shearY", 0); data.rotateMix = GetFloat(constraintMap, "rotateMix", 1); data.translateMix = GetFloat(constraintMap, "translateMix", 1); data.scaleMix = GetFloat(constraintMap, "scaleMix", 1); data.shearMix = GetFloat(constraintMap, "shearMix", 1); skeletonData.transformConstraints.Add(data); } } // Path constraints. if (root.ContainsKey("path")) { foreach (Dictionary <String, Object> constraintMap in (List <Object>)root["path"]) { PathConstraintData data = new PathConstraintData((String)constraintMap["name"]); foreach (String boneName in (List <Object>)constraintMap["bones"]) { BoneData bone = skeletonData.FindBone(boneName); if (bone == null) { throw new Exception("Path bone not found: " + boneName); } data.bones.Add(bone); } String targetName = (String)constraintMap["target"]; data.target = skeletonData.FindSlot(targetName); if (data.target == null) { throw new Exception("Target slot not found: " + targetName); } data.positionMode = (PositionMode)Enum.Parse(typeof(PositionMode), GetString(constraintMap, "positionMode", "percent"), true); data.spacingMode = (SpacingMode)Enum.Parse(typeof(SpacingMode), GetString(constraintMap, "spacingMode", "length"), true); data.rotateMode = (RotateMode)Enum.Parse(typeof(RotateMode), GetString(constraintMap, "rotateMode", "tangent"), true); data.offsetRotation = GetFloat(constraintMap, "rotation", 0); data.position = GetFloat(constraintMap, "position", 0); if (data.positionMode == PositionMode.Fixed) { data.position *= scale; } data.spacing = GetFloat(constraintMap, "spacing", 0); if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) { data.spacing *= scale; } data.rotateMix = GetFloat(constraintMap, "rotateMix", 1); data.translateMix = GetFloat(constraintMap, "translateMix", 1); skeletonData.pathConstraints.Add(data); } } // Skins. if (root.ContainsKey("skins")) { foreach (KeyValuePair <String, Object> skinMap in (Dictionary <String, Object>)root["skins"]) { var skin = new Skin(skinMap.Key); foreach (KeyValuePair <String, Object> slotEntry in (Dictionary <String, Object>)skinMap.Value) { int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); foreach (KeyValuePair <String, Object> entry in ((Dictionary <String, Object>)slotEntry.Value)) { try { Attachment attachment = ReadAttachment((Dictionary <String, Object>)entry.Value, skin, slotIndex, entry.Key); if (attachment != null) { skin.AddAttachment(slotIndex, entry.Key, attachment); } } catch (Exception e) { throw new Exception("Error reading attachment: " + entry.Key + ", skin: " + skin, e); } } } skeletonData.skins.Add(skin); if (skin.name == "default") { skeletonData.defaultSkin = skin; } } } // Linked meshes. for (int i = 0, n = linkedMeshes.Count; i < n; i++) { LinkedMesh linkedMesh = linkedMeshes[i]; Skin skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.FindSkin(linkedMesh.skin); if (skin == null) { throw new Exception("Slot not found: " + linkedMesh.skin); } Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent); if (parent == null) { throw new Exception("Parent mesh not found: " + linkedMesh.parent); } linkedMesh.mesh.ParentMesh = (MeshAttachment)parent; linkedMesh.mesh.UpdateUVs(); } linkedMeshes.Clear(); // Events. if (root.ContainsKey("events")) { foreach (KeyValuePair <String, Object> entry in (Dictionary <String, Object>)root["events"]) { var entryMap = (Dictionary <String, Object>)entry.Value; var data = new EventData(entry.Key); data.Int = GetInt(entryMap, "int", 0); data.Float = GetFloat(entryMap, "float", 0); data.String = GetString(entryMap, "string", null); skeletonData.events.Add(data); } } // Animations. if (root.ContainsKey("animations")) { foreach (KeyValuePair <String, Object> entry in (Dictionary <String, Object>)root["animations"]) { try { ReadAnimation((Dictionary <String, Object>)entry.Value, entry.Key, skeletonData); } catch (Exception e) { throw new Exception("Error reading animation: " + entry.Key, e); } } } skeletonData.bones.TrimExcess(); skeletonData.slots.TrimExcess(); skeletonData.skins.TrimExcess(); skeletonData.events.TrimExcess(); skeletonData.animations.TrimExcess(); skeletonData.ikConstraints.TrimExcess(); return(skeletonData); }