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; }
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); }
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); }
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)); }
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 > lastTime and <= 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); } }
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; }
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(); } }
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)); }
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); }
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; } }
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)); }
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); }
/** 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; } }
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; }