Implementation
SkeletonData readSkeletonData(Object object) {
final double scale = this.scale;
final SkeletonData skeletonData = SkeletonData('');
dynamic root;
if (object is String) {
root = json.decode(object);
} else if (object is Map) {
root = object;
} else {
throw ArgumentError('object must be a String or Map.');
}
// Skeleton
if (root.containsKey('skeleton')) {
final dynamic skeletonMap = root['skeleton'];
skeletonData
..hash = _getString(skeletonMap, 'hash')
..version = _getString(skeletonMap, 'spine')
..width = _getDouble(skeletonMap, 'width')
..height = _getDouble(skeletonMap, 'height')
..fps = _getDouble(skeletonMap, 'fps')
..imagesPath = _getString(skeletonMap, 'images');
}
// Bones
if (root.containsKey('bones')) {
for (int i = 0; i < root['bones'].length; i++) {
final dynamic boneMap = root['bones'][i];
BoneData? parent;
final String parentName = _getString(boneMap, 'parent');
if (parentName.isNotEmpty) {
parent = skeletonData.findBone(parentName);
if (parent == null) {
throw StateError('Parent bone not found: $parentName');
}
}
final BoneData data = BoneData(
skeletonData.bones.length, _getString(boneMap, 'name'), parent)
..length = _getDouble(boneMap, 'length', 0.0) * scale
..x = _getDouble(boneMap, 'x', 0.0) * scale
..y = _getDouble(boneMap, 'y', 0.0) * scale
..rotation = _getDouble(boneMap, 'rotation', 0.0)
..scaleX = _getDouble(boneMap, 'scaleX', 1.0)
..scaleY = _getDouble(boneMap, 'scaleY', 1.0)
..shearX = _getDouble(boneMap, 'shearX', 0.0)
..shearY = _getDouble(boneMap, 'shearY', 0.0)
..transformMode = SkeletonJson.transformModeFromString(
_getString(boneMap, 'transform', 'normal'));
skeletonData.bones.add(data);
}
}
// Slots.
if (root.containsKey('slots')) {
for (int i = 0; i < root['slots'].length; i++) {
final dynamic slotMap = root['slots'][i];
final String slotName = _getString(slotMap, 'name');
final String boneName = _getString(slotMap, 'bone');
final BoneData? boneData = skeletonData.findBone(boneName);
if (boneData == null) {
throw StateError('Slot bone not found: ' + boneName);
}
final SlotData data =
SlotData(skeletonData.slots.length, slotName, boneData);
final String color = _getString(slotMap, 'color');
if (color.isNotEmpty) data.color.setFromString(color);
final String dark = _getString(slotMap, 'dark');
if (dark.isNotEmpty) {
data.darkColor = Color(1.0, 1.0, 1.0, 1.0);
data.darkColor!.setFromString(dark);
}
data
..attachmentName = _getString(slotMap, 'attachment')
..blendMode = SkeletonJson.blendModeFromString(
_getString(slotMap, 'blend', 'normal'));
skeletonData.slots.add(data);
}
}
// IK constraints
if (root.containsKey('ik')) {
for (int i = 0; i < root['ik'].length; i++) {
final dynamic constraintMap = root['ik'][i];
final IkConstraintData data =
IkConstraintData(_getString(constraintMap, 'name'))
..order = _getInt(constraintMap, 'order', 0);
for (int j = 0; j < constraintMap['bones'].length; j++) {
final String boneName = constraintMap['bones'][j];
final BoneData? bone = skeletonData.findBone(boneName);
if (bone == null) throw StateError('IK bone not found: ' + boneName);
data.bones.add(bone);
}
final String targetName = _getString(constraintMap, 'target');
data.target = skeletonData.findBone(targetName);
if (data.target == null) {
throw StateError('IK target bone not found: ' + targetName);
}
data
..bendDirection =
_getBool(constraintMap, 'bendPositive', true) ? 1 : -1
..mix = _getDouble(constraintMap, 'mix', 1.0);
skeletonData.ikConstraints.add(data);
}
}
// Transform constraints.
if (root.containsKey('transform')) {
for (int i = 0; i < root['transform'].length; i++) {
final dynamic constraintMap = root['transform'][i];
final TransformConstraintData data =
TransformConstraintData(_getString(constraintMap, 'name'))
..order = _getInt(constraintMap, 'order', 0);
for (int j = 0; j < constraintMap['bones'].length; j++) {
final String boneName = constraintMap['bones'][j]!;
final BoneData? bone = skeletonData.findBone(boneName);
if (bone == null) {
throw StateError('Transform constraint bone not found: $boneName');
}
data.bones.add(bone);
}
final String targetName = _getString(constraintMap, 'target');
data.target = skeletonData.findBone(targetName);
if (data.target == null) {
throw StateError(
'Transform constraint target bone not found: $targetName');
}
data
..local = _getBool(constraintMap, 'local', false)
..relative = _getBool(constraintMap, 'relative', false)
..offsetRotation = _getDouble(constraintMap, 'rotation', 0.0)
..offsetX = _getDouble(constraintMap, 'x', 0.0) * scale
..offsetY = _getDouble(constraintMap, 'y', 0.0) * scale
..offsetScaleX = _getDouble(constraintMap, 'scaleX', 0.0)
..offsetScaleY = _getDouble(constraintMap, 'scaleY', 0.0)
..offsetShearY = _getDouble(constraintMap, 'shearY', 0.0)
..rotateMix = _getDouble(constraintMap, 'rotateMix', 1.0)
..translateMix = _getDouble(constraintMap, 'translateMix', 1.0)
..scaleMix = _getDouble(constraintMap, 'scaleMix', 1.0)
..shearMix = _getDouble(constraintMap, 'shearMix', 1.0);
skeletonData.transformConstraints.add(data);
}
}
// Path constraints.
if (root.containsKey('path')) {
for (int i = 0; i < root['path'].length; i++) {
final dynamic constraintMap = root['path'][i];
final PathConstraintData data =
PathConstraintData(_getString(constraintMap, 'name'))
..order = _getInt(constraintMap, 'order', 0);
for (int j = 0; j < constraintMap['bones'].length; j++) {
final String boneName = constraintMap['bones'][j]!;
final BoneData? bone = skeletonData.findBone(boneName);
if (bone == null) {
throw StateError('Transform constraint bone not found: $boneName');
}
data.bones.add(bone);
}
final String targetName = _getString(constraintMap, 'target');
data.target = skeletonData.findSlot(targetName);
if (data.target == null) {
throw StateError('Path target slot not found: $targetName');
}
data
..positionMode = SkeletonJson.positionModeFromString(
_getString(constraintMap, 'positionMode', 'percent'))
..spacingMode = SkeletonJson.spacingModeFromString(
_getString(constraintMap, 'spacingMode', 'length'))
..rotateMode = SkeletonJson.rotateModeFromString(
_getString(constraintMap, 'rotateMode', 'tangent'))
..offsetRotation = _getDouble(constraintMap, 'rotation', 0.0)
..position = _getDouble(constraintMap, 'position', 0.0);
if (data.positionMode == PositionMode.fixed) {
data.position = data.position * scale;
}
data.spacing = _getDouble(constraintMap, 'spacing', 0.0);
if (data.spacingMode == SpacingMode.length ||
data.spacingMode == SpacingMode.fixed) {
data.spacing = data.spacing * scale;
}
data
..rotateMix = _getDouble(constraintMap, 'rotateMix', 1.0)
..translateMix = _getDouble(constraintMap, 'translateMix', 1.0);
skeletonData.pathConstraints.add(data);
}
}
// // Skins.
if (root.containsKey('skins')) {
dynamic skins = {};
if (root['skins'] is List) {
for (dynamic skin in root['skins']) {
skins[skin['name']] = skin['attachments'];
}
} else {
skins = root['skins'];
}
for (String skinName in skins.keys) {
final dynamic skinMap = skins[skinName];
final Skin skin = Skin(skinName);
for (String slotName in skinMap.keys) {
final int slotIndex = skeletonData.findSlotIndex(slotName);
if (slotIndex == -1) throw StateError('Slot not found: $slotName');
final dynamic slotMap = skinMap[slotName];
for (String entryName in slotMap.keys) {
final Attachment? attachment = readAttachment(
slotMap[entryName], skin, slotIndex, entryName, skeletonData);
if (attachment != null) {
skin.addAttachment(slotIndex, entryName, attachment);
}
}
}
skeletonData.skins.add(skin);
if (skin.name == 'default') skeletonData.defaultSkin = skin;
}
}
// Linked meshes.
final int n = linkedMeshes.length;
for (int i = 0; i < n; i++) {
final LinkedMesh linkedMesh = linkedMeshes[i];
final Skin? skin = linkedMesh.skin.isEmpty
? skeletonData.defaultSkin
: skeletonData.findSkin(linkedMesh.skin);
if (skin == null) throw StateError('Skin not found: $linkedMesh.skin');
final Attachment? parent =
skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parent == null) {
throw StateError('Parent mesh not found: $linkedMesh.parent');
}
linkedMesh.mesh.parentMesh = parent as MeshAttachment?;
linkedMesh.mesh.updateUVs();
}
linkedMeshes.length = 0;
// Events.
if (root.containsKey('events')) {
for (String eventName in root['events'].keys) {
final dynamic eventMap = root['events'][eventName];
final EventData data = EventData(eventName)
..intValue = _getInt(eventMap, 'int')
..floatValue = _getDouble(eventMap, 'float')
..stringValue = _getString(eventMap, 'string');
skeletonData.events.add(data);
}
}
// Animations.
if (root.containsKey('animations')) {
for (String animationName in root['animations'].keys) {
final dynamic animationMap = root['animations'][animationName];
readAnimation(animationMap, animationName, skeletonData);
}
}
return skeletonData;
}