public Animation (String name, ExposedList<Timeline> timelines, float duration) {
			if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
			if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null.");
			this.name = name;
			this.timelines = timelines;
			this.duration = duration;
		}
Example #2
0
		public Skeleton (SkeletonData data) {
			if (data == null) throw new ArgumentNullException("data cannot be null.");
			this.data = data;

			bones = new ExposedList<Bone>(data.bones.Count);
			foreach (BoneData boneData in data.bones) {
				Bone parent = boneData.parent == null ? null : bones.Items[data.bones.IndexOf(boneData.parent)];
				Bone bone = new Bone(boneData, this, parent);
				if (parent != null) parent.children.Add(bone);
				bones.Add(bone);
			}

			slots = new ExposedList<Slot>(data.slots.Count);
			drawOrder = new ExposedList<Slot>(data.slots.Count);
			foreach (SlotData slotData in data.slots) {
				Bone bone = bones.Items[data.bones.IndexOf(slotData.boneData)];
				Slot slot = new Slot(slotData, bone);
				slots.Add(slot);
				drawOrder.Add(slot);
			}

			ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
			foreach (IkConstraintData ikConstraintData in data.ikConstraints)
				ikConstraints.Add(new IkConstraint(ikConstraintData, this));

			transformConstraints = new ExposedList<TransformConstraint>(data.transformConstraints.Count);
			foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
				transformConstraints.Add(new TransformConstraint(transformConstraintData, this));

			UpdateCache();
			UpdateWorldTransform();
		}
		/// <summary>Applies all the animation's timelines to the specified skeleton.</summary>
		/// <param name="skeleton">The skeleton to be posed.</param>
		/// <param name="lastTime">The last time the animation was applied.</param>
		/// <param name="time">The point in time in the animation to apply to the skeleton.</param>
		/// <param name="loop">If true, time wraps within the animation duration.</param>
		/// <param name="events">Any triggered events are added. May be null.</param>
		/// <param name="alpha">The percentage between this animation's pose and the current pose.</param>
		/// <param name="setupPose">If true, the animation is mixed with the setup pose, else it is mixed with the current pose. Passing true when alpha is 1 is slightly more efficient.</param>
		/// <param name="mixingOut">True when mixing over time toward the setup or current pose, false when mixing toward the keyed pose. Irrelevant when alpha is 1.</param>
		/// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, bool, bool)"/>
		public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events, float alpha, bool setupPose, bool mixingOut) {
			if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");

			if (loop && duration != 0) {
				time %= duration;
				if (lastTime > 0) lastTime %= duration;
			}

			ExposedList<Timeline> timelines = this.timelines;
			for (int i = 0, n = timelines.Count; i < n; i++)
				timelines.Items[i].Apply(skeleton, lastTime, time, events, alpha, setupPose, mixingOut);
		}
Example #4
0
		public IkConstraint (IkConstraintData data, Skeleton skeleton) {
			if (data == null) throw new ArgumentNullException("data cannot be null.");
			if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
			this.data = data;
			mix = data.mix;
			bendDirection = data.bendDirection;

			bones = new ExposedList<Bone>(data.bones.Count);
			foreach (BoneData boneData in data.bones)
				bones.Add(skeleton.FindBone(boneData.name));
			target = skeleton.FindBone(data.target.name);
		}
		public PathConstraint (PathConstraintData data, Skeleton skeleton) {
			if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
			if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
			this.data = data;
			bones = new ExposedList<Bone>(data.Bones.Count);
			foreach (BoneData boneData in data.bones)
				bones.Add(skeleton.FindBone(boneData.name));
			target = skeleton.FindSlot(data.target.name);
			position = data.position;
			spacing = data.spacing;
			rotateMix = data.rotateMix;
			translateMix = data.translateMix;
		}
		public TransformConstraint (TransformConstraintData data, Skeleton skeleton) {
			if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
			if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
			this.data = data;
			rotateMix = data.rotateMix;
			translateMix = data.translateMix;
			scaleMix = data.scaleMix;
			shearMix = data.shearMix;

			bones = new ExposedList<Bone>();
			foreach (BoneData boneData in data.bones)
				bones.Add (skeleton.FindBone (boneData.name));
			
			target = skeleton.FindBone(data.target.name);
		}
Example #7
0
		private void ReadAnimation (String name, Dictionary<String, Object> map, SkeletonData skeletonData) {
			var timelines = new ExposedList<Timeline>();
			float duration = 0;
			float scale = Scale;

			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(timeline, frameIndex, valueMap);
								frameIndex++;
							}
							timelines.Add(timeline);
							duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 5 - 5]);

						} 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 + ")");
					}
				}
			}

			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) {
								float time = (float)valueMap["time"];
								timeline.SetFrame(frameIndex, time, (float)valueMap["angle"]);
								ReadCurve(timeline, frameIndex, valueMap);
								frameIndex++;
							}
							timelines.Add(timeline);
							duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]);

						} else if (timelineName == "translate" || timelineName == "scale") {
							TranslateTimeline timeline;
							float timelineScale = 1;
							if (timelineName == "scale")
								timeline = new ScaleTimeline(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 = valueMap.ContainsKey("x") ? (float)valueMap["x"] : 0;
								float y = valueMap.ContainsKey("y") ? (float)valueMap["y"] : 0;
								timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale);
								ReadCurve(timeline, frameIndex, valueMap);
								frameIndex++;
							}
							timelines.Add(timeline);
							duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]);

						} else
							throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
					}
				}
			}

			if (map.ContainsKey("ik")) {
				foreach (KeyValuePair<String, Object> ikMap in (Dictionary<String, Object>)map["ik"]) {
					IkConstraintData ikConstraint = skeletonData.FindIkConstraint(ikMap.Key);
					var values = (List<Object>)ikMap.Value;
					var timeline = new IkConstraintTimeline(values.Count);
					timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(ikConstraint);
					int frameIndex = 0;
					foreach (Dictionary<String, Object> valueMap in values) {
						float time = (float)valueMap["time"];
						float mix = valueMap.ContainsKey("mix") ? (float)valueMap["mix"] : 1;
						bool bendPositive = valueMap.ContainsKey("bendPositive") ? (bool)valueMap["bendPositive"] : true;
						timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1);
						ReadCurve(timeline, frameIndex, valueMap);
						frameIndex++;
					}
					timelines.Add(timeline);
					duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]);
				}
			}

			if (map.ContainsKey("ffd")) {
				foreach (KeyValuePair<String, Object> ffdMap in (Dictionary<String, Object>)map["ffd"]) {
					Skin skin = skeletonData.FindSkin(ffdMap.Key);
					foreach (KeyValuePair<String, Object> slotMap in (Dictionary<String, Object>)ffdMap.Value) {
						int slotIndex = skeletonData.FindSlotIndex(slotMap.Key);
						foreach (KeyValuePair<String, Object> meshMap in (Dictionary<String, Object>)slotMap.Value) {
							var values = (List<Object>)meshMap.Value;
							var timeline = new FFDTimeline(values.Count);
							Attachment attachment = skin.GetAttachment(slotIndex, meshMap.Key);
							if (attachment == null) throw new Exception("FFD attachment not found: " + meshMap.Key);
							timeline.slotIndex = slotIndex;
							timeline.attachment = attachment;

							int vertexCount;
							if (attachment is MeshAttachment)
								vertexCount = ((MeshAttachment)attachment).vertices.Length;
							else
								vertexCount = ((WeightedMeshAttachment)attachment).Weights.Length / 3 * 2;

							int frameIndex = 0;
							foreach (Dictionary<String, Object> valueMap in values) {
								float[] vertices;
								if (!valueMap.ContainsKey("vertices")) {
									if (attachment is MeshAttachment)
										vertices = ((MeshAttachment)attachment).vertices;
									else
										vertices = new float[vertexCount];
								} else {
									var verticesValue = (List<Object>)valueMap["vertices"];
									vertices = new float[vertexCount];
									int start = GetInt(valueMap, "offset", 0);
									if (scale == 1) {
										for (int i = 0, n = verticesValue.Count; i < n; i++)
											vertices[i + start] = (float)verticesValue[i];
									} else {
										for (int i = 0, n = verticesValue.Count; i < n; i++)
											vertices[i + start] = (float)verticesValue[i] * scale;
									}
									if (attachment is MeshAttachment) {
										float[] meshVertices = ((MeshAttachment)attachment).vertices;
										for (int i = 0; i < vertexCount; i++)
											vertices[i] += meshVertices[i];
									}
								}

								timeline.SetFrame(frameIndex, (float)valueMap["time"], vertices);
								ReadCurve(timeline, frameIndex, valueMap);
								frameIndex++;
							}
							timelines.Add(timeline);
							duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
						}
					}
				}
			}

			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]);
			}

			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"]);
					float time = (float)eventMap["time"];
					var e = new Event(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++, time, e);
				}
				timelines.Add(timeline);
				duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
			}

			timelines.TrimExcess();
			skeletonData.animations.Add(new Animation(name, timelines, duration));
		}
