private void readAnimation(String name, DataInput input, SkeletonData skeletonData) { Array<Timeline> timelines = new Array(); float scale = this.scale; float duration = 0; try { // Slot timelines. for (int i = 0, n = input.readInt(true); i < n; i++) { int slotIndex = input.readInt(true); for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { int timelineType = input.readByte(); int frameCount = input.readInt(true); switch (timelineType) { case TIMELINE_COLOR: { ColorTimeline timeline = new ColorTimeline(frameCount); timeline.slotIndex = slotIndex; for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { float time = input.readFloat(); Color.rgba8888ToColor(tempColor, input.readInt()); timeline.setFrame( frameIndex, time, tempColor.r, tempColor.g, tempColor.b, tempColor.a); if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[frameCount * 5 - 5]); break; } case TIMELINE_ATTACHMENT: AttachmentTimeline timeline = new AttachmentTimeline(frameCount); timeline.slotIndex = slotIndex; for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) timeline.setFrame(frameIndex, input.readFloat(), input.readString()); timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[frameCount - 1]); break; } } } // Bone timelines. for (int i = 0, n = input.readInt(true); i < n; i++) { int boneIndex = input.readInt(true); for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { int timelineType = input.readByte(); int frameCount = input.readInt(true); switch (timelineType) { case TIMELINE_ROTATE: { RotateTimeline timeline = new RotateTimeline(frameCount); timeline.boneIndex = boneIndex; for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { timeline.setFrame(frameIndex, input.readFloat(), input.readFloat()); if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[frameCount * 2 - 2]); break; } case TIMELINE_TRANSLATE: case TIMELINE_SCALE: TranslateTimeline timeline; float timelineScale = 1; if (timelineType == TIMELINE_SCALE) timeline = new ScaleTimeline(frameCount); else { timeline = new TranslateTimeline(frameCount); timelineScale = scale; } timeline.boneIndex = boneIndex; for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { timeline.setFrame( frameIndex, input.readFloat(), input.readFloat() * timelineScale, input.readFloat() * timelineScale); if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[frameCount * 3 - 3]); break; } } } // FFD timelines. for (int i = 0, n = input.readInt(true); i < n; i++) { Skin skin = skeletonData.getSkins().get(input.readInt(true) + 1); for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { int slotIndex = input.readInt(true); for (int iii = 0, nnn = input.readInt(true); iii < nnn; iii++) { Attachment attachment = skin.getAttachment(slotIndex, input.readString()); int frameCount = input.readInt(true); FfdTimeline timeline = new FfdTimeline(frameCount); timeline.slotIndex = slotIndex; timeline.attachment = attachment; for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { float time = input.readFloat(); float[] vertices; int vertexCount; if (attachment instanceof MeshAttachment) vertexCount = ((MeshAttachment) attachment).getVertices().length; else vertexCount = ((SkinnedMeshAttachment) attachment).getWeights().length / 3 * 2; int end = input.readInt(true); if (end == 0) { if (attachment instanceof MeshAttachment) vertices = ((MeshAttachment) attachment).getVertices(); else vertices = new float[vertexCount]; } else { vertices = new float[vertexCount]; int start = input.readInt(true); end += start; if (scale == 1) { for (int v = start; v < end; v++) vertices[v] = input.readFloat(); } else { for (int v = start; v < end; v++) vertices[v] = input.readFloat() * scale; } if (attachment instanceof MeshAttachment) { float[] meshVertices = ((MeshAttachment) attachment).getVertices(); for (int v = 0, vn = vertices.length; v < vn; v++) vertices[v] += meshVertices[v]; } } timeline.setFrame(frameIndex, time, vertices); if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[frameCount - 1]); } } } // Draw order timeline. int drawOrderCount = input.readInt(true); if (drawOrderCount > 0) { DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrderCount); int slotCount = skeletonData.slots.size; for (int i = 0; i < drawOrderCount; i++) { int offsetCount = input.readInt(true); int[] drawOrder = new int[slotCount]; for (int ii = slotCount - 1; ii >= 0; ii--) drawOrder[ii] = -1; int[] unchanged = new int[slotCount - offsetCount]; int originalIndex = 0, unchangedIndex = 0; for (int ii = 0; ii < offsetCount; ii++) { int slotIndex = input.readInt(true); // Collect unchanged items. while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++; // Set changed items. drawOrder[originalIndex + input.readInt(true)] = originalIndex++; } // Collect remaining unchanged items. while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++; // Fill in unchanged items. for (int ii = slotCount - 1; ii >= 0; ii--) if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; timeline.setFrame(i, input.readFloat(), drawOrder); } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[drawOrderCount - 1]); } // Event timeline. int eventCount = input.readInt(true); if (eventCount > 0) { EventTimeline timeline = new EventTimeline(eventCount); for (int i = 0; i < eventCount; i++) { float time = input.readFloat(); EventData eventData = skeletonData.events.get(input.readInt(true)); Event event = new Event(eventData); event.intValue = input.readInt(false); event.floatValue = input.readFloat(); event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue; timeline.setFrame(i, time, event); } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[eventCount - 1]); } } catch (IOException ex) { throw new SerializationException("Error reading skeleton file.", ex); } timelines.shrink(); skeletonData.addAnimation(new Animation(name, timelines, duration)); }
private void applyRotateTimeline( Timeline timeline, Skeleton skeleton, float time, float alpha, boolean setupPose, float[] timelinesRotation, int i, boolean firstFrame) { if (firstFrame) timelinesRotation[i] = 0; if (alpha == 1) { timeline.apply(skeleton, 0, time, null, 1, setupPose, false); return; } RotateTimeline rotateTimeline = (RotateTimeline) timeline; Bone bone = skeleton.bones.get(rotateTimeline.boneIndex); float[] frames = rotateTimeline.frames; if (time < frames[0]) { // Time is before first frame. if (setupPose) bone.rotation = bone.data.rotation; return; } float r2; if (time >= frames[frames.length - ENTRIES]) // Time is after last frame. r2 = bone.data.rotation + frames[frames.length + PREV_ROTATION]; else { // Interpolate between the previous frame and the current frame. int frame = Animation.binarySearch(frames, time, ENTRIES); float prevRotation = frames[frame + PREV_ROTATION]; float frameTime = frames[frame]; float percent = rotateTimeline.getCurvePercent( (frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); r2 = frames[frame + ROTATION] - prevRotation; r2 -= (16384 - (int) (16384.499999999996 - r2 / 360)) * 360; r2 = prevRotation + r2 * percent + bone.data.rotation; r2 -= (16384 - (int) (16384.499999999996 - r2 / 360)) * 360; } // Mix between rotations using the direction of the shortest route on the first frame while // detecting crosses. float r1 = setupPose ? bone.data.rotation : bone.rotation; float total, diff = r2 - r1; if (diff == 0) total = timelinesRotation[i]; else { diff -= (16384 - (int) (16384.499999999996 - diff / 360)) * 360; float lastTotal, lastDiff; if (firstFrame) { lastTotal = 0; lastDiff = diff; } else { lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops. lastDiff = timelinesRotation[i + 1]; // Difference between bones. } boolean current = diff > 0, dir = lastTotal >= 0; // Detect cross at 0 (not 180). if (Math.signum(lastDiff) != Math.signum(diff) && Math.abs(lastDiff) <= 90) { // A cross after a 360 rotation is a loop. if (Math.abs(lastTotal) > 180) lastTotal += 360 * Math.signum(lastTotal); dir = current; } total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal. if (dir != current) total += 360 * Math.signum(lastTotal); timelinesRotation[i] = total; } timelinesRotation[i + 1] = diff; r1 += total * alpha; bone.rotation = r1 - (16384 - (int) (16384.499999999996 - r1 / 360)) * 360; }
private void readAnimation(String name, JsonValue map, SkeletonData skeletonData) { Array<Timeline> timelines = new Array(); float duration = 0; for (JsonValue boneMap = map.getChild("bones"); boneMap != null; boneMap = boneMap.next()) { int boneIndex = skeletonData.findBoneIndex(boneMap.name()); if (boneIndex == -1) throw new SerializationException("Bone not found: " + boneMap.name()); for (JsonValue timelineMap = boneMap.child(); timelineMap != null; timelineMap = timelineMap.next()) { String timelineName = timelineMap.name(); if (timelineName.equals(TIMELINE_ROTATE)) { RotateTimeline timeline = new RotateTimeline(timelineMap.size()); timeline.setBoneIndex(boneIndex); int frameIndex = 0; for (JsonValue valueMap = timelineMap.child(); valueMap != null; valueMap = valueMap.next()) { float time = valueMap.getFloat("time"); timeline.setFrame(frameIndex, time, valueMap.getFloat("angle")); readCurve(timeline, frameIndex, valueMap); frameIndex++; } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 2 - 2]); } else if (timelineName.equals(TIMELINE_TRANSLATE) || timelineName.equals(TIMELINE_SCALE)) { TranslateTimeline timeline; float timelineScale = 1; if (timelineName.equals(TIMELINE_SCALE)) timeline = new ScaleTimeline(timelineMap.size()); else { timeline = new TranslateTimeline(timelineMap.size()); timelineScale = scale; } timeline.setBoneIndex(boneIndex); int frameIndex = 0; for (JsonValue valueMap = timelineMap.child(); valueMap != null; valueMap = valueMap.next()) { float time = valueMap.getFloat("time"); float x = valueMap.getFloat("x", 0), y = valueMap.getFloat("y", 0); timeline.setFrame(frameIndex, time, x * timelineScale, y * timelineScale); readCurve(timeline, frameIndex, valueMap); frameIndex++; } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 3 - 3]); } else throw new RuntimeException( "Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name() + ")"); } } for (JsonValue slotMap = map.getChild("slots"); slotMap != null; slotMap = slotMap.next()) { int slotIndex = skeletonData.findSlotIndex(slotMap.name()); for (JsonValue timelineMap = slotMap.child(); timelineMap != null; timelineMap = timelineMap.next()) { String timelineName = timelineMap.name(); if (timelineName.equals(TIMELINE_COLOR)) { ColorTimeline timeline = new ColorTimeline(timelineMap.size()); timeline.setSlotIndex(slotIndex); int frameIndex = 0; for (JsonValue valueMap = timelineMap.child(); valueMap != null; valueMap = valueMap.next()) { float time = valueMap.getFloat("time"); Color color = Color.valueOf(valueMap.getString("color")); timeline.setFrame(frameIndex, time, color.r, color.g, color.b, color.a); readCurve(timeline, frameIndex, valueMap); frameIndex++; } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 5 - 5]); } else if (timelineName.equals(TIMELINE_ATTACHMENT)) { AttachmentTimeline timeline = new AttachmentTimeline(timelineMap.size()); timeline.setSlotIndex(slotIndex); int frameIndex = 0; for (JsonValue valueMap = timelineMap.child(); valueMap != null; valueMap = valueMap.next()) { float time = valueMap.getFloat("time"); timeline.setFrame(frameIndex++, time, valueMap.getString("name")); } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() - 1]); } else throw new RuntimeException( "Invalid timeline type for a slot: " + timelineName + " (" + slotMap.name() + ")"); } } timelines.shrink(); skeletonData.addAnimation(new Animation(name, timelines, duration)); }