Example #8
0
	private bool CheckIfMustUpdateMeshStructure (ExposedList<int> attachmentsTriangleCountTemp, ExposedList<bool> attachmentsFlipStateTemp, ExposedList<LastState.AddSubmeshArguments> addSubmeshArgumentsTemp) {
		// Check if any mesh settings were changed
		bool mustUpdateMeshStructure =
			immutableTriangles != (useMesh1 ? lastState.immutableTrianglesMesh1 : lastState.immutableTrianglesMesh2);
#if UNITY_EDITOR
		mustUpdateMeshStructure |= !Application.isPlaying;
#endif

		if (mustUpdateMeshStructure)
			return true;

		// Check if any attachments were enabled/disabled
		// or submesh structures has changed
		ExposedList<int> attachmentsTriangleCountCurrentMesh;
		ExposedList<bool> attachmentsFlipStateCurrentMesh;
		ExposedList<LastState.AddSubmeshArguments> addSubmeshArgumentsCurrentMesh;
		if (useMesh1) {
			attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh1;
			addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh1;
			attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh1;
		} else {
			attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh2;
			addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh2;
			attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh2;
		}

		// Check attachments
		int attachmentCount = attachmentsTriangleCountTemp.Count;
		if (attachmentsTriangleCountCurrentMesh.Count != attachmentCount)
			return true;

		for (int i = 0; i < attachmentCount; i++) {
			if (attachmentsTriangleCountCurrentMesh.Items[i] != attachmentsTriangleCountTemp.Items[i])
				return true;
		}

		// Check flip state
		for (int i = 0; i < attachmentCount; i++) {
			if (attachmentsFlipStateCurrentMesh.Items[i] != attachmentsFlipStateTemp.Items[i])
				return true;
		}

		// Check submeshes
		int submeshCount = addSubmeshArgumentsTemp.Count;
		if (addSubmeshArgumentsCurrentMesh.Count != submeshCount)
			return true;

		for (int i = 0; i < submeshCount; i++) {
			if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref addSubmeshArgumentsTemp.Items[i]))
				return true;
		}

		return false;
	}
		private void ReadVertices (Dictionary<String, Object> map, VertexAttachment attachment, int verticesLength) {
			attachment.WorldVerticesLength = verticesLength;
			float[] vertices = GetFloatArray(map, "vertices", 1);
			float scale = Scale;
			if (verticesLength == vertices.Length) {
				if (scale != 1) {
					for (int i = 0; i < vertices.Length; i++) {
						vertices[i] *= scale;
					}
				}
				attachment.vertices = vertices;
				return;
			}
			ExposedList<float> weights = new ExposedList<float>(verticesLength * 3 * 3);
			ExposedList<int> bones = new ExposedList<int>(verticesLength * 3);
			for (int i = 0, n = vertices.Length; i < n;) {
				int boneCount = (int)vertices[i++];
				bones.Add(boneCount);
				for (int nn = i + boneCount * 4; i < nn; i += 4) {
					bones.Add((int)vertices[i]);
					weights.Add(vertices[i + 1] * this.Scale);
					weights.Add(vertices[i + 2] * this.Scale);
					weights.Add(vertices[i + 3]);
				}
			}
			attachment.bones = bones.ToArray();
			attachment.vertices = weights.ToArray();
		}
		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
			float[] frames = this.frames;
			if (time < frames[0]) {
				if (setupPose) {
					constraint.mix = constraint.data.mix;
					constraint.bendDirection = constraint.data.bendDirection;
				}
				return;
			}

			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
				if (setupPose) {
					constraint.mix = constraint.data.mix + (frames[frames.Length + PREV_MIX] - constraint.data.mix) * alpha;
					constraint.bendDirection = mixingOut ? constraint.data.bendDirection
						: (int)frames[frames.Length + PREV_BEND_DIRECTION];
				} else {
					constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
					if (!mixingOut) constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
				}
				return;
			}

			// Interpolate between the previous frame and the current frame.
			int frame = Animation.BinarySearch(frames, time, ENTRIES);
			float mix = frames[frame + PREV_MIX];
			float frameTime = frames[frame];
			float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));

			if (setupPose) {
				constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
				constraint.bendDirection = mixingOut ? constraint.data.bendDirection : (int)frames[frame + PREV_BEND_DIRECTION];
			} else {
				constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
				if (!mixingOut) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
			}
		}
		/// <summary>Fires events for frames &gt; lastTime and &lt;= time.</summary>
		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			if (firedEvents == null) return;
			float[] frames = this.frames;
			int frameCount = frames.Length;

			if (lastTime > time) { // Fire events after last time for looped animations.
				Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha, setupPose, mixingOut);
				lastTime = -1f;
			} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
				return;
			if (time < frames[0]) return; // Time is before first frame.

			int frame;
			if (lastTime < frames[0])
				frame = 0;
			else {
				frame = Animation.BinarySearch(frames, lastTime);
				float frameTime = frames[frame];
				while (frame > 0) { // Fire multiple events with the same frame.
					if (frames[frame - 1] != frameTime) break;
					frame--;
				}
			}
			for (; frame < frameCount && time >= frames[frame]; frame++)
				firedEvents.Add(events[frame]);
		}
    public virtual void LateUpdate()
    {
        if (!valid)
        {
            return;
        }

        // Exit early if there is nothing to render
        if (!meshRenderer.enabled && submeshRenderers.Length == 0)
        {
            return;
        }

        // Count vertices and submesh triangles.
        int                vertexCount = 0;
        int                submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
        Material           lastMaterial               = null;
        ExposedList <Slot> drawOrder                  = skeleton.drawOrder;
        int                drawOrderCount             = drawOrder.Count;
        int                submeshSeparatorSlotsCount = submeshSeparatorSlots.Count;
        bool               renderMeshes               = this.renderMeshes;

        // Clear last state of attachments and submeshes
        ExposedList <int> attachmentsTriangleCountTemp = lastState.attachmentsTriangleCountTemp;

        attachmentsTriangleCountTemp.GrowIfNeeded(drawOrderCount);
        attachmentsTriangleCountTemp.Count = drawOrderCount;
        ExposedList <bool> attachmentsFlipStateTemp = lastState.attachmentsFlipStateTemp;

        attachmentsFlipStateTemp.GrowIfNeeded(drawOrderCount);
        attachmentsFlipStateTemp.Count = drawOrderCount;

        ExposedList <LastState.AddSubmeshArguments> addSubmeshArgumentsTemp = lastState.addSubmeshArgumentsTemp;

        addSubmeshArgumentsTemp.Clear(false);
        for (int i = 0; i < drawOrderCount; i++)
        {
            Slot       slot       = drawOrder.Items[i];
            Bone       bone       = slot.bone;
            Attachment attachment = slot.attachment;

            object rendererObject;
            int    attachmentVertexCount, attachmentTriangleCount;
            bool   worldScaleXIsPositive = bone.worldScaleX >= 0f;
            bool   worldScaleYIsPositive = bone.worldScaleY >= 0f;
            bool   worldScaleIsSameSigns = (worldScaleXIsPositive && worldScaleYIsPositive) ||
                                           (!worldScaleXIsPositive && !worldScaleYIsPositive);
            bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns);
            attachmentsFlipStateTemp.Items[i] = flip;

            attachmentsTriangleCountTemp.Items[i] = -1;
            RegionAttachment regionAttachment = attachment as RegionAttachment;
            if (regionAttachment != null)
            {
                rendererObject          = regionAttachment.RendererObject;
                attachmentVertexCount   = 4;
                attachmentTriangleCount = 6;
            }
            else
            {
                if (!renderMeshes)
                {
                    continue;
                }
                MeshAttachment meshAttachment = attachment as MeshAttachment;
                if (meshAttachment != null)
                {
                    rendererObject          = meshAttachment.RendererObject;
                    attachmentVertexCount   = meshAttachment.vertices.Length >> 1;
                    attachmentTriangleCount = meshAttachment.triangles.Length;
                }
                else
                {
                    SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
                    if (skinnedMeshAttachment != null)
                    {
                        rendererObject          = skinnedMeshAttachment.RendererObject;
                        attachmentVertexCount   = skinnedMeshAttachment.uvs.Length >> 1;
                        attachmentTriangleCount = skinnedMeshAttachment.triangles.Length;
                    }
                    else
                    {
                        continue;
                    }
                }
            }

            // Populate submesh when material changes.
#if !SPINE_TK2D
            Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
#else
            Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
#endif
            if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) ||
                (submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot)))
            {
                addSubmeshArgumentsTemp.Add(
                    new LastState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false)
                    );
                submeshTriangleCount  = 0;
                submeshFirstVertex    = vertexCount;
                submeshStartSlotIndex = i;
            }
            lastMaterial = material;

            submeshTriangleCount += attachmentTriangleCount;
            vertexCount          += attachmentVertexCount;

            attachmentsTriangleCountTemp.Items[i] = attachmentTriangleCount;
        }
        addSubmeshArgumentsTemp.Add(
            new LastState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true)
            );

        bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(attachmentsTriangleCountTemp, attachmentsFlipStateTemp, addSubmeshArgumentsTemp);
        if (mustUpdateMeshStructure)
        {
            submeshMaterials.Clear();
            for (int i = 0, n = addSubmeshArgumentsTemp.Count; i < n; i++)
            {
                LastState.AddSubmeshArguments arguments = addSubmeshArgumentsTemp.Items[i];
                AddSubmesh(
                    arguments.material,
                    arguments.startSlot,
                    arguments.endSlot,
                    arguments.triangleCount,
                    arguments.firstVertex,
                    arguments.lastSubmesh,
                    attachmentsFlipStateTemp
                    );
            }

            // Set materials.
            if (submeshMaterials.Count == sharedMaterials.Length)
            {
                submeshMaterials.CopyTo(sharedMaterials);
            }
            else
            {
                sharedMaterials = submeshMaterials.ToArray();
            }

            if (IsSharedMaterials)
            {
                meshRenderer.sharedMaterials = sharedMaterials;
            }
            else
            {
                meshRenderer.materials = sharedMaterials;
            }
        }

        // Ensure mesh data is the right size.
        Vector3[] vertices     = this.vertices;
        bool      newTriangles = vertexCount > vertices.Length;
        if (newTriangles)
        {
            // Not enough vertices, increase size.
            this.vertices = vertices = new Vector3[vertexCount];
            this.colors   = new Color32[vertexCount];
            this.uvs      = new Vector2[vertexCount];
            mesh1.Clear();
            mesh2.Clear();
        }
        else
        {
            // Too many vertices, zero the extra.
            Vector3 zero = Vector3.zero;
            for (int i = vertexCount, n = lastState.vertexCount; i < n; i++)
            {
                vertices[i] = zero;
            }
        }
        lastState.vertexCount = vertexCount;

        // Setup mesh.
        float     zSpacing = this.zSpacing;
        float[]   tempVertices = this.tempVertices;
        Vector2[] uvs = this.uvs;
        Color32[] colors = this.colors;
        int       vertexIndex = 0;
        Color32   color = new Color32();
        float     a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;

        Vector3 meshBoundsMin;
        Vector3 meshBoundsMax;
        if (vertexCount == 0)
        {
            meshBoundsMin = new Vector3(0, 0, 0);
            meshBoundsMax = new Vector3(0, 0, 0);
        }
        else
        {
            meshBoundsMin.x = int.MaxValue;
            meshBoundsMin.y = int.MaxValue;
            meshBoundsMax.x = int.MinValue;
            meshBoundsMax.y = int.MinValue;
            if (zSpacing > 0f)
            {
                meshBoundsMin.z = 0f;
                meshBoundsMax.z = zSpacing * (drawOrderCount - 1);
            }
            else
            {
                meshBoundsMin.z = zSpacing * (drawOrderCount - 1);
                meshBoundsMax.z = 0f;
            }
            int i = 0;
            do
            {
                Slot             slot             = drawOrder.Items[i];
                Attachment       attachment       = slot.attachment;
                RegionAttachment regionAttachment = attachment as RegionAttachment;
                if (regionAttachment != null)
                {
                    regionAttachment.ComputeWorldVertices(slot.bone, tempVertices);

                    float z = i * zSpacing;
                    float x1 = tempVertices[RegionAttachment.X1], y1 = tempVertices[RegionAttachment.Y1];
                    float x2 = tempVertices[RegionAttachment.X2], y2 = tempVertices[RegionAttachment.Y2];
                    float x3 = tempVertices[RegionAttachment.X3], y3 = tempVertices[RegionAttachment.Y3];
                    float x4 = tempVertices[RegionAttachment.X4], y4 = tempVertices[RegionAttachment.Y4];
                    vertices[vertexIndex].x     = x1;
                    vertices[vertexIndex].y     = y1;
                    vertices[vertexIndex].z     = z;
                    vertices[vertexIndex + 1].x = x4;
                    vertices[vertexIndex + 1].y = y4;
                    vertices[vertexIndex + 1].z = z;
                    vertices[vertexIndex + 2].x = x2;
                    vertices[vertexIndex + 2].y = y2;
                    vertices[vertexIndex + 2].z = z;
                    vertices[vertexIndex + 3].x = x3;
                    vertices[vertexIndex + 3].y = y3;
                    vertices[vertexIndex + 3].z = z;

                    color.a = (byte)(a * slot.a * regionAttachment.a);
                    color.r = (byte)(r * slot.r * regionAttachment.r * color.a);
                    color.g = (byte)(g * slot.g * regionAttachment.g * color.a);
                    color.b = (byte)(b * slot.b * regionAttachment.b * color.a);
                    if (slot.data.blendMode == BlendMode.additive)
                    {
                        color.a = 0;
                    }
                    colors[vertexIndex]     = color;
                    colors[vertexIndex + 1] = color;
                    colors[vertexIndex + 2] = color;
                    colors[vertexIndex + 3] = color;

                    float[] regionUVs = regionAttachment.uvs;
                    uvs[vertexIndex].x     = regionUVs[RegionAttachment.X1];
                    uvs[vertexIndex].y     = regionUVs[RegionAttachment.Y1];
                    uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4];
                    uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4];
                    uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2];
                    uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2];
                    uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3];
                    uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3];

                    // Calculate min/max X
                    if (x1 < meshBoundsMin.x)
                    {
                        meshBoundsMin.x = x1;
                    }
                    else if (x1 > meshBoundsMax.x)
                    {
                        meshBoundsMax.x = x1;
                    }
                    if (x2 < meshBoundsMin.x)
                    {
                        meshBoundsMin.x = x2;
                    }
                    else if (x2 > meshBoundsMax.x)
                    {
                        meshBoundsMax.x = x2;
                    }
                    if (x3 < meshBoundsMin.x)
                    {
                        meshBoundsMin.x = x3;
                    }
                    else if (x3 > meshBoundsMax.x)
                    {
                        meshBoundsMax.x = x3;
                    }
                    if (x4 < meshBoundsMin.x)
                    {
                        meshBoundsMin.x = x4;
                    }
                    else if (x4 > meshBoundsMax.x)
                    {
                        meshBoundsMax.x = x4;
                    }

                    // Calculate min/max Y
                    if (y1 < meshBoundsMin.y)
                    {
                        meshBoundsMin.y = y1;
                    }
                    else if (y1 > meshBoundsMax.y)
                    {
                        meshBoundsMax.y = y1;
                    }
                    if (y2 < meshBoundsMin.y)
                    {
                        meshBoundsMin.y = y2;
                    }
                    else if (y2 > meshBoundsMax.y)
                    {
                        meshBoundsMax.y = y2;
                    }
                    if (y3 < meshBoundsMin.y)
                    {
                        meshBoundsMin.y = y3;
                    }
                    else if (y3 > meshBoundsMax.y)
                    {
                        meshBoundsMax.y = y3;
                    }
                    if (y4 < meshBoundsMin.y)
                    {
                        meshBoundsMin.y = y4;
                    }
                    else if (y4 > meshBoundsMax.y)
                    {
                        meshBoundsMax.y = y4;
                    }

                    vertexIndex += 4;
                }
                else
                {
                    if (!renderMeshes)
                    {
                        continue;
                    }
                    MeshAttachment meshAttachment = attachment as MeshAttachment;
                    if (meshAttachment != null)
                    {
                        int meshVertexCount = meshAttachment.vertices.Length;
                        if (tempVertices.Length < meshVertexCount)
                        {
                            this.tempVertices = tempVertices = new float[meshVertexCount];
                        }
                        meshAttachment.ComputeWorldVertices(slot, tempVertices);

                        color.a = (byte)(a * slot.a * meshAttachment.a);
                        color.r = (byte)(r * slot.r * meshAttachment.r * color.a);
                        color.g = (byte)(g * slot.g * meshAttachment.g * color.a);
                        color.b = (byte)(b * slot.b * meshAttachment.b * color.a);
                        if (slot.data.blendMode == BlendMode.additive)
                        {
                            color.a = 0;
                        }

                        float[] meshUVs = meshAttachment.uvs;
                        float   z       = i * zSpacing;
                        for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++)
                        {
                            float x = tempVertices[ii], y = tempVertices[ii + 1];
                            vertices[vertexIndex].x = x;
                            vertices[vertexIndex].y = y;
                            vertices[vertexIndex].z = z;
                            colors[vertexIndex]     = color;
                            uvs[vertexIndex].x      = meshUVs[ii];
                            uvs[vertexIndex].y      = meshUVs[ii + 1];

                            if (x < meshBoundsMin.x)
                            {
                                meshBoundsMin.x = x;
                            }
                            else if (x > meshBoundsMax.x)
                            {
                                meshBoundsMax.x = x;
                            }
                            if (y < meshBoundsMin.y)
                            {
                                meshBoundsMin.y = y;
                            }
                            else if (y > meshBoundsMax.y)
                            {
                                meshBoundsMax.y = y;
                            }
                        }
                    }
                    else
                    {
                        SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
                        if (skinnedMeshAttachment != null)
                        {
                            int meshVertexCount = skinnedMeshAttachment.uvs.Length;
                            if (tempVertices.Length < meshVertexCount)
                            {
                                this.tempVertices = tempVertices = new float[meshVertexCount];
                            }
                            skinnedMeshAttachment.ComputeWorldVertices(slot, tempVertices);

                            color.a = (byte)(a * slot.a * skinnedMeshAttachment.a);
                            color.r = (byte)(r * slot.r * skinnedMeshAttachment.r * color.a);
                            color.g = (byte)(g * slot.g * skinnedMeshAttachment.g * color.a);
                            color.b = (byte)(b * slot.b * skinnedMeshAttachment.b * color.a);
                            if (slot.data.blendMode == BlendMode.additive)
                            {
                                color.a = 0;
                            }

                            float[] meshUVs = skinnedMeshAttachment.uvs;
                            float   z       = i * zSpacing;
                            for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++)
                            {
                                float x = tempVertices[ii], y = tempVertices[ii + 1];
                                vertices[vertexIndex].x = x;
                                vertices[vertexIndex].y = y;
                                vertices[vertexIndex].z = z;
                                colors[vertexIndex]     = color;
                                uvs[vertexIndex].x      = meshUVs[ii];
                                uvs[vertexIndex].y      = meshUVs[ii + 1];

                                if (x < meshBoundsMin.x)
                                {
                                    meshBoundsMin.x = x;
                                }
                                else if (x > meshBoundsMax.x)
                                {
                                    meshBoundsMax.x = x;
                                }
                                if (y < meshBoundsMin.y)
                                {
                                    meshBoundsMin.y = y;
                                }
                                else if (y > meshBoundsMax.y)
                                {
                                    meshBoundsMax.y = y;
                                }
                            }
                        }
                    }
                }
            } while (++i < drawOrderCount);
        }

        // Double buffer mesh.
        Mesh mesh = useMesh1 ? mesh1 : mesh2;
        meshFilter.sharedMesh = mesh;

        mesh.vertices = vertices;
        mesh.colors32 = colors;
        mesh.uv       = uvs;

        if (mustUpdateMeshStructure)
        {
            int submeshCount = submeshMaterials.Count;
            mesh.subMeshCount = submeshCount;
            for (int i = 0; i < submeshCount; ++i)
            {
                mesh.SetTriangles(submeshes.Items[i].triangles, i);
            }
        }

        Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin;
        Vector3 meshBoundsCenter  = meshBoundsMin + meshBoundsExtents * 0.5f;
        mesh.bounds = new Bounds(meshBoundsCenter, meshBoundsExtents);

        if (newTriangles && calculateNormals)
        {
            Vector3[] normals = new Vector3[vertexCount];
            Vector3   normal  = new Vector3(0, 0, -1);
            for (int i = 0; i < vertexCount; i++)
            {
                normals[i] = normal;
            }
            (useMesh1 ? mesh2 : mesh1).vertices = vertices;             // Set other mesh vertices.
            mesh1.normals = normals;
            mesh2.normals = normals;

            if (calculateTangents)
            {
                Vector4[] tangents = new Vector4[vertexCount];
                Vector3   tangent  = new Vector3(0, 0, 1);
                for (int i = 0; i < vertexCount; i++)
                {
                    tangents[i] = tangent;
                }
                mesh1.tangents = tangents;
                mesh2.tangents = tangents;
            }
        }

        // Update previous state
        ExposedList <int>  attachmentsTriangleCountCurrentMesh;
        ExposedList <bool> attachmentsFlipStateCurrentMesh;
        ExposedList <LastState.AddSubmeshArguments> addSubmeshArgumentsCurrentMesh;
        if (useMesh1)
        {
            attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh1;
            addSubmeshArgumentsCurrentMesh      = lastState.addSubmeshArgumentsMesh1;
            attachmentsFlipStateCurrentMesh     = lastState.attachmentsFlipStateMesh1;
            lastState.immutableTrianglesMesh1   = immutableTriangles;
        }
        else
        {
            attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh2;
            addSubmeshArgumentsCurrentMesh      = lastState.addSubmeshArgumentsMesh2;
            attachmentsFlipStateCurrentMesh     = lastState.attachmentsFlipStateMesh2;
            lastState.immutableTrianglesMesh2   = immutableTriangles;
        }

        attachmentsTriangleCountCurrentMesh.GrowIfNeeded(attachmentsTriangleCountTemp.Capacity);
        attachmentsTriangleCountCurrentMesh.Count = attachmentsTriangleCountTemp.Count;
        attachmentsTriangleCountTemp.CopyTo(attachmentsTriangleCountCurrentMesh.Items, 0);

        attachmentsFlipStateCurrentMesh.GrowIfNeeded(attachmentsFlipStateTemp.Capacity);
        attachmentsFlipStateCurrentMesh.Count = attachmentsFlipStateTemp.Count;
        attachmentsFlipStateTemp.CopyTo(attachmentsFlipStateCurrentMesh.Items, 0);

        addSubmeshArgumentsCurrentMesh.GrowIfNeeded(addSubmeshArgumentsTemp.Count);
        addSubmeshArgumentsCurrentMesh.Count = addSubmeshArgumentsTemp.Count;
        addSubmeshArgumentsTemp.CopyTo(addSubmeshArgumentsCurrentMesh.Items);

        if (submeshRenderers.Length > 0)
        {
            for (int i = 0; i < submeshRenderers.Length; i++)
            {
                SkeletonUtilitySubmeshRenderer submeshRenderer = submeshRenderers[i];
                if (submeshRenderer.submeshIndex < sharedMaterials.Length)
                {
                    submeshRenderer.SetMesh(meshRenderer, useMesh1 ? mesh1 : mesh2, sharedMaterials[submeshRenderer.submeshIndex]);
                }
                else
                {
                    submeshRenderer.GetComponent <Renderer>().enabled = false;
                }
            }
        }

        useMesh1 = !useMesh1;
    }
		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			Slot slot = skeleton.slots.Items[slotIndex];
			float[] frames = this.frames;
			if (time < frames[0]) {
				if (setupPose) {
					var slotData = slot.data;
					slot.r = slotData.r;
					slot.g = slotData.g;
					slot.b = slotData.b;
					slot.a = slotData.a;
				}
				return;
			}

			float r, g, b, a;
			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
				int i = frames.Length;
				r = frames[i + PREV_R];
				g = frames[i + PREV_G];
				b = frames[i + PREV_B];
				a = frames[i + PREV_A];
			} else {
				// Interpolate between the previous frame and the current frame.
				int frame = Animation.BinarySearch(frames, time, ENTRIES);
				r = frames[frame + PREV_R];
				g = frames[frame + PREV_G];
				b = frames[frame + PREV_B];
				a = frames[frame + PREV_A];
				float frameTime = frames[frame];
				float percent = GetCurvePercent(frame / ENTRIES - 1,
					1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));

				r += (frames[frame + R] - r) * percent;
				g += (frames[frame + G] - g) * percent;
				b += (frames[frame + B] - b) * percent;
				a += (frames[frame + A] - a) * percent;
			}
			if (alpha == 1) {
				slot.r = r;
				slot.g = g;
				slot.b = b;
				slot.a = a;
			} else {
				float br, bg, bb, ba;
				if (setupPose) {
					br = slot.data.r;
					bg = slot.data.g;
					bb = slot.data.b;
					ba = slot.data.a;
				} else {
					br = slot.r;
					bg = slot.g;
					bb = slot.b;
					ba = slot.a;
				}
				slot.r = br + ((r - br) * alpha);
				slot.g = bg + ((g - bg) * alpha);
				slot.b = bb + ((b - bb) * alpha);
				slot.a = ba + ((a - ba) * alpha);
			}
		}
Example #14
0
        public override void Apply(Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha)
        {
            float[] frames = this.frames;
            if (time < frames[0]) return; // Time is before first frame.

            Bone bone = skeleton.bones.Items[boneIndex];

            if (time >= frames[frames.Length - 3]) { // Time is after last frame.
                bone.x += (bone.data.x + frames[frames.Length - 2] - bone.x) * alpha;
                bone.y += (bone.data.y + frames[frames.Length - 1] - bone.y) * alpha;
                return;
            }

            // Interpolate between the previous frame and the current frame.
            int frameIndex = Animation.binarySearch(frames, time, 3);
            float prevFrameX = frames[frameIndex - 2];
            float prevFrameY = frames[frameIndex - 1];
            float frameTime = frames[frameIndex];
            float percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime);
            percent = GetCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));

            bone.x += (bone.data.x + prevFrameX + (frames[frameIndex + FRAME_X] - prevFrameX) * percent - bone.x) * alpha;
            bone.y += (bone.data.y + prevFrameY + (frames[frameIndex + FRAME_Y] - prevFrameY) * percent - bone.y) * alpha;
        }
    /** Stores vertices and triangles for a single material. */
    private void AddSubmesh(Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh, ExposedList <bool> flipStates)
    {
        int submeshIndex = submeshMaterials.Count;

        submeshMaterials.Add(material);

        if (submeshes.Count <= submeshIndex)
        {
            submeshes.Add(new Submesh());
        }
        else if (immutableTriangles)
        {
            return;
        }

        Submesh submesh = submeshes.Items[submeshIndex];

        int[] triangles         = submesh.triangles;
        int   trianglesCapacity = triangles.Length;

        if (lastSubmesh && trianglesCapacity > triangleCount)
        {
            // Last submesh may have more triangles than required, so zero triangles to the end.
            for (int i = triangleCount; i < trianglesCapacity; i++)
            {
                triangles[i] = 0;
            }
            submesh.triangleCount = triangleCount;
        }
        else if (trianglesCapacity != triangleCount)
        {
            // Reallocate triangles when not the exact size needed.
            submesh.triangles     = triangles = new int[triangleCount];
            submesh.triangleCount = 0;
        }

        if (!renderMeshes && !frontFacing)
        {
            // Use stored triangles if possible.
            if (submesh.firstVertex != firstVertex || submesh.triangleCount < triangleCount)
            {
                submesh.triangleCount = triangleCount;
                submesh.firstVertex   = firstVertex;
                int drawOrderIndex = 0;
                for (int i = 0; i < triangleCount; i += 6, firstVertex += 4, drawOrderIndex++)
                {
                    triangles[i]     = firstVertex;
                    triangles[i + 1] = firstVertex + 2;
                    triangles[i + 2] = firstVertex + 1;
                    triangles[i + 3] = firstVertex + 2;
                    triangles[i + 4] = firstVertex + 3;
                    triangles[i + 5] = firstVertex + 1;
                }
            }
            return;
        }

        // Store triangles.
        ExposedList <Slot> drawOrder = skeleton.DrawOrder;

        for (int i = startSlot, triangleIndex = 0; i < endSlot; i++)
        {
            Slot       slot       = drawOrder.Items[i];
            Attachment attachment = slot.attachment;

            bool flip = flipStates.Items[i];

            if (attachment is RegionAttachment)
            {
                if (!flip)
                {
                    triangles[triangleIndex]     = firstVertex;
                    triangles[triangleIndex + 1] = firstVertex + 2;
                    triangles[triangleIndex + 2] = firstVertex + 1;
                    triangles[triangleIndex + 3] = firstVertex + 2;
                    triangles[triangleIndex + 4] = firstVertex + 3;
                    triangles[triangleIndex + 5] = firstVertex + 1;
                }
                else
                {
                    triangles[triangleIndex]     = firstVertex + 1;
                    triangles[triangleIndex + 1] = firstVertex + 2;
                    triangles[triangleIndex + 2] = firstVertex;
                    triangles[triangleIndex + 3] = firstVertex + 1;
                    triangles[triangleIndex + 4] = firstVertex + 3;
                    triangles[triangleIndex + 5] = firstVertex + 2;
                }

                triangleIndex += 6;
                firstVertex   += 4;
                continue;
            }
            int[]          attachmentTriangles;
            int            attachmentVertexCount;
            MeshAttachment meshAttachment = attachment as MeshAttachment;
            if (meshAttachment != null)
            {
                attachmentVertexCount = meshAttachment.vertices.Length >> 1;
                attachmentTriangles   = meshAttachment.triangles;
            }
            else
            {
                SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
                if (skinnedMeshAttachment != null)
                {
                    attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
                    attachmentTriangles   = skinnedMeshAttachment.triangles;
                }
                else
                {
                    continue;
                }
            }

            if (flip)
            {
                for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii += 3, triangleIndex += 3)
                {
                    triangles[triangleIndex + 2] = firstVertex + attachmentTriangles[ii];
                    triangles[triangleIndex + 1] = firstVertex + attachmentTriangles[ii + 1];
                    triangles[triangleIndex]     = firstVertex + attachmentTriangles[ii + 2];
                }
            }
            else
            {
                for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
                {
                    triangles[triangleIndex] = firstVertex + attachmentTriangles[ii];
                }
            }

            firstVertex += attachmentVertexCount;
        }
    }
    public void LoadContent(ContentManager contentManager)
    {
        skeletonRenderer = new SkeletonRenderer(App.graphicsDevice);
        skeletonRenderer.PremultipliedAlpha = App.globalValues.Alpha;

        atlas = new Atlas(App.globalValues.SelectAtlasFile, new XnaTextureLoader(App.graphicsDevice));

        if (Common.IsBinaryData(App.globalValues.SelectSpineFile))
        {
            binary       = new SkeletonBinary(atlas);
            binary.Scale = App.globalValues.Scale;
            skeletonData = binary.ReadSkeletonData(App.globalValues.SelectSpineFile);
        }
        else
        {
            json         = new SkeletonJson(atlas);
            json.Scale   = App.globalValues.Scale;
            skeletonData = json.ReadSkeletonData(App.globalValues.SelectSpineFile);
        }
        App.globalValues.SpineVersion = skeletonData.Version;
        skeleton = new Skeleton(skeletonData);

        Common.SetInitLocation(skeleton.Data.Height);
        App.globalValues.FileHash = skeleton.Data.Hash;

        stateData = new AnimationStateData(skeleton.Data);

        state = new AnimationState(stateData);

        List <string> AnimationNames = new List <string>();

        listAnimation = state.Data.skeletonData.Animations;
        foreach (Animation An in listAnimation)
        {
            AnimationNames.Add(An.name);
        }
        App.globalValues.AnimeList = AnimationNames;

        List <string> SkinNames = new List <string>();

        listSkin = state.Data.skeletonData.Skins;
        foreach (Skin Sk in listSkin)
        {
            SkinNames.Add(Sk.name);
        }
        App.globalValues.SkinList = SkinNames;

        if (App.globalValues.SelectAnimeName != "")
        {
            state.SetAnimation(0, App.globalValues.SelectAnimeName, App.globalValues.IsLoop);
        }
        else
        {
            state.SetAnimation(0, state.Data.skeletonData.animations.Items[0].name, App.globalValues.IsLoop);
        }

        if (App.isNew)
        {
            MainWindow.SetCBAnimeName();
        }
        App.isNew = false;
    }
Example #17
0
        public void CollectBones()
        {
            if (skeletonRenderer.skeleton == null)
            {
                return;
            }

            if (boneRoot != null)
            {
                List <string> constraintTargetNames = new List <string>();

                ExposedList <IkConstraint> ikConstraints = skeletonRenderer.skeleton.IkConstraints;
                for (int i = 0, n = ikConstraints.Count; i < n; i++)
                {
                    constraintTargetNames.Add(ikConstraints.Items[i].Target.Data.Name);
                }

                var utilityBones = this.utilityBones;
                for (int i = 0, n = utilityBones.Count; i < n; i++)
                {
                    var b = utilityBones[i];
                    if (b.bone == null)
                    {
                        return;
                    }
                    if (b.mode == SkeletonUtilityBone.Mode.Override)
                    {
                        hasTransformBones = true;
                    }

                    if (constraintTargetNames.Contains(b.bone.Data.Name))
                    {
                        hasUtilityConstraints = true;
                    }
                }

                if (utilityConstraints.Count > 0)
                {
                    hasUtilityConstraints = true;
                }

                if (skeletonAnimation != null)
                {
                    skeletonAnimation.UpdateWorld    -= UpdateWorld;
                    skeletonAnimation.UpdateComplete -= UpdateComplete;

                    if (hasTransformBones || hasUtilityConstraints)
                    {
                        skeletonAnimation.UpdateWorld += UpdateWorld;
                    }

                    if (hasUtilityConstraints)
                    {
                        skeletonAnimation.UpdateComplete += UpdateComplete;
                    }
                }

                needToReprocessBones = false;
            }
            else
            {
                utilityBones.Clear();
                utilityConstraints.Clear();
            }
        }
Example #18
0
        public MeshAndMaterials GenerateMesh(ExposedList <SubmeshInstruction> instructions, int startSubmesh, int endSubmesh)
        {
            // STEP 0: Prepare instructions.
            var paramItems = instructions.Items;

            currentInstructions.Clear(false);
            for (int i = startSubmesh, n = endSubmesh; i < n; i++)
            {
                this.currentInstructions.Add(paramItems[i]);
            }
            var smartMesh                = doubleBufferedSmartMesh.GetNext();
            var mesh                     = smartMesh.mesh;
            int submeshCount             = currentInstructions.Count;
            var currentInstructionsItems = currentInstructions.Items;
            int vertexCount              = 0;

            for (int i = 0; i < submeshCount; i++)
            {
                currentInstructionsItems[i].firstVertexIndex = vertexCount;             // Ensure current instructions have correct cached values.
                vertexCount += currentInstructionsItems[i].vertexCount;                 // vertexCount will also be used for the rest of this method.
            }

            // STEP 1: Ensure correct buffer sizes.
            bool vertBufferResized     = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
            bool submeshBuffersResized = ArraysMeshGenerator.EnsureTriangleBuffersSize(submeshBuffers, submeshCount, currentInstructionsItems);

            // STEP 2: Update buffers based on Skeleton.

            // Initial values for manual Mesh Bounds calculation
            Vector3 meshBoundsMin;
            Vector3 meshBoundsMax;
            float   zSpacing = this.ZSpacing;

            if (vertexCount <= 0)
            {
                meshBoundsMin = new Vector3(0, 0, 0);
                meshBoundsMax = new Vector3(0, 0, 0);
            }
            else
            {
                meshBoundsMin.x = int.MaxValue;
                meshBoundsMin.y = int.MaxValue;
                meshBoundsMax.x = int.MinValue;
                meshBoundsMax.y = int.MinValue;

                int endSlot = currentInstructionsItems[submeshCount - 1].endSlot;
                if (zSpacing > 0f)
                {
                    meshBoundsMin.z = 0f;
                    meshBoundsMax.z = zSpacing * endSlot;
                }
                else
                {
                    meshBoundsMin.z = zSpacing * endSlot;
                    meshBoundsMax.z = 0f;
                }
            }

            // For each submesh, add vertex data From attachments.
            var workingAttachments = this.attachmentBuffer;

            workingAttachments.Clear(false);
            int vertexIndex = 0;             // modified by FillVerts

            for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++)
            {
                var currentInstruction     = currentInstructionsItems[submeshIndex];
                int startSlot              = currentInstruction.startSlot;
                int endSlot                = currentInstruction.endSlot;
                var skeleton               = currentInstruction.skeleton;
                var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
                for (int i = startSlot; i < endSlot; i++)
                {
                    var ca = skeletonDrawOrderItems[i].attachment;
                    if (ca != null)
                    {
                        workingAttachments.Add(ca);                                 // Includes BoundingBoxes. This is ok.
                    }
                }
                ArraysMeshGenerator.FillVerts(skeleton, startSlot, endSlot, zSpacing, this.PremultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
            }

            bool structureDoesntMatch = vertBufferResized || submeshBuffersResized || smartMesh.StructureDoesntMatch(workingAttachments, currentInstructions);

            for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++)
            {
                var currentInstruction = currentInstructionsItems[submeshIndex];
                if (structureDoesntMatch)
                {
                    var  currentBuffer = submeshBuffers.Items[submeshIndex];
                    bool isLastSubmesh = (submeshIndex == submeshCount - 1);
                    ArraysMeshGenerator.FillTriangles(ref currentBuffer.triangles, currentInstruction.skeleton, currentInstruction.triangleCount, currentInstruction.firstVertexIndex, currentInstruction.startSlot, currentInstruction.endSlot, isLastSubmesh);
                    currentBuffer.triangleCount = currentInstruction.triangleCount;
                    currentBuffer.firstVertex   = currentInstruction.firstVertexIndex;
                }
            }

            if (structureDoesntMatch)
            {
                mesh.Clear();
                this.sharedMaterials = currentInstructions.GetUpdatedMaterialArray(this.sharedMaterials);
            }

            // STEP 3: Assign the buffers into the Mesh.
            smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, workingAttachments, currentInstructions);
            mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);


            if (structureDoesntMatch)
            {
                // Push new triangles if doesn't match.
                mesh.subMeshCount = submeshCount;
                for (int i = 0; i < submeshCount; i++)
                {
                    mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);
                }

                this.TryAddNormalsTo(mesh, vertexCount);
            }

            if (addTangents)
            {
                SolveTangents2DEnsureSize(ref this.meshTangents, ref this.tempTanBuffer, vertexCount);

                for (int i = 0, n = submeshCount; i < n; i++)
                {
                    var submesh = submeshBuffers.Items[i];
                    SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, meshVertices, meshUVs, vertexCount);
                }

                SolveTangents2DBuffer(this.meshTangents, this.tempTanBuffer, vertexCount);
            }

            return(new MeshAndMaterials(smartMesh.mesh, sharedMaterials));
        }
Example #19
0
            public void Set(Vector3[] verts, Vector2[] uvs, Color32[] colors, ExposedList <Attachment> attachments, ExposedList <SubmeshInstruction> instructions)
            {
                mesh.vertices = verts;
                mesh.uv       = uvs;
                mesh.colors32 = colors;

                attachmentsUsed.Clear(false);
                attachmentsUsed.GrowIfNeeded(attachments.Capacity);
                attachmentsUsed.Count = attachments.Count;
                attachments.CopyTo(attachmentsUsed.Items);

                instructionsUsed.Clear(false);
                instructionsUsed.GrowIfNeeded(instructions.Capacity);
                instructionsUsed.Count = instructions.Count;
                instructions.CopyTo(instructionsUsed.Items);
            }
		public static bool EnsureTriangleBuffersSize (ExposedList<SubmeshTriangleBuffer> submeshBuffers, int targetSubmeshCount, SubmeshInstruction[] instructionItems) {
			bool submeshBuffersWasResized = submeshBuffers.Count < targetSubmeshCount;
			if (submeshBuffersWasResized) {
				submeshBuffers.GrowIfNeeded(targetSubmeshCount - submeshBuffers.Count);
				for (int i = submeshBuffers.Count; submeshBuffers.Count < targetSubmeshCount; i++)
					submeshBuffers.Add(new SubmeshTriangleBuffer(instructionItems[i].triangleCount));
			}
			return submeshBuffersWasResized;
		}
            public void Set(Vector3[] verts, Vector2[] uvs, Color32[] colors, ExposedList<Attachment> attachments, ExposedList<SubmeshInstruction> instructions)
            {
                mesh.vertices = verts;
                mesh.uv = uvs;
                mesh.colors32 = colors;

                attachmentsUsed.Clear(false);
                attachmentsUsed.GrowIfNeeded(attachments.Capacity);
                attachmentsUsed.Count = attachments.Count;
                attachments.CopyTo(attachmentsUsed.Items);

                instructionsUsed.Clear(false);
                instructionsUsed.GrowIfNeeded(instructions.Capacity);
                instructionsUsed.Count = instructions.Count;
                instructions.CopyTo(instructionsUsed.Items);
            }
Example #22
0
        void SetSubmesh(int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, ExposedList <bool> flipStates, bool isLastSubmesh)
        {
            var currentSubmesh = submeshes.Items[submeshIndex];

            int[] triangles = currentSubmesh.triangles;

            int triangleCount = submeshInstructions.triangleCount;
            int firstVertex   = submeshInstructions.firstVertexIndex;

            int trianglesCapacity = triangles.Length;

            if (isLastSubmesh && trianglesCapacity > triangleCount)
            {
                // Last submesh may have more triangles than required, so zero triangles to the end.
                for (int i = triangleCount; i < trianglesCapacity; i++)
                {
                    triangles[i] = 0;
                }

                currentSubmesh.triangleCount = triangleCount;
            }
            else if (trianglesCapacity != triangleCount)
            {
                // Reallocate triangles when not the exact size needed.
                currentSubmesh.triangles     = triangles = new int[triangleCount];
                currentSubmesh.triangleCount = 0;
            }

            if (!this.renderMeshes && !this.frontFacing)
            {
                // Use stored triangles if possible.
                if (currentSubmesh.firstVertex != firstVertex || currentSubmesh.triangleCount < triangleCount)                   //|| currentSubmesh.triangleCount == 0
                {
                    currentSubmesh.triangleCount = triangleCount;
                    currentSubmesh.firstVertex   = firstVertex;

                    for (int i = 0; i < triangleCount; i += 6, firstVertex += 4)
                    {
                        triangles[i]     = firstVertex;
                        triangles[i + 1] = firstVertex + 2;
                        triangles[i + 2] = firstVertex + 1;
                        triangles[i + 3] = firstVertex + 2;
                        triangles[i + 4] = firstVertex + 3;
                        triangles[i + 5] = firstVertex + 1;
                    }
                }
                return;
            }

            var flipStatesItems = flipStates.Items;

            // Iterate through all slots and store their triangles.
            var drawOrderItems = skeleton.DrawOrder.Items;
            int triangleIndex  = 0;            // Modified by loop

            for (int i = submeshInstructions.startSlot, n = submeshInstructions.endSlot; i < n; i++)
            {
                Attachment attachment = drawOrderItems[i].attachment;
                bool       flip       = frontFacing && flipStatesItems[i];

                // Add RegionAttachment triangles
                if (attachment is RegionAttachment)
                {
                    if (!flip)
                    {
                        triangles[triangleIndex]     = firstVertex;
                        triangles[triangleIndex + 1] = firstVertex + 2;
                        triangles[triangleIndex + 2] = firstVertex + 1;
                        triangles[triangleIndex + 3] = firstVertex + 2;
                        triangles[triangleIndex + 4] = firstVertex + 3;
                        triangles[triangleIndex + 5] = firstVertex + 1;
                    }
                    else
                    {
                        triangles[triangleIndex]     = firstVertex + 1;
                        triangles[triangleIndex + 1] = firstVertex + 2;
                        triangles[triangleIndex + 2] = firstVertex;
                        triangles[triangleIndex + 3] = firstVertex + 1;
                        triangles[triangleIndex + 4] = firstVertex + 3;
                        triangles[triangleIndex + 5] = firstVertex + 2;
                    }

                    triangleIndex += 6;
                    firstVertex   += 4;
                    continue;
                }

                // Add (Weighted)MeshAttachment triangles
                int[] attachmentTriangles;
                int   attachmentVertexCount;
                var   meshAttachment = attachment as MeshAttachment;
                if (meshAttachment != null)
                {
                    attachmentVertexCount = meshAttachment.worldVerticesLength >> 1;                     // length/2
                    attachmentTriangles   = meshAttachment.triangles;
                }
                else
                {
                    continue;
                }

                if (flip)
                {
                    for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii += 3, triangleIndex += 3)
                    {
                        triangles[triangleIndex + 2] = firstVertex + attachmentTriangles[ii];
                        triangles[triangleIndex + 1] = firstVertex + attachmentTriangles[ii + 1];
                        triangles[triangleIndex]     = firstVertex + attachmentTriangles[ii + 2];
                    }
                }
                else
                {
                    for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
                    {
                        triangles[triangleIndex] = firstVertex + attachmentTriangles[ii];
                    }
                }

                firstVertex += attachmentVertexCount;
            }
        }
            public bool StructureDoesntMatch(ExposedList<Attachment> attachments, ExposedList<SubmeshInstruction> instructions)
            {
                // Check count inequality.
                if (attachments.Count != this.attachmentsUsed.Count) return true;
                if (instructions.Count != this.instructionsUsed.Count) return true;

                // Check each attachment.
                var attachmentsPassed = attachments.Items;
                var myAttachments = this.attachmentsUsed.Items;
                for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
                    if (attachmentsPassed[i] != myAttachments[i]) return true;

                // Check each submesh for equal arrangement.
                var instructionListItems = instructions.Items;
                var myInstructions = this.instructionsUsed.Items;
                for (int i = 0, n = this.instructionsUsed.Count; i < n; i++) {
                    var lhs = instructionListItems[i];
                    var rhs = myInstructions[i];
                    if (
                        lhs.material.GetInstanceID() != rhs.material.GetInstanceID() ||
                        lhs.startSlot != rhs.startSlot ||
                        lhs.endSlot != rhs.endSlot ||
                        lhs.triangleCount != rhs.triangleCount ||
                        lhs.vertexCount != rhs.vertexCount ||
                        lhs.firstVertexIndex != rhs.firstVertexIndex
                    ) return true;
                }

                return false;
            }
		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			string attachmentName;
			Slot slot = skeleton.slots.Items[slotIndex];
			if (mixingOut && setupPose) {
				attachmentName = slot.data.attachmentName;
				slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
				return;
			}

			float[] frames = this.frames;
			if (time < frames[0]) { // Time is before first frame.
				if (setupPose) {
					attachmentName = slot.data.attachmentName;
					slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
				}
				return;
			}

			int frameIndex;
			if (time >= frames[frames.Length - 1]) // Time is after last frame.
				frameIndex = frames.Length - 1;
			else
				frameIndex = Animation.BinarySearch(frames, time, 1) - 1;

			attachmentName = attachmentNames[frameIndex];
			slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
		}
        public MeshAndMaterials GenerateMesh(ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh)
        {
            // STEP 0: Prepare instructions.
            var paramItems = instructions.Items;
            currentInstructions.Clear(false);
            for (int i = startSubmesh, n = endSubmesh; i < n; i++) {
                this.currentInstructions.Add(paramItems[i]);
            }
            var smartMesh = doubleBufferedSmartMesh.GetNext();
            var mesh = smartMesh.mesh;
            int submeshCount = currentInstructions.Count;
            var currentInstructionsItems = currentInstructions.Items;
            int vertexCount = 0;
            for (int i = 0; i < submeshCount; i++) {
                currentInstructionsItems[i].firstVertexIndex = vertexCount;// Ensure current instructions have correct cached values.
                vertexCount += currentInstructionsItems[i].vertexCount; // vertexCount will also be used for the rest of this method.
            }

            // STEP 1: Ensure correct buffer sizes.
            bool vertBufferResized = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
            bool submeshBuffersResized = ArraysMeshGenerator.EnsureTriangleBuffersSize(submeshBuffers, submeshCount, currentInstructionsItems);

            // STEP 2: Update buffers based on Skeleton.

            // Initial values for manual Mesh Bounds calculation
            Vector3 meshBoundsMin;
            Vector3 meshBoundsMax;
            float zSpacing = this.ZSpacing;
            if (vertexCount <= 0) {
                meshBoundsMin = new Vector3(0, 0, 0);
                meshBoundsMax = new Vector3(0, 0, 0);
            } else {
                meshBoundsMin.x = int.MaxValue;
                meshBoundsMin.y = int.MaxValue;
                meshBoundsMax.x = int.MinValue;
                meshBoundsMax.y = int.MinValue;

                int endSlot = currentInstructionsItems[submeshCount - 1].endSlot;
                if (zSpacing > 0f) {
                    meshBoundsMin.z = 0f;
                    meshBoundsMax.z = zSpacing * endSlot;
                } else {
                    meshBoundsMin.z = zSpacing * endSlot;
                    meshBoundsMax.z = 0f;
                }
            }

            // For each submesh, add vertex data from attachments.
            var workingAttachments = this.attachmentBuffer;
            workingAttachments.Clear(false);
            int vertexIndex = 0; // modified by FillVerts
            for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
                var currentInstruction = currentInstructionsItems[submeshIndex];
                int startSlot = currentInstruction.startSlot;
                int endSlot = currentInstruction.endSlot;
                var skeleton = currentInstruction.skeleton;
                var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
                for (int i = startSlot; i < endSlot; i++) {
                    var ca = skeletonDrawOrderItems[i].attachment;
                    if (ca != null) workingAttachments.Add(ca); // Includes BoundingBoxes. This is ok.
                }
                ArraysMeshGenerator.FillVerts(skeleton, startSlot, endSlot, zSpacing, this.PremultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
            }

            bool structureDoesntMatch = vertBufferResized || submeshBuffersResized || smartMesh.StructureDoesntMatch(workingAttachments, currentInstructions);
            for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
                var currentInstruction = currentInstructionsItems[submeshIndex];
                if (structureDoesntMatch) {
                    var currentBuffer = submeshBuffers.Items[submeshIndex];
                    bool isLastSubmesh = (submeshIndex == submeshCount - 1);
                    ArraysMeshGenerator.FillTriangles(ref currentBuffer.triangles, currentInstruction.skeleton, currentInstruction.triangleCount, currentInstruction.firstVertexIndex, currentInstruction.startSlot, currentInstruction.endSlot, isLastSubmesh);
                    currentBuffer.triangleCount = currentInstruction.triangleCount;
                    currentBuffer.firstVertex = currentInstruction.firstVertexIndex;
                }
            }

            if (structureDoesntMatch) {
                mesh.Clear();
                this.sharedMaterials = currentInstructions.GetUpdatedMaterialArray(this.sharedMaterials);
            }

            // STEP 3: Assign the buffers into the Mesh.
            smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, workingAttachments, currentInstructions);
            mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);

            if (structureDoesntMatch) {
                // Push new triangles if doesn't match.
                mesh.subMeshCount = submeshCount;
                for (int i = 0; i < submeshCount; i++)
                    mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);

                this.TryAddNormalsTo(mesh, vertexCount);
            }

            if (addTangents) {
                SolveTangents2DEnsureSize(ref this.meshTangents, ref this.tempTanBuffer, vertexCount);

                for (int i = 0, n = submeshCount; i < n; i++) {
                    var submesh = submeshBuffers.Items[i];
                    SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, meshVertices, meshUVs, vertexCount);
                }

                SolveTangents2DBuffer(this.meshTangents, this.tempTanBuffer, vertexCount);
            }

            return new MeshAndMaterials(smartMesh.mesh, sharedMaterials);
        }
		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			Slot slot = skeleton.slots.Items[slotIndex];
			VertexAttachment slotAttachment = slot.attachment as VertexAttachment;
			if (slotAttachment == null || !slotAttachment.ApplyDeform(attachment)) return;

			var verticesArray = slot.attachmentVertices;
			float[] frames = this.frames;
			if (time < frames[0]) {
				if (setupPose) verticesArray.Clear();
				return;
			}

			float[][] frameVertices = this.frameVertices;
			int vertexCount = frameVertices[0].Length;

			if (verticesArray.Count != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices.
			// verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count.
			if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount;
			verticesArray.Count = vertexCount;
			float[] vertices = verticesArray.Items;

			if (time >= frames[frames.Length - 1]) { // Time is after last frame.
				float[] lastVertices = frameVertices[frames.Length - 1];
				if (alpha == 1) {
					// Vertex positions or deform offsets, no alpha.
					Array.Copy(lastVertices, 0, vertices, 0, vertexCount);
				} else if (setupPose) {
					VertexAttachment vertexAttachment = slotAttachment;
					if (vertexAttachment.bones == null) {
						// Unweighted vertex positions, with alpha.
						float[] setupVertices = vertexAttachment.vertices;
						for (int i = 0; i < vertexCount; i++) {
							float setup = setupVertices[i];
							vertices[i] = setup + (lastVertices[i] - setup) * alpha;
						}
					} else {
						// Weighted deform offsets, with alpha.
						for (int i = 0; i < vertexCount; i++)
							vertices[i] = lastVertices[i] * alpha;
					}
				} else {
					// Vertex positions or deform offsets, with alpha.
					for (int i = 0; i < vertexCount; i++)
						vertices[i] += (lastVertices[i] - vertices[i]) * alpha;
				}
				return;
			}

			// Interpolate between the previous frame and the current frame.
			int frame = Animation.BinarySearch(frames, time);
			float[] prevVertices = frameVertices[frame - 1];
			float[] nextVertices = frameVertices[frame];
			float frameTime = frames[frame];
			float percent = GetCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));

			if (alpha == 1) {
				// Vertex positions or deform offsets, no alpha.
				for (int i = 0; i < vertexCount; i++) {
					float prev = prevVertices[i];
					vertices[i] = prev + (nextVertices[i] - prev) * percent;
				}
			} else if (setupPose) {
				VertexAttachment vertexAttachment = (VertexAttachment)slotAttachment;
				if (vertexAttachment.bones == null) {
					// Unweighted vertex positions, with alpha.
					var setupVertices = vertexAttachment.vertices;
					for (int i = 0; i < vertexCount; i++) {
						float prev = prevVertices[i], setup = setupVertices[i];
						vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
					}
				} else {
					// Weighted deform offsets, with alpha.
					for (int i = 0; i < vertexCount; i++) {
						float prev = prevVertices[i];
						vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
					}
				}
			} else {
				// Vertex positions or deform offsets, with alpha.
				for (int i = 0; i < vertexCount; i++) {
					float prev = prevVertices[i];
					vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha;
				}
			}
		}
		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
			float[] frames = this.frames;
			if (time < frames[0]) {
				if (setupPose) constraint.spacing = constraint.data.spacing;
				return;
			}

			float spacing;
			if (time >= frames[frames.Length - ENTRIES]) // Time is after last frame.
				spacing = frames[frames.Length + PREV_VALUE];
			else {
				// Interpolate between the previous frame and the current frame.
				int frame = Animation.BinarySearch(frames, time, ENTRIES);
				spacing = frames[frame + PREV_VALUE];
				float frameTime = frames[frame];
				float percent = GetCurvePercent(frame / ENTRIES - 1,
					1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));

				spacing += (frames[frame + VALUE] - spacing) * percent;
			}

			if (setupPose)
				constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
			else
				constraint.spacing += (spacing - constraint.spacing) * alpha;
		}
		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			ExposedList<Slot> drawOrder = skeleton.drawOrder;
			ExposedList<Slot> slots = skeleton.slots;
			if (mixingOut && setupPose) {
				Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count);
				return;
			}

			float[] frames = this.frames;
			if (time < frames[0]) {
				if (setupPose) Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count);
				return;
			}

			int frame;
			if (time >= frames[frames.Length - 1]) // Time is after last frame.
				frame = frames.Length - 1;
			else
				frame = Animation.BinarySearch(frames, time) - 1;
			
			int[] drawOrderToSetupIndex = drawOrders[frame];
			if (drawOrderToSetupIndex == null) {
				drawOrder.Clear();
				for (int i = 0, n = slots.Count; i < n; i++)
					drawOrder.Add(slots.Items[i]);
			} else {
				var drawOrderItems = drawOrder.Items;
				var slotsItems = slots.Items;
				for (int i = 0, n = drawOrderToSetupIndex.Length; i < n; i++)
					drawOrderItems[i] = slotsItems[drawOrderToSetupIndex[i]];
			}
		}
		abstract public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut);
		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
			float[] frames = this.frames;
			if (time < frames[0]) {
				if (setupPose) {
					var data = constraint.data;
					constraint.rotateMix = data.rotateMix;
					constraint.translateMix = data.translateMix;
					constraint.scaleMix = data.scaleMix;
					constraint.shearMix = data.shearMix;
				}
				return;
			}

			float rotate, translate, scale, shear;
			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
				int i = frames.Length;
				rotate = frames[i + PREV_ROTATE];
				translate = frames[i + PREV_TRANSLATE];
				scale = frames[i + PREV_SCALE];
				shear = frames[i + PREV_SHEAR];
			} else {
				// Interpolate between the previous frame and the current frame.
				int frame = Animation.BinarySearch(frames, time, ENTRIES);
				rotate = frames[frame + PREV_ROTATE];
				translate = frames[frame + PREV_TRANSLATE];
				scale = frames[frame + PREV_SCALE];
				shear = frames[frame + PREV_SHEAR];
				float frameTime = frames[frame];
				float percent = GetCurvePercent(frame / ENTRIES - 1,
					1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));

				rotate += (frames[frame + ROTATE] - rotate) * percent;
				translate += (frames[frame + TRANSLATE] - translate) * percent;
				scale += (frames[frame + SCALE] - scale) * percent;
				shear += (frames[frame + SHEAR] - shear) * percent;
			}
			if (setupPose) {
				TransformConstraintData data = constraint.data;
				constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha;
				constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha;
				constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha;
				constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha;
			} else {
				constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
				constraint.translateMix += (translate - constraint.translateMix) * alpha;
				constraint.scaleMix += (scale - constraint.scaleMix) * alpha;
				constraint.shearMix += (shear - constraint.shearMix) * alpha;
			}
		}
Example #31
0
		float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangents, bool percentPosition,
			bool percentSpacing) {

			Slot target = this.target;
			float position = this.position;
			float[] spaces = this.spaces.Items, output = this.positions.Resize(spacesCount * 3 + 2).Items, world;
			bool closed = path.Closed;
			int verticesLength = path.WorldVerticesLength, curveCount = verticesLength / 6, prevCurve = NONE;

			float pathLength;
			if (!path.ConstantSpeed) {
				float[] lengths = path.Lengths;
				curveCount -= closed ? 1 : 2;
				pathLength = lengths[curveCount];
				if (percentPosition) position *= pathLength;
				if (percentSpacing) {
					for (int i = 0; i < spacesCount; i++)
						spaces[i] *= pathLength;
				}
				world = this.world.Resize(8).Items;
				for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
					float space = spaces[i];
					position += space;
					float p = position;

					if (closed) {
						p %= pathLength;
						if (p < 0) p += pathLength;
						curve = 0;
					} else if (p < 0) {
						if (prevCurve != BEFORE) {
							prevCurve = BEFORE;
							path.ComputeWorldVertices(target, 2, 4, world, 0);
						}
						AddBeforePosition(p, world, 0, output, o);
						continue;
					} else if (p > pathLength) {
						if (prevCurve != AFTER) {
							prevCurve = AFTER;
							path.ComputeWorldVertices(target, verticesLength - 6, 4, world, 0);
						}
						AddAfterPosition(p - pathLength, world, 0, output, o);
						continue;
					}

					// Determine curve containing position.
					for (;; curve++) {
						float length = lengths[curve];
						if (p > length) continue;
						if (curve == 0)
							p /= length;
						else {
							float prev = lengths[curve - 1];
							p = (p - prev) / (length - prev);
						}
						break;
					}
					if (curve != prevCurve) {
						prevCurve = curve;
						if (closed && curve == curveCount) {
							path.ComputeWorldVertices(target, verticesLength - 4, 4, world, 0);
							path.ComputeWorldVertices(target, 0, 4, world, 4);
						} else
							path.ComputeWorldVertices(target, curve * 6 + 2, 8, world, 0);
					}
					AddCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], output, o,
						tangents || (i > 0 && space == 0));
				}
				return output;
			}

			// World vertices.
			if (closed) {
				verticesLength += 2;
				world = this.world.Resize(verticesLength).Items;
				path.ComputeWorldVertices(target, 2, verticesLength - 4, world, 0);
				path.ComputeWorldVertices(target, 0, 2, world, verticesLength - 4);
				world[verticesLength - 2] = world[0];
				world[verticesLength - 1] = world[1];
			} else {
				curveCount--;
				verticesLength -= 4;
				world = this.world.Resize(verticesLength).Items;
				path.ComputeWorldVertices(target, 2, verticesLength, world, 0);
			}

			// Curve lengths.
			float[] curves = this.curves.Resize(curveCount).Items;
			pathLength = 0;
			float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
			float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy;
			for (int i = 0, w = 2; i < curveCount; i++, w += 6) {
				cx1 = world[w];
				cy1 = world[w + 1];
				cx2 = world[w + 2];
				cy2 = world[w + 3];
				x2 = world[w + 4];
				y2 = world[w + 5];
				tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f;
				tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f;
				dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f;
				dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f;
				ddfx = tmpx * 2 + dddfx;
				ddfy = tmpy * 2 + dddfy;
				dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f;
				dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f;
				pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
				dfx += ddfx;
				dfy += ddfy;
				ddfx += dddfx;
				ddfy += dddfy;
				pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
				dfx += ddfx;
				dfy += ddfy;
				pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
				dfx += ddfx + dddfx;
				dfy += ddfy + dddfy;
				pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
				curves[i] = pathLength;
				x1 = x2;
				y1 = y2;
			}
			if (percentPosition) position *= pathLength;
			if (percentSpacing) {
				for (int i = 0; i < spacesCount; i++)
					spaces[i] *= pathLength;
			}

			float[] segments = this.segments;
			float curveLength = 0;
			for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
				float space = spaces[i];
				position += space;
				float p = position;

				if (closed) {
					p %= pathLength;
					if (p < 0) p += pathLength;
					curve = 0;
				} else if (p < 0) {
					AddBeforePosition(p, world, 0, output, o);
					continue;
				} else if (p > pathLength) {
					AddAfterPosition(p - pathLength, world, verticesLength - 4, output, o);
					continue;
				}

				// Determine curve containing position.
				for (;; curve++) {
					float length = curves[curve];
					if (p > length) continue;
					if (curve == 0)
						p /= length;
					else {
						float prev = curves[curve - 1];
						p = (p - prev) / (length - prev);
					}
					break;
				}

				// Curve segment lengths.
				if (curve != prevCurve) {
					prevCurve = curve;
					int ii = curve * 6;
					x1 = world[ii];
					y1 = world[ii + 1];
					cx1 = world[ii + 2];
					cy1 = world[ii + 3];
					cx2 = world[ii + 4];
					cy2 = world[ii + 5];
					x2 = world[ii + 6];
					y2 = world[ii + 7];
					tmpx = (x1 - cx1 * 2 + cx2) * 0.03f;
					tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
					dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
					dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f;
					ddfx = tmpx * 2 + dddfx;
					ddfy = tmpy * 2 + dddfy;
					dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f;
					dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f;
					curveLength = (float)Math.Sqrt(dfx * dfx + dfy * dfy);
					segments[0] = curveLength;
					for (ii = 1; ii < 8; ii++) {
						dfx += ddfx;
						dfy += ddfy;
						ddfx += dddfx;
						ddfy += dddfy;
						curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
						segments[ii] = curveLength;
					}
					dfx += ddfx;
					dfy += ddfy;
					curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
					segments[8] = curveLength;
					dfx += ddfx + dddfx;
					dfy += ddfy + dddfy;
					curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
					segments[9] = curveLength;
					segment = 0;
				}

				// Weight by segment length.
				p *= curveLength;
				for (;; segment++) {
					float length = segments[segment];
					if (p > length) continue;
					if (segment == 0)
						p /= length;
					else {
						float prev = segments[segment - 1];
						p = segment + (p - prev) / (length - prev);
					}
					break;
				}
				AddCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, output, o, tangents || (i > 0 && space == 0));
			}
			return output;
		}
		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));
		}
Example #33
0
        public virtual void LateUpdate()
        {
            if (!valid)
            {
                return;
            }
            bool flag = this.generateMeshOverride != null;

            if (!meshRenderer.enabled && !flag)
            {
                return;
            }
            SkeletonRendererInstruction      skeletonRendererInstruction = currentInstructions;
            ExposedList <SubmeshInstruction> submeshInstructions         = skeletonRendererInstruction.submeshInstructions;

            MeshRendererBuffers.SmartMesh nextMesh = rendererBuffers.GetNextMesh();
            bool flag2;

            if (singleSubmesh)
            {
                MeshGenerator.GenerateSingleSubmeshInstruction(skeletonRendererInstruction, skeleton, skeletonDataAsset.atlasAssets[0].materials[0]);
                if (customMaterialOverride.Count > 0)
                {
                    MeshGenerator.TryReplaceMaterials(submeshInstructions, customMaterialOverride);
                }
                meshGenerator.settings = new MeshGenerator.Settings
                {
                    pmaVertexColors   = pmaVertexColors,
                    zSpacing          = zSpacing,
                    useClipping       = useClipping,
                    tintBlack         = tintBlack,
                    calculateTangents = calculateTangents,
                    addNormals        = addNormals
                };
                meshGenerator.Begin();
                flag2 = SkeletonRendererInstruction.GeometryNotEqual(skeletonRendererInstruction, nextMesh.instructionUsed);
                if (skeletonRendererInstruction.hasActiveClipping)
                {
                    meshGenerator.AddSubmesh(submeshInstructions.Items[0], flag2);
                }
                else
                {
                    meshGenerator.BuildMeshWithArrays(skeletonRendererInstruction, flag2);
                }
            }
            else
            {
                MeshGenerator.GenerateSkeletonRendererInstruction(skeletonRendererInstruction, skeleton, customSlotMaterials, separatorSlots, flag, immutableTriangles);
                if (customMaterialOverride.Count > 0)
                {
                    MeshGenerator.TryReplaceMaterials(submeshInstructions, customMaterialOverride);
                }
                if (flag)
                {
                    this.generateMeshOverride(skeletonRendererInstruction);
                    if (disableRenderingOnOverride)
                    {
                        return;
                    }
                }
                flag2 = SkeletonRendererInstruction.GeometryNotEqual(skeletonRendererInstruction, nextMesh.instructionUsed);
                meshGenerator.settings = new MeshGenerator.Settings
                {
                    pmaVertexColors   = pmaVertexColors,
                    zSpacing          = zSpacing,
                    useClipping       = useClipping,
                    tintBlack         = tintBlack,
                    calculateTangents = calculateTangents,
                    addNormals        = addNormals
                };
                meshGenerator.Begin();
                if (skeletonRendererInstruction.hasActiveClipping)
                {
                    meshGenerator.BuildMesh(skeletonRendererInstruction, flag2);
                }
                else
                {
                    meshGenerator.BuildMeshWithArrays(skeletonRendererInstruction, flag2);
                }
            }
            if (this.OnPostProcessVertices != null)
            {
                this.OnPostProcessVertices(meshGenerator.Buffers);
            }
            Mesh mesh = nextMesh.mesh;

            meshGenerator.FillVertexData(mesh);
            rendererBuffers.UpdateSharedMaterials(submeshInstructions);
            if (flag2)
            {
                meshGenerator.FillTriangles(mesh);
                meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedShaderdMaterialsArray();
            }
            else if (rendererBuffers.MaterialsChangedInLastUpdate())
            {
                meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedShaderdMaterialsArray();
            }
            meshFilter.sharedMesh = mesh;
            nextMesh.instructionUsed.Set(skeletonRendererInstruction);
        }
Example #34
0
	/** Stores vertices and triangles for a single material. */
	private void AddSubmesh (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh, ExposedList<bool> flipStates) {
		int submeshIndex = submeshMaterials.Count;
		submeshMaterials.Add(material);

		if (submeshes.Count <= submeshIndex)
			submeshes.Add(new Submesh());
		else if (immutableTriangles)
			return;

		Submesh submesh = submeshes.Items[submeshIndex];

		int[] triangles = submesh.triangles;
		int trianglesCapacity = triangles.Length;
		if (lastSubmesh && trianglesCapacity > triangleCount) {
			// Last submesh may have more triangles than required, so zero triangles to the end.
			for (int i = triangleCount; i < trianglesCapacity; i++)
				triangles[i] = 0;
			submesh.triangleCount = triangleCount;
		} else if (trianglesCapacity != triangleCount) {
			// Reallocate triangles when not the exact size needed.
			submesh.triangles = triangles = new int[triangleCount];
			submesh.triangleCount = 0;
		}

		if (!renderMeshes && !frontFacing) {
			// Use stored triangles if possible.
			if (submesh.firstVertex != firstVertex || submesh.triangleCount < triangleCount) {
				submesh.triangleCount = triangleCount;
				submesh.firstVertex = firstVertex;
				int drawOrderIndex = 0;
				for (int i = 0; i < triangleCount; i += 6, firstVertex += 4, drawOrderIndex++) {
					triangles[i] = firstVertex;
					triangles[i + 1] = firstVertex + 2;
					triangles[i + 2] = firstVertex + 1;
					triangles[i + 3] = firstVertex + 2;
					triangles[i + 4] = firstVertex + 3;
					triangles[i + 5] = firstVertex + 1;
				}
			}
			return;
		}

		// Store triangles.
		ExposedList<Slot> drawOrder = skeleton.DrawOrder;
		for (int i = startSlot, triangleIndex = 0; i < endSlot; i++) {
			Slot slot = drawOrder.Items[i];
			Attachment attachment = slot.attachment;

			bool flip = flipStates.Items[i];

			if (attachment is RegionAttachment) {
				if (!flip) {
					triangles[triangleIndex] = firstVertex;
					triangles[triangleIndex + 1] = firstVertex + 2;
					triangles[triangleIndex + 2] = firstVertex + 1;
					triangles[triangleIndex + 3] = firstVertex + 2;
					triangles[triangleIndex + 4] = firstVertex + 3;
					triangles[triangleIndex + 5] = firstVertex + 1;
				} else {
					triangles[triangleIndex] = firstVertex + 1;
					triangles[triangleIndex + 1] = firstVertex + 2;
					triangles[triangleIndex + 2] = firstVertex;
					triangles[triangleIndex + 3] = firstVertex + 1;
					triangles[triangleIndex + 4] = firstVertex + 3;
					triangles[triangleIndex + 5] = firstVertex + 2;
				}

				triangleIndex += 6;
				firstVertex += 4;
				continue;
			}
			int[] attachmentTriangles;
			int attachmentVertexCount;
			MeshAttachment meshAttachment = attachment as MeshAttachment;
			if (meshAttachment != null) {
				attachmentVertexCount = meshAttachment.vertices.Length >> 1;
				attachmentTriangles = meshAttachment.triangles;
			} else {
				SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
				if (skinnedMeshAttachment != null) {
					attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
					attachmentTriangles = skinnedMeshAttachment.triangles;
				} else
					continue;
			}

			if (flip) {
				for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii += 3, triangleIndex += 3) {
					triangles[triangleIndex + 2] = firstVertex + attachmentTriangles[ii];
					triangles[triangleIndex + 1] = firstVertex + attachmentTriangles[ii + 1];
					triangles[triangleIndex] = firstVertex + attachmentTriangles[ii + 2];
				}
			} else {
				for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) {
					triangles[triangleIndex] = firstVertex + attachmentTriangles[ii];
				}
			}

			firstVertex += attachmentVertexCount;
		}
	}
		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			Bone bone = skeleton.bones.Items[boneIndex];
			float[] frames = this.frames;
			if (time < frames[0]) {
				if (setupPose) {
					bone.shearX = bone.data.shearX;
					bone.shearY = bone.data.shearY;
				}
				return;
			}

			float x, y;
			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
				x = frames[frames.Length + PREV_X];
				y = frames[frames.Length + PREV_Y];
			} else {
				// Interpolate between the previous frame and the current frame.
				int frame = Animation.BinarySearch(frames, time, ENTRIES);
				x = frames[frame + PREV_X];
				y = frames[frame + PREV_Y];
				float frameTime = frames[frame];
				float percent = GetCurvePercent(frame / ENTRIES - 1,
					1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));

				x = x + (frames[frame + X] - x) * percent;
				y = y + (frames[frame + Y] - y) * percent;
			}
			if (setupPose) {
				bone.shearX = bone.data.shearX + x * alpha;
				bone.shearY = bone.data.shearY + y * alpha;
			} else {
				bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
				bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
			}
		}
		public SkeletonBounds () {
			BoundingBoxes = new ExposedList<BoundingBoxAttachment>();
			Polygons = new ExposedList<Polygon>();
		}
    private bool CheckIfMustUpdateMeshStructure(ExposedList <int> attachmentsTriangleCountTemp, ExposedList <bool> attachmentsFlipStateTemp, ExposedList <LastState.AddSubmeshArguments> addSubmeshArgumentsTemp)
    {
        // Check if any mesh settings were changed
        bool mustUpdateMeshStructure =
            immutableTriangles != (useMesh1 ? lastState.immutableTrianglesMesh1 : lastState.immutableTrianglesMesh2);

#if UNITY_EDITOR
        mustUpdateMeshStructure |= !Application.isPlaying;
#endif

        if (mustUpdateMeshStructure)
        {
            return(true);
        }

        // Check if any attachments were enabled/disabled
        // or submesh structures has changed
        ExposedList <int>  attachmentsTriangleCountCurrentMesh;
        ExposedList <bool> attachmentsFlipStateCurrentMesh;
        ExposedList <LastState.AddSubmeshArguments> addSubmeshArgumentsCurrentMesh;
        if (useMesh1)
        {
            attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh1;
            addSubmeshArgumentsCurrentMesh      = lastState.addSubmeshArgumentsMesh1;
            attachmentsFlipStateCurrentMesh     = lastState.attachmentsFlipStateMesh1;
        }
        else
        {
            attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh2;
            addSubmeshArgumentsCurrentMesh      = lastState.addSubmeshArgumentsMesh2;
            attachmentsFlipStateCurrentMesh     = lastState.attachmentsFlipStateMesh2;
        }

        // Check attachments
        int attachmentCount = attachmentsTriangleCountTemp.Count;
        if (attachmentsTriangleCountCurrentMesh.Count != attachmentCount)
        {
            return(true);
        }

        for (int i = 0; i < attachmentCount; i++)
        {
            if (attachmentsTriangleCountCurrentMesh.Items[i] != attachmentsTriangleCountTemp.Items[i])
            {
                return(true);
            }
        }

        // Check flip state
        for (int i = 0; i < attachmentCount; i++)
        {
            if (attachmentsFlipStateCurrentMesh.Items[i] != attachmentsFlipStateTemp.Items[i])
            {
                return(true);
            }
        }

        // Check submeshes
        int submeshCount = addSubmeshArgumentsTemp.Count;
        if (addSubmeshArgumentsCurrentMesh.Count != submeshCount)
        {
            return(true);
        }

        for (int i = 0; i < submeshCount; i++)
        {
            if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref addSubmeshArgumentsTemp.Items[i]))
            {
                return(true);
            }
        }

        return(false);
    }
		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			Bone bone = skeleton.bones.Items[boneIndex];

			float[] frames = this.frames;
			if (time < frames[0]) {
				if (setupPose) bone.rotation = bone.data.rotation;
				return;
			}

			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
				if (setupPose) {
					bone.rotation = bone.data.rotation + frames[frames.Length + PREV_ROTATION] * alpha;
				} else {
					float rr = bone.data.rotation + frames[frames.Length + PREV_ROTATION] - bone.rotation;
					rr -= (16384 - (int)(16384.499999999996 - rr / 360)) * 360; // Wrap within -180 and 180.
					bone.rotation += rr * alpha;
				}
				return;
			}

			// 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 = GetCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));

			float r = frames[frame + ROTATION] - prevRotation;
			r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
			r = prevRotation + r * percent;
			if (setupPose) {
				r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
				bone.rotation = bone.data.rotation + r * alpha;
			} else {
				r = bone.data.rotation + r - bone.rotation;
				r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
				bone.rotation += r * alpha;
			}
		}
Example #39
0
        public virtual void LateUpdate()
        {
            if (!valid)
            {
                return;
            }

            if (
                (!meshRenderer.enabled)
#if SPINE_OPTIONAL_RENDEROVERRIDE
                && this.generateMeshOverride == null
#endif
                )
            {
                return;
            }


            // STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. ============================================================
            ExposedList <Slot> drawOrder = skeleton.drawOrder;
            var  drawOrderItems          = drawOrder.Items;
            int  drawOrderCount          = drawOrder.Count;
            bool renderMeshes            = this.renderMeshes;

            // Clear last state of attachments and submeshes
            var workingInstruction = this.currentInstructions;
            var workingAttachments = workingInstruction.attachments;
            workingAttachments.Clear(false);
            workingAttachments.GrowIfNeeded(drawOrderCount);
            workingAttachments.Count = drawOrderCount;
            var workingAttachmentsItems = workingInstruction.attachments.Items;

#if SPINE_OPTIONAL_FRONTFACING
            var workingFlips = workingInstruction.attachmentFlips;
            workingFlips.Clear(false);
            workingFlips.GrowIfNeeded(drawOrderCount);
            workingFlips.Count = drawOrderCount;
            var workingFlipsItems = workingFlips.Items;
#endif

            var workingSubmeshInstructions = workingInstruction.submeshInstructions;    // Items array should not be cached. There is dynamic writing to this list.
            workingSubmeshInstructions.Clear(false);

#if !SPINE_TK2D
            bool isCustomSlotMaterialsPopulated = customSlotMaterials.Count > 0;
#endif

            int      vertexCount = 0;
            int      submeshVertexCount = 0;
            int      submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
            Material lastMaterial = null;
            for (int i = 0; i < drawOrderCount; i++)
            {
                Slot       slot       = drawOrderItems[i];
                Attachment attachment = slot.attachment;
                workingAttachmentsItems[i] = attachment;

#if SPINE_OPTIONAL_FRONTFACING
                bool flip = frontFacing && (slot.bone.WorldSignX != slot.bone.WorldSignY);
                workingFlipsItems[i] = flip;
#endif

                object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
                int    attachmentVertexCount, attachmentTriangleCount;

                var regionAttachment = attachment as RegionAttachment;
                if (regionAttachment != null)
                {
                    rendererObject          = regionAttachment.RendererObject;
                    attachmentVertexCount   = 4;
                    attachmentTriangleCount = 6;
                }
                else
                {
                    if (!renderMeshes)
                    {
                        continue;
                    }
                    var meshAttachment = attachment as MeshAttachment;
                    if (meshAttachment != null)
                    {
                        rendererObject          = meshAttachment.RendererObject;
                        attachmentVertexCount   = meshAttachment.worldVerticesLength >> 1;
                        attachmentTriangleCount = meshAttachment.triangles.Length;
                    }
                    else
                    {
                        continue;
                    }
                }

#if !SPINE_TK2D
                Material material; //= (Material)((AtlasRegion)rendererObject).page.rendererObject; // For no customSlotMaterials
                if (isCustomSlotMaterialsPopulated)
                {
                    if (!customSlotMaterials.TryGetValue(slot, out material))
                    {
                        material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
                    }
                }
                else
                {
                    material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
                }
#else
                Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
#endif

                // Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator)
                bool forceSeparate = (separatorSlots.Count > 0 && separatorSlots.Contains(slot));
                if (vertexCount > 0 && (lastMaterial.GetInstanceID() != material.GetInstanceID() || forceSeparate))
                {
                    workingSubmeshInstructions.Add(
                        new Spine.Unity.MeshGeneration.SubmeshInstruction
                    {
                        skeleton         = this.skeleton,
                        material         = lastMaterial,
                        startSlot        = submeshStartSlotIndex,
                        endSlot          = i,
                        triangleCount    = submeshTriangleCount,
                        firstVertexIndex = submeshFirstVertex,
                        vertexCount      = submeshVertexCount,
                        forceSeparate    = forceSeparate
                    }
                        );
                    submeshTriangleCount  = 0;
                    submeshVertexCount    = 0;
                    submeshFirstVertex    = vertexCount;
                    submeshStartSlotIndex = i;
                }
                // Update state for the next iteration.
                lastMaterial          = material;
                submeshTriangleCount += attachmentTriangleCount;
                vertexCount          += attachmentVertexCount;
                submeshVertexCount   += attachmentVertexCount;
            }

            if (submeshVertexCount != 0)
            {
                workingSubmeshInstructions.Add(
                    new Spine.Unity.MeshGeneration.SubmeshInstruction
                {
                    skeleton         = this.skeleton,
                    material         = lastMaterial,
                    startSlot        = submeshStartSlotIndex,
                    endSlot          = drawOrderCount,
                    triangleCount    = submeshTriangleCount,
                    firstVertexIndex = submeshFirstVertex,
                    vertexCount      = submeshVertexCount,
                    forceSeparate    = false
                }
                    );
            }

            workingInstruction.vertexCount        = vertexCount;
            workingInstruction.immutableTriangles = this.immutableTriangles;
#if SPINE_OPTIONAL_FRONTFACING
            workingInstruction.frontFacing = this.frontFacing;
#endif


            // STEP 1.9. Post-process workingInstructions. ============================================================

#if SPINE_OPTIONAL_MATERIALOVERRIDE
            // Material overrides are done here so they can be applied per submesh instead of per slot
            // but they will still be passed through the GenerateMeshOverride delegate,
            // and will still go through the normal material match check step in STEP 3.
            if (customMaterialOverride.Count > 0)
            { // isCustomMaterialOverridePopulated
                var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items;
                for (int i = 0; i < workingSubmeshInstructions.Count; i++)
                {
                    var      m = workingSubmeshInstructionsItems[i].material;
                    Material mo;
                    if (customMaterialOverride.TryGetValue(m, out mo))
                    {
                        workingSubmeshInstructionsItems[i].material = mo;
                    }
                }
            }
#endif
#if SPINE_OPTIONAL_RENDEROVERRIDE
            if (this.generateMeshOverride != null)
            {
                this.generateMeshOverride(workingInstruction);
                if (disableRenderingOnOverride)
                {
                    return;
                }
            }
#endif


            // STEP 2. Update vertex buffer based on verts from the attachments.  ============================================================
            // Uses values that were also stored in workingInstruction.
            bool vertexCountIncreased = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.vertices, ref this.uvs, ref this.colors);
#if SPINE_OPTIONAL_NORMALS
            if (vertexCountIncreased && calculateNormals)
            {
                Vector3[] localNormals = this.normals = new Vector3[vertexCount];
                Vector3   normal       = new Vector3(0, 0, -1);
                for (int i = 0; i < vertexCount; i++)
                {
                    localNormals[i] = normal;
                }
            }
#endif

            Vector3 meshBoundsMin;
            Vector3 meshBoundsMax;
            if (vertexCount <= 0)
            {
                meshBoundsMin = new Vector3(0, 0, 0);
                meshBoundsMax = new Vector3(0, 0, 0);
            }
            else
            {
                meshBoundsMin.x = int.MaxValue;
                meshBoundsMin.y = int.MaxValue;
                meshBoundsMax.x = int.MinValue;
                meshBoundsMax.y = int.MinValue;

                if (zSpacing > 0f)
                {
                    meshBoundsMin.z = 0f;
                    meshBoundsMax.z = zSpacing * (drawOrderCount - 1);
                }
                else
                {
                    meshBoundsMin.z = zSpacing * (drawOrderCount - 1);
                    meshBoundsMax.z = 0f;
                }
            }
            int vertexIndex = 0;
            ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, this.zSpacing, pmaVertexColors, this.vertices, this.uvs, this.colors, ref vertexIndex, ref tempVertices, ref meshBoundsMin, ref meshBoundsMax, renderMeshes);


            // Step 3. Move the mesh data into a UnityEngine.Mesh ============================================================
            var currentSmartMesh = doubleBufferedMesh.GetNext();    // Double-buffer for performance.
            var currentMesh      = currentSmartMesh.mesh;
            currentMesh.vertices = this.vertices;
            currentMesh.colors32 = colors;
            currentMesh.uv       = uvs;
            currentMesh.bounds   = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);

            var currentSmartMeshInstructionUsed = currentSmartMesh.instructionUsed;
#if SPINE_OPTIONAL_NORMALS
            if (calculateNormals && currentSmartMeshInstructionUsed.vertexCount < vertexCount)
            {
                currentMesh.normals = normals;
            }
#endif

            // Check if the triangles should also be updated.
            // This thorough structure check is cheaper than updating triangles every frame.
            bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(workingInstruction, currentSmartMeshInstructionUsed);
            int  submeshCount            = workingSubmeshInstructions.Count;
            if (mustUpdateMeshStructure)
            {
                var thisSubmeshMaterials = this.submeshMaterials;
                thisSubmeshMaterials.Clear(false);

                int oldSubmeshCount = submeshes.Count;

                if (submeshes.Capacity < submeshCount)
                {
                    submeshes.Capacity = submeshCount;
                }
                for (int i = oldSubmeshCount; i < submeshCount; i++)
                {
                    submeshes.Items[i] = new ArraysMeshGenerator.SubmeshTriangleBuffer(workingSubmeshInstructions.Items[i].triangleCount);
                }
                submeshes.Count = submeshCount;

                var mutableTriangles = !workingInstruction.immutableTriangles;
                for (int i = 0, last = submeshCount - 1; i < submeshCount; i++)
                {
                    var submeshInstruction = workingSubmeshInstructions.Items[i];

                    if (mutableTriangles || i >= oldSubmeshCount)
                    {
#if !SPINE_OPTIONAL_FRONTFACING
                        var currentSubmesh           = submeshes.Items[i];
                        int instructionTriangleCount = submeshInstruction.triangleCount;
                        if (renderMeshes)
                        {
                            ArraysMeshGenerator.FillTriangles(ref currentSubmesh.triangles, skeleton, instructionTriangleCount, submeshInstruction.firstVertexIndex, submeshInstruction.startSlot, submeshInstruction.endSlot, (i == last));
                            currentSubmesh.triangleCount = instructionTriangleCount;
                        }
                        else
                        {
                            ArraysMeshGenerator.FillTrianglesQuads(ref currentSubmesh.triangles, ref currentSubmesh.triangleCount, ref currentSubmesh.firstVertex, submeshInstruction.firstVertexIndex, instructionTriangleCount, (i == last));
                        }
#else
                        SetSubmesh(i, submeshInstruction, currentInstructions.attachmentFlips, i == last);
#endif
                    }

                    thisSubmeshMaterials.Add(submeshInstruction.material);
                }

                currentMesh.subMeshCount = submeshCount;

                for (int i = 0; i < submeshCount; ++i)
                {
                    currentMesh.SetTriangles(submeshes.Items[i].triangles, i);
                }
            }

#if SPINE_OPTIONAL_SOLVETANGENTS
            if (calculateTangents)
            {
                ArraysMeshGenerator.SolveTangents2DEnsureSize(ref this.tangents, ref this.tempTanBuffer, vertices.Length);
                for (int i = 0; i < submeshCount; i++)
                {
                    var submesh = submeshes.Items[i];
                    ArraysMeshGenerator.SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, this.vertices, this.uvs, vertexCount);
                }
                ArraysMeshGenerator.SolveTangents2DBuffer(this.tangents, this.tempTanBuffer, vertexCount);
                currentMesh.tangents = this.tangents;
            }
#endif

            // CheckIfMustUpdateMaterialArray (last pushed materials vs currently parsed materials)
            // Needs to check against the Working Submesh Instructions Materials instead of the cached submeshMaterials.
            {
                var  lastPushedMaterials         = this.sharedMaterials;
                bool mustUpdateRendererMaterials = mustUpdateMeshStructure ||
                                                   (lastPushedMaterials.Length != submeshCount);

                if (!mustUpdateRendererMaterials)
                {
                    var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items;
                    for (int i = 0, n = lastPushedMaterials.Length; i < n; i++)
                    {
                        if (lastPushedMaterials[i].GetInstanceID() != workingSubmeshInstructionsItems[i].material.GetInstanceID())
                        {   // Bounds check is implied above.
                            mustUpdateRendererMaterials = true;
                            break;
                        }
                    }
                }

                if (mustUpdateRendererMaterials)
                {
                    if (submeshMaterials.Count == sharedMaterials.Length)
                    {
                        submeshMaterials.CopyTo(sharedMaterials);
                    }
                    else
                    {
                        sharedMaterials = submeshMaterials.ToArray();
                    }

                    meshRenderer.sharedMaterials = sharedMaterials;
                }
            }


            // Step 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh. ============================================================
            meshFilter.sharedMesh = currentMesh;
            currentSmartMesh.instructionUsed.Set(workingInstruction);
        }
		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
			Bone bone = skeleton.bones.Items[boneIndex];

			float[] frames = this.frames;
			if (time < frames[0]) {
				if (setupPose) {
					bone.scaleX = bone.data.scaleX;
					bone.scaleY = bone.data.scaleY;
				}
				return;
			}

			float x, y;
			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
				x = frames[frames.Length + PREV_X] * bone.data.scaleX;
				y = frames[frames.Length + PREV_Y] * bone.data.scaleY;
			} else {
				// Interpolate between the previous frame and the current frame.
				int frame = Animation.BinarySearch(frames, time, ENTRIES);
				x = frames[frame + PREV_X];
				y = frames[frame + PREV_Y];
				float frameTime = frames[frame];
				float percent = GetCurvePercent(frame / ENTRIES - 1,
					1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));

				x = (x + (frames[frame + X] - x) * percent) * bone.data.scaleX;
				y = (y + (frames[frame + Y] - y) * percent) * bone.data.scaleY;
			}
			if (alpha == 1) {
				bone.scaleX = x;
				bone.scaleY = y;
			} else {
				float bx, by;
				if (setupPose) {
					bx = bone.data.scaleX;
					by = bone.data.scaleY;
				} else {
					bx = bone.scaleX;
					by = bone.scaleY;
				}
				// Mixing out uses sign of setup or current pose, else use sign of key.
				if (mixingOut) {
					x = Math.Abs(x) * Math.Sign(bx);
					y = Math.Abs(y) * Math.Sign(by);
				} else {
					bx = Math.Abs(bx) * Math.Sign(x);
					by = Math.Abs(by) * Math.Sign(y);
				}
				bone.scaleX = bx + (x - bx) * alpha;
				bone.scaleY = by + (y - by) * alpha;
			}
		}
    public void UpdateMesh()
    {
        //Debug.Log("UpdateMesh");
        if (!valid)
        {
            return;
        }

        float scale = canvas.referencePixelsPerUnit;

        // Count vertices and submesh triangles.
        int                vertexCount = 0;
        int                submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
        Material           lastMaterial   = null;
        ExposedList <Slot> drawOrder      = skeleton.drawOrder;
        int                drawOrderCount = drawOrder.Count;
        bool               renderMeshes   = this.renderMeshes;

        // Clear last state of attachments and submeshes
        ExposedList <int> attachmentsTriangleCountTemp = lastState.attachmentsTriangleCountTemp;

        attachmentsTriangleCountTemp.GrowIfNeeded(drawOrderCount);
        attachmentsTriangleCountTemp.Count = drawOrderCount;
        ExposedList <bool> attachmentsFlipStateTemp = lastState.attachmentsFlipStateTemp;

        attachmentsFlipStateTemp.GrowIfNeeded(drawOrderCount);
        attachmentsFlipStateTemp.Count = drawOrderCount;

        ExposedList <LastState.AddSubmeshArguments> addSubmeshArgumentsTemp = lastState.addSubmeshArgumentsTemp;

        addSubmeshArgumentsTemp.Clear(false);
        for (int i = 0; i < drawOrderCount; i++)
        {
            Slot       slot       = drawOrder.Items[i];
            Bone       bone       = slot.bone;
            Attachment attachment = slot.attachment;

            object rendererObject;
            int    attachmentVertexCount, attachmentTriangleCount;
            bool   worldScaleXIsPositive = bone.worldScaleX >= 0f;
            bool   worldScaleYIsPositive = bone.worldScaleY >= 0f;
            bool   worldScaleIsSameSigns = (worldScaleXIsPositive && worldScaleYIsPositive) ||
                                           (!worldScaleXIsPositive && !worldScaleYIsPositive);
            bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns);
            attachmentsFlipStateTemp.Items[i] = flip;

            attachmentsTriangleCountTemp.Items[i] = -1;
            var regionAttachment = attachment as RegionAttachment;
            if (regionAttachment != null)
            {
                rendererObject          = regionAttachment.RendererObject;
                attachmentVertexCount   = 4;
                attachmentTriangleCount = 6;
            }
            else
            {
                if (!renderMeshes)
                {
                    continue;
                }
                var meshAttachment = attachment as MeshAttachment;
                if (meshAttachment != null)
                {
                    rendererObject          = meshAttachment.RendererObject;
                    attachmentVertexCount   = meshAttachment.vertices.Length >> 1;
                    attachmentTriangleCount = meshAttachment.triangles.Length;
                }
                else
                {
                    var skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
                    if (skinnedMeshAttachment != null)
                    {
                        rendererObject          = skinnedMeshAttachment.RendererObject;
                        attachmentVertexCount   = skinnedMeshAttachment.uvs.Length >> 1;
                        attachmentTriangleCount = skinnedMeshAttachment.triangles.Length;
                    }
                    else
                    {
                        continue;
                    }
                }
            }

            // Populate submesh when material changes.
#if !SPINE_TK2D
            var currentMaterial = (Material)((AtlasRegion)rendererObject).page.rendererObject;
#else
            var currentMaterial = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
#endif
            if ((lastMaterial != null && lastMaterial.GetInstanceID() != currentMaterial.GetInstanceID()))
            {
                addSubmeshArgumentsTemp.Add(
                    new LastState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false)
                    );
                submeshTriangleCount  = 0;
                submeshFirstVertex    = vertexCount;
                submeshStartSlotIndex = i;
            }
            lastMaterial = currentMaterial;

            submeshTriangleCount += attachmentTriangleCount;
            vertexCount          += attachmentVertexCount;

            attachmentsTriangleCountTemp.Items[i] = attachmentTriangleCount;
        }
        addSubmeshArgumentsTemp.Add(
            new LastState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true)
            );

        bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(attachmentsTriangleCountTemp, attachmentsFlipStateTemp, addSubmeshArgumentsTemp);
        if (mustUpdateMeshStructure)
        {
            submeshMaterials.Clear();
            for (int i = 0, n = addSubmeshArgumentsTemp.Count; i < n; i++)
            {
                LastState.AddSubmeshArguments arguments = addSubmeshArgumentsTemp.Items[i];
                AddSubmesh(
                    arguments.material,
                    arguments.startSlot,
                    arguments.endSlot,
                    arguments.triangleCount,
                    arguments.firstVertex,
                    arguments.lastSubmesh,
                    attachmentsFlipStateTemp
                    );
            }

            // Set materials.
            if (submeshMaterials.Count == sharedMaterials.Length)
            {
                submeshMaterials.CopyTo(sharedMaterials);
            }
            else
            {
                sharedMaterials = submeshMaterials.ToArray();
            }

            //meshRenderer.sharedMaterials = sharedMaterials;
            this.material = sharedMaterials[0];
            canvasRenderer.SetMaterial(sharedMaterials[0], (Texture)null);
        }

        // Ensure mesh data is the right size.
        Vector3[] vertices     = this.vertices;
        bool      newTriangles = vertexCount > vertices.Length;
        if (newTriangles)
        {
            // Not enough vertices, increase size.
            this.vertices = vertices = new Vector3[vertexCount];
            this.colors   = new Color32[vertexCount];
            this.uvs      = new Vector2[vertexCount];
            mesh1.Clear();
            mesh2.Clear();
        }
        else
        {
            // Too many vertices, zero the extra.
            Vector3 zero = Vector3.zero;
            for (int i = vertexCount, n = lastState.vertexCount; i < n; i++)
            {
                vertices[i] = zero;
            }
        }
        lastState.vertexCount = vertexCount;

        // Setup mesh.
        float     zSpacing = this.zSpacing;
        float[]   tempVertices = this.tempVertices;
        Vector2[] uvs = this.uvs;
        Color32[] colors = this.colors;
        int       vertexIndex = 0;
        Color32   vertColor = new Color32();
        Color     graphicColor = base.color;
        float     a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;


        // Mesh bounds
        Vector3 meshBoundsMin;
        meshBoundsMin.x = float.MaxValue;
        meshBoundsMin.y = float.MaxValue;
        meshBoundsMin.z = zSpacing > 0f ? 0f : zSpacing * (drawOrderCount - 1);
        Vector3 meshBoundsMax;
        meshBoundsMax.x = float.MinValue;
        meshBoundsMax.y = float.MinValue;
        meshBoundsMax.z = zSpacing < 0f ? 0f : zSpacing * (drawOrderCount - 1);

        for (int i = 0; i < drawOrderCount; i++)
        {
            Slot       slot             = drawOrder.Items[i];
            Attachment attachment       = slot.attachment;
            var        regionAttachment = attachment as RegionAttachment;
            if (regionAttachment != null)
            {
                regionAttachment.ComputeWorldVertices(slot.bone, tempVertices);

                float z = i * zSpacing;
                vertices[vertexIndex].x     = tempVertices[RegionAttachment.X1] * scale;
                vertices[vertexIndex].y     = tempVertices[RegionAttachment.Y1] * scale;
                vertices[vertexIndex].z     = z;
                vertices[vertexIndex + 1].x = tempVertices[RegionAttachment.X4] * scale;
                vertices[vertexIndex + 1].y = tempVertices[RegionAttachment.Y4] * scale;
                vertices[vertexIndex + 1].z = z;
                vertices[vertexIndex + 2].x = tempVertices[RegionAttachment.X2] * scale;
                vertices[vertexIndex + 2].y = tempVertices[RegionAttachment.Y2] * scale;
                vertices[vertexIndex + 2].z = z;
                vertices[vertexIndex + 3].x = tempVertices[RegionAttachment.X3] * scale;
                vertices[vertexIndex + 3].y = tempVertices[RegionAttachment.Y3] * scale;
                vertices[vertexIndex + 3].z = z;

                vertColor.a = (byte)(a * slot.a * regionAttachment.a * graphicColor.a);
                vertColor.r = (byte)(r * slot.r * regionAttachment.r * graphicColor.r * vertColor.a);
                vertColor.g = (byte)(g * slot.g * regionAttachment.g * graphicColor.g * vertColor.a);
                vertColor.b = (byte)(b * slot.b * regionAttachment.b * graphicColor.b * vertColor.a);
                if (slot.data.blendMode == BlendMode.additive)
                {
                    vertColor.a = 0;
                }
                colors[vertexIndex]     = vertColor;
                colors[vertexIndex + 1] = vertColor;
                colors[vertexIndex + 2] = vertColor;
                colors[vertexIndex + 3] = vertColor;

                float[] regionUVs = regionAttachment.uvs;
                uvs[vertexIndex].x     = regionUVs[RegionAttachment.X1];
                uvs[vertexIndex].y     = regionUVs[RegionAttachment.Y1];
                uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4];
                uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4];
                uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2];
                uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2];
                uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3];
                uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3];


                // Calculate Bounds min/max X
                if (tempVertices[RegionAttachment.X1] < meshBoundsMin.x)
                {
                    meshBoundsMin.x = tempVertices[RegionAttachment.X1];
                }
                else if (tempVertices[RegionAttachment.X1] > meshBoundsMax.x)
                {
                    meshBoundsMax.x = tempVertices[RegionAttachment.X1];
                }
                if (tempVertices[RegionAttachment.X2] < meshBoundsMin.x)
                {
                    meshBoundsMin.x = tempVertices[RegionAttachment.X2];
                }
                else if (tempVertices[RegionAttachment.X2] > meshBoundsMax.x)
                {
                    meshBoundsMax.x = tempVertices[RegionAttachment.X2];
                }
                if (tempVertices[RegionAttachment.X3] < meshBoundsMin.x)
                {
                    meshBoundsMin.x = tempVertices[RegionAttachment.X3];
                }
                else if (tempVertices[RegionAttachment.X3] > meshBoundsMax.x)
                {
                    meshBoundsMax.x = tempVertices[RegionAttachment.X3];
                }
                if (tempVertices[RegionAttachment.X4] < meshBoundsMin.x)
                {
                    meshBoundsMin.x = tempVertices[RegionAttachment.X4];
                }
                else if (tempVertices[RegionAttachment.X4] > meshBoundsMax.x)
                {
                    meshBoundsMax.x = tempVertices[RegionAttachment.X4];
                }

                // Calculate Bounds min/max Y
                if (tempVertices[RegionAttachment.Y1] < meshBoundsMin.y)
                {
                    meshBoundsMin.y = tempVertices[RegionAttachment.Y1];
                }
                else if (tempVertices[RegionAttachment.Y1] > meshBoundsMax.y)
                {
                    meshBoundsMax.y = tempVertices[RegionAttachment.Y1];
                }
                if (tempVertices[RegionAttachment.Y2] < meshBoundsMin.y)
                {
                    meshBoundsMin.y = tempVertices[RegionAttachment.Y2];
                }
                else if (tempVertices[RegionAttachment.Y2] > meshBoundsMax.y)
                {
                    meshBoundsMax.y = tempVertices[RegionAttachment.Y2];
                }
                if (tempVertices[RegionAttachment.Y3] < meshBoundsMin.y)
                {
                    meshBoundsMin.y = tempVertices[RegionAttachment.Y3];
                }
                else if (tempVertices[RegionAttachment.Y3] > meshBoundsMax.y)
                {
                    meshBoundsMax.y = tempVertices[RegionAttachment.Y3];
                }
                if (tempVertices[RegionAttachment.Y4] < meshBoundsMin.y)
                {
                    meshBoundsMin.y = tempVertices[RegionAttachment.Y4];
                }
                else if (tempVertices[RegionAttachment.Y4] > meshBoundsMax.y)
                {
                    meshBoundsMax.y = tempVertices[RegionAttachment.Y4];
                }

                vertexIndex += 4;
            }
            else
            {
                if (!renderMeshes)
                {
                    continue;
                }
                var meshAttachment = attachment as MeshAttachment;
                if (meshAttachment != null)
                {
                    int meshVertexCount = meshAttachment.vertices.Length;
                    if (tempVertices.Length < meshVertexCount)
                    {
                        this.tempVertices = tempVertices = new float[meshVertexCount];
                    }
                    meshAttachment.ComputeWorldVertices(slot, tempVertices);

                    vertColor.a = (byte)(a * slot.a * meshAttachment.a * graphicColor.a);
                    vertColor.r = (byte)(r * slot.r * meshAttachment.r * graphicColor.r * vertColor.a);
                    vertColor.g = (byte)(g * slot.g * meshAttachment.g * graphicColor.g * vertColor.a);
                    vertColor.b = (byte)(b * slot.b * meshAttachment.b * graphicColor.b * vertColor.a);
                    if (slot.data.blendMode == BlendMode.additive)
                    {
                        vertColor.a = 0;
                    }

                    float[] meshUVs = meshAttachment.uvs;
                    float   z       = i * zSpacing;
                    for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++)
                    {
                        vertices[vertexIndex].x = tempVertices[ii] * scale;
                        vertices[vertexIndex].y = tempVertices[ii + 1] * scale;
                        vertices[vertexIndex].z = z;
                        colors[vertexIndex]     = vertColor;
                        uvs[vertexIndex].x      = meshUVs[ii];
                        uvs[vertexIndex].y      = meshUVs[ii + 1];

                        // Calculate Bounds
                        if (tempVertices[ii] < meshBoundsMin.x)
                        {
                            meshBoundsMin.x = tempVertices[ii];
                        }
                        else if (tempVertices[ii] > meshBoundsMax.x)
                        {
                            meshBoundsMax.x = tempVertices[ii];
                        }
                        if (tempVertices[ii + 1] < meshBoundsMin.y)
                        {
                            meshBoundsMin.y = tempVertices[ii + 1];
                        }
                        else if (tempVertices[ii + 1] > meshBoundsMax.y)
                        {
                            meshBoundsMax.y = tempVertices[ii + 1];
                        }
                    }
                }
                else
                {
                    var skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
                    if (skinnedMeshAttachment != null)
                    {
                        int meshVertexCount = skinnedMeshAttachment.uvs.Length;
                        if (tempVertices.Length < meshVertexCount)
                        {
                            this.tempVertices = tempVertices = new float[meshVertexCount];
                        }
                        skinnedMeshAttachment.ComputeWorldVertices(slot, tempVertices);

                        vertColor.a = (byte)(a * slot.a * skinnedMeshAttachment.a * graphicColor.a);
                        vertColor.r = (byte)(r * slot.r * skinnedMeshAttachment.r * graphicColor.r * vertColor.a);
                        vertColor.g = (byte)(g * slot.g * skinnedMeshAttachment.g * graphicColor.g * vertColor.a);
                        vertColor.b = (byte)(b * slot.b * skinnedMeshAttachment.b * graphicColor.b * vertColor.a);
                        if (slot.data.blendMode == BlendMode.additive)
                        {
                            vertColor.a = 0;
                        }

                        float[] meshUVs = skinnedMeshAttachment.uvs;
                        float   z       = i * zSpacing;
                        for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++)
                        {
                            vertices[vertexIndex].x = tempVertices[ii] * scale;
                            vertices[vertexIndex].y = tempVertices[ii + 1] * scale;
                            vertices[vertexIndex].z = z;
                            colors[vertexIndex]     = vertColor;
                            uvs[vertexIndex].x      = meshUVs[ii];
                            uvs[vertexIndex].y      = meshUVs[ii + 1];


                            // Calculate Bounds
                            if (tempVertices[ii] < meshBoundsMin.x)
                            {
                                meshBoundsMin.x = tempVertices[ii];
                            }
                            else if (tempVertices[ii] > meshBoundsMax.x)
                            {
                                meshBoundsMax.x = tempVertices[ii];
                            }
                            if (tempVertices[ii + 1] < meshBoundsMin.y)
                            {
                                meshBoundsMin.y = tempVertices[ii + 1];
                            }
                            else if (tempVertices[ii + 1] > meshBoundsMax.y)
                            {
                                meshBoundsMax.y = tempVertices[ii + 1];
                            }
                        }
                    }
                }
            }
        }


        // Double buffer mesh.
        Mesh mesh = useMesh1 ? mesh1 : mesh2;

        // Push data from buffers.
        mesh.vertices = vertices;
        mesh.colors32 = colors;
        mesh.uv       = uvs;

        // Set Mesh bounds.
        Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin) * scale;    // scaled
        Vector3 meshBoundsCenter  = meshBoundsMin + meshBoundsExtents * 0.5f;
        mesh.bounds = new Bounds(meshBoundsCenter, meshBoundsExtents);
        //mesh.RecalculateBounds();

        canvasRenderer.SetMesh(mesh);
        //this.SetVerticesDirty();


        if (mustUpdateMeshStructure)
        {
            int submeshCount = submeshMaterials.Count;
            mesh.subMeshCount = submeshCount;
            for (int i = 0; i < submeshCount; ++i)
            {
                mesh.SetTriangles(submeshes.Items[i].triangles, i);
            }

            /*
             * TODO: Check fix with a known repro case.
             *          if (useMesh1)
             *                  lastState.forceUpdateMesh1 = false;
             *          else
             *                  lastState.forceUpdateMesh2 = false;
             */
        }

        if (newTriangles && calculateNormals)
        {
            var normals = new Vector3[vertexCount];
            var normal  = new Vector3(0, 0, -1);
            for (int i = 0; i < vertexCount; i++)
            {
                normals[i] = normal;
            }
            (useMesh1 ? mesh2 : mesh1).vertices = vertices; // Set other mesh vertices.
            mesh1.normals = normals;
            mesh2.normals = normals;

            if (calculateTangents)
            {
                var tangents = new Vector4[vertexCount];
                var tangent  = new Vector3(0, 0, 1);
                for (int i = 0; i < vertexCount; i++)
                {
                    tangents[i] = tangent;
                }
                mesh1.tangents = tangents;
                mesh2.tangents = tangents;
            }
        }

        // Update previous state
        ExposedList <int>  attachmentsTriangleCountCurrentMesh;
        ExposedList <bool> attachmentsFlipStateCurrentMesh;
        ExposedList <LastState.AddSubmeshArguments> addSubmeshArgumentsCurrentMesh;
        if (useMesh1)
        {
            attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh1;
            addSubmeshArgumentsCurrentMesh      = lastState.addSubmeshArgumentsMesh1;
            attachmentsFlipStateCurrentMesh     = lastState.attachmentsFlipStateMesh1;
            lastState.immutableTrianglesMesh1   = immutableTriangles;
        }
        else
        {
            attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh2;
            addSubmeshArgumentsCurrentMesh      = lastState.addSubmeshArgumentsMesh2;
            attachmentsFlipStateCurrentMesh     = lastState.attachmentsFlipStateMesh2;
            lastState.immutableTrianglesMesh2   = immutableTriangles;
        }

        attachmentsTriangleCountCurrentMesh.GrowIfNeeded(attachmentsTriangleCountTemp.Capacity);
        attachmentsTriangleCountCurrentMesh.Count = attachmentsTriangleCountTemp.Count;
        attachmentsTriangleCountTemp.CopyTo(attachmentsTriangleCountCurrentMesh.Items, 0);

        attachmentsFlipStateCurrentMesh.GrowIfNeeded(attachmentsFlipStateTemp.Capacity);
        attachmentsFlipStateCurrentMesh.Count = attachmentsFlipStateTemp.Count;
        attachmentsFlipStateTemp.CopyTo(attachmentsFlipStateCurrentMesh.Items, 0);

        addSubmeshArgumentsCurrentMesh.GrowIfNeeded(addSubmeshArgumentsTemp.Count);
        addSubmeshArgumentsCurrentMesh.Count = addSubmeshArgumentsTemp.Count;
        addSubmeshArgumentsTemp.CopyTo(addSubmeshArgumentsCurrentMesh.Items);

        useMesh1 = !useMesh1;
    }