mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-25 22:23:42 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
ed2e688b59
@ -17,6 +17,61 @@ The Spine Runtimes are developed with the intent to be used with data exported f
|
||||
|
||||
Alternatively, the contents of the `spine-c/src`, `spine-c/include` and `spine-cocos2dx/3/src` directories can be copied into your project. Be sure your header search path will find the contents of the `spine-c/include` and `spine-cocos2dx/3.1/src` directories. Note that the includes use `spine/Xxx.h`, so the `spine` directory cannot be omitted when copying the files.
|
||||
|
||||
## Setup for Cocos Studio Users (cocos2d-x-3.10)
|
||||
1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip).
|
||||
- Extract them somewhere you can find them. You will only need `spine-c` and `spine-cocos2dx`.
|
||||
1. Download and install Cocos: http://www.cocos2d-x.org/download
|
||||
- (2016 March 4) Downloading and installing Cocos Studio and cocos2d-x-3.10 includes a Spine runtime in their source files. For instance, if the default install folder is `C:\Cocos\`, you will find an existing spine-c and spine-cocos2dx runtime inside `C:\Cocos\Cocos2d-x\cocos2d-x-3.10\cocos\editor-support\spine`.
|
||||
- Replace the contents of that folder with the files from the following Spine runtime folders:
|
||||
- `spine-c/src/spine`
|
||||
- `spine-c/include/spine`
|
||||
- `spine-cocos2dx/3/src/spine`
|
||||
- Since this is the shared version of the runtime Cocos Studio uses for all projects created through it, updating the Spine runtime here also updates the Spine runtime of all those projects.
|
||||
|
||||
### Examples
|
||||
1. Create a **new C++ Cocos Studio project**. Make sure you can find that folder.
|
||||
1. The `spine-cocos2dx/3/` runtime folder contains a folder named `example`.
|
||||
- Copy the files in `spine-cocos2dx/3/example/Classes` folder (except `AppDelegate.cpp`, `AppDelegate.h` and `AppMacros.h` into the `Classes` folder of your Cocos Studio project.)
|
||||
1. Add these files into your C++ Solution. (through your IDE, Visual Studio or Eclipse, etc...)
|
||||
2. If this is a newly created project, edit the `AppDelegate.cpp`. For this example, we will use RaptorExample.
|
||||
1. Add `#include "RaptorExample.h"` to the includes near the top of the file.
|
||||
2. Look for the `bool AppDelegate::applicationDidFinishLaunching()` method and replace it with this. Notice the line where it defines the scene `auto scene = RaptorExample::scene()`:
|
||||
|
||||
```cpp
|
||||
bool AppDelegate::applicationDidFinishLaunching() {
|
||||
// initialize director
|
||||
auto director = Director::getInstance();
|
||||
auto glview = director->getOpenGLView();
|
||||
if(!glview) {
|
||||
glview = GLViewImpl::createWithRect("Spine Test", Rect(0, 0, 960, 640));
|
||||
director->setOpenGLView(glview);
|
||||
}
|
||||
|
||||
director->getOpenGLView()->setDesignResolutionSize(960, 640, ResolutionPolicy::SHOW_ALL);
|
||||
|
||||
// turn on display FPS
|
||||
director->setDisplayStats(true);
|
||||
|
||||
// set FPS. the default value is 1.0/60 if you don't call this
|
||||
director->setAnimationInterval(1.0f / 60);
|
||||
|
||||
FileUtils::getInstance()->addSearchPath("res");
|
||||
|
||||
// create a scene. it's an autorelease object
|
||||
auto scene = RaptorExample::scene();
|
||||
|
||||
// run
|
||||
director->runWithScene(scene);
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
You can do the same with `GoblinsExample` and `SpineboyExample`.
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Images are premultiplied by cocos2d-x, so the Spine atlas images should *not* use premultiplied alpha.
|
||||
|
||||
@ -604,7 +604,7 @@ function Animation.FfdTimeline.new ()
|
||||
|
||||
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
|
||||
local slot = skeleton.slots[self.slotIndex]
|
||||
if slot.attachment ~= attachment then return end
|
||||
if slot.attachment ~= self.attachment then return end
|
||||
|
||||
local frames = self.frames
|
||||
if time < frames[0] then return end -- Time is before first frame.
|
||||
@ -612,7 +612,7 @@ function Animation.FfdTimeline.new ()
|
||||
local frameVertices = self.frameVertices
|
||||
local vertexCount = #frameVertices[0]
|
||||
local vertices = slot.attachmentVertices
|
||||
if #vertices < vertexCount then
|
||||
if not vertices or #vertices < vertexCount then
|
||||
vertices = {}
|
||||
slot.attachmentVertices = vertices
|
||||
end
|
||||
@ -620,16 +620,15 @@ function Animation.FfdTimeline.new ()
|
||||
alpha = 1 -- Don't mix from uninitialized slot vertices.
|
||||
end
|
||||
slot.attachmentVerticesCount = vertexCount
|
||||
|
||||
if time >= frames[#frames] then -- Time is after last frame.
|
||||
local lastVertices = frameVertices[#frames]
|
||||
if time >= frames[#frames - 1] then -- Time is after last frame.
|
||||
local lastVertices = frameVertices[#frames - 1]
|
||||
if alpha < 1 then
|
||||
for i = 0, vertexCount do
|
||||
for i = 1, vertexCount do
|
||||
local vertex = vertices[i]
|
||||
vertices[i] = vertex + (lastVertices[i] - vertex) * alpha
|
||||
end
|
||||
else
|
||||
for i = 0, vertexCount do
|
||||
for i = 1, vertexCount do
|
||||
vertices[i] = lastVertices[i]
|
||||
end
|
||||
end
|
||||
@ -647,13 +646,13 @@ function Animation.FfdTimeline.new ()
|
||||
local nextVertices = frameVertices[frameIndex]
|
||||
|
||||
if alpha < 1 then
|
||||
for i = 0, vertexCount do
|
||||
for i = 1, vertexCount do
|
||||
local prev = prevVertices[i]
|
||||
local vertices = vertices[i]
|
||||
vertices[i] = vertices + (prev + (nextVertices[i] - prev) * percent - vertices) * alpha
|
||||
local vertex = vertices[i]
|
||||
vertices[i] = vertex + (prev + (nextVertices[i] - prev) * percent - vertex) * alpha
|
||||
end
|
||||
else
|
||||
for i = 0, vertexCount do
|
||||
for i = 1, vertexCount do
|
||||
local prev = prevVertices[i]
|
||||
vertices[i] = prev + (nextVertices[i] - prev) * percent
|
||||
end
|
||||
|
||||
@ -32,6 +32,8 @@
|
||||
local AttachmentType = require "spine-lua.AttachmentType"
|
||||
local RegionAttachment = require "spine-lua.RegionAttachment"
|
||||
local BoundingBoxAttachment = require "spine-lua.BoundingBoxAttachment"
|
||||
local MeshAttachment = require "spine-lua.MeshAttachment"
|
||||
local SkinningMeshAttachment = require "spine-lua.SkinnedMeshAttachment"
|
||||
|
||||
local AttachmentLoader = {}
|
||||
function AttachmentLoader.new ()
|
||||
|
||||
@ -34,7 +34,7 @@ local AttachmentType = require "spine-lua.AttachmentType"
|
||||
local MeshAttachment = {}
|
||||
function MeshAttachment.new (name)
|
||||
if not name then error("name cannot be nil", 2) end
|
||||
|
||||
|
||||
local self = {
|
||||
name = name,
|
||||
type = AttachmentType.mesh,
|
||||
@ -46,7 +46,7 @@ function MeshAttachment.new (name)
|
||||
r = 1, g = 1, b = 1, a = 1,
|
||||
path = nil,
|
||||
rendererObject = nil,
|
||||
regionU = 0, regionV = 0, regionU2 = 0, regionV2 = 0, regionRotate = false,
|
||||
regionU = 0, regionV = 0, regionU2 = 1, regionV2 = 1, regionRotate = false,
|
||||
regionOffsetX = 0, regionOffsetY = 0,
|
||||
regionWidth = 0, regionHeight = 0,
|
||||
regionOriginalWidth = 0, regionOriginalHeight = 0,
|
||||
@ -75,12 +75,13 @@ function MeshAttachment.new (name)
|
||||
|
||||
function self:computeWorldVertices (x, y, slot, worldVertices)
|
||||
local bone = slot.bone
|
||||
x,y=slot.bone.skeleton.x,slot.bone.skeleton.y
|
||||
x = x + bone.worldX
|
||||
y = y + bone.worldY
|
||||
local m00, m01, m10, m11 = bone.m00, bone.m01, bone.m10, bone.m11
|
||||
local vertices = self.vertices
|
||||
local verticesCount = vertices.length
|
||||
if #slot.attachmentVertices == verticesCount then vertices = slot.attachmentVertices end
|
||||
local verticesCount = #vertices
|
||||
if slot.attachmentVertices and #slot.attachmentVertices == verticesCount then vertices = slot.attachmentVertices end
|
||||
for i = 1, verticesCount, 2 do
|
||||
local vx = vertices[i]
|
||||
local vy = vertices[i + 1]
|
||||
|
||||
@ -178,9 +178,8 @@ function SkeletonJson.new (attachmentLoader)
|
||||
end
|
||||
if skin.name == "default" then
|
||||
skeletonData.defaultSkin = skin
|
||||
else
|
||||
table.insert(skeletonData.skins, skin)
|
||||
end
|
||||
table.insert(skeletonData.skins, skin)
|
||||
end
|
||||
end
|
||||
|
||||
@ -266,19 +265,21 @@ function SkeletonJson.new (attachmentLoader)
|
||||
return mesh
|
||||
|
||||
elseif type == AttachmentType.skinnedmesh then
|
||||
local mesh = self.attachmentLoader.newSkinnedMeshAttachment(skin, name, path)
|
||||
local mesh = self.attachmentLoader.newSkinningMeshAttachment(skin, name, path)
|
||||
if not mesh then return null end
|
||||
mesh.path = path
|
||||
|
||||
local uvs = getArray(map, "uvs", 1)
|
||||
vertices = getArray(map, "vertices", 1)
|
||||
local vertices = getArray(map, "vertices", 1)
|
||||
local weights = {}
|
||||
local bones = {}
|
||||
for i = 1, vertices do
|
||||
local i, n = 1, #vertices
|
||||
while i < n do
|
||||
local boneCount = vertices[i]
|
||||
i = i + 1
|
||||
table.insert(bones, boneCount)
|
||||
for ii = 1, i + boneCount * 4 do
|
||||
local nn = i + boneCount * 4
|
||||
while i < nn do
|
||||
table.insert(bones, vertices[i])
|
||||
table.insert(weights, vertices[i + 1] * scale)
|
||||
table.insert(weights, vertices[i + 2] * scale)
|
||||
@ -468,22 +469,21 @@ function SkeletonJson.new (attachmentLoader)
|
||||
local ffd = map["ffd"]
|
||||
if ffd then
|
||||
for skinName,slotMap in pairs(ffd) do
|
||||
local skin = skeletonData.findSkin(skinName)
|
||||
local skin = skeletonData:findSkin(skinName)
|
||||
for slotName,meshMap in pairs(slotMap) do
|
||||
local slotIndex = skeletonData.findSlotIndex(slotName)
|
||||
local slotIndex = skeletonData:findSlotIndex(slotName)
|
||||
for meshName,values in pairs(meshMap) do
|
||||
local timeline = Animation.FfdTimeline.new()
|
||||
local attachment = skin:getAttachment(slotIndex, meshName)
|
||||
if not attachment then error("FFD attachment not found: " .. meshName) end
|
||||
timeline.slotIndex = slotIndex
|
||||
timeline.attachment = attachment
|
||||
|
||||
local isMesh = attachment.type == AttachmentType.mesh
|
||||
local vertexCount
|
||||
if isMesh then
|
||||
vertexCount = attachment.vertices.length
|
||||
vertexCount = #attachment.vertices
|
||||
else
|
||||
vertexCount = attachment.weights.length / 3 * 2
|
||||
vertexCount = #attachment.weights / 3 * 2
|
||||
end
|
||||
|
||||
local frameIndex = 0
|
||||
@ -494,12 +494,18 @@ function SkeletonJson.new (attachmentLoader)
|
||||
vertices = attachment.vertices
|
||||
else
|
||||
vertices = {}
|
||||
vertices.length = vertexCount
|
||||
for i = 1, vertexCount do
|
||||
vertices[i] = 0
|
||||
end
|
||||
end
|
||||
else
|
||||
local verticesValue = valueMap["vertices"]
|
||||
local vertices = {}
|
||||
local scale = self.scale
|
||||
vertices = {}
|
||||
local start = valueMap["offset"] or 0
|
||||
for ii = 1, start do
|
||||
vertices[ii] = 0
|
||||
end
|
||||
if scale == 1 then
|
||||
for ii = 1, #verticesValue do
|
||||
vertices[ii + start] = verticesValue[ii]
|
||||
@ -518,7 +524,6 @@ function SkeletonJson.new (attachmentLoader)
|
||||
vertices[vertexCount] = 0
|
||||
end
|
||||
end
|
||||
|
||||
timeline:setFrame(frameIndex, valueMap["time"], vertices)
|
||||
readCurve(timeline, frameIndex, valueMap)
|
||||
frameIndex = frameIndex + 1
|
||||
|
||||
@ -37,7 +37,7 @@ function SkinnedMeshAttachment.new (name)
|
||||
|
||||
local self = {
|
||||
name = name,
|
||||
type = AttachmentType.mesh,
|
||||
type = AttachmentType.skinnedmesh,
|
||||
bones = nil,
|
||||
weights = nil,
|
||||
uvs = nil,
|
||||
@ -47,7 +47,7 @@ function SkinnedMeshAttachment.new (name)
|
||||
r = 1, g = 1, b = 1, a = 1,
|
||||
path = nil,
|
||||
rendererObject = nil,
|
||||
regionU = 0, regionV = 0, regionU2 = 0, regionV2 = 0, regionRotate = false,
|
||||
regionU = 0, regionV = 0, regionU2 = 1, regionV2 = 1, regionRotate = false,
|
||||
regionOffsetX = 0, regionOffsetY = 0,
|
||||
regionWidth = 0, regionHeight = 0,
|
||||
regionOriginalWidth = 0, regionOriginalHeight = 0,
|
||||
@ -75,21 +75,21 @@ function SkinnedMeshAttachment.new (name)
|
||||
end
|
||||
|
||||
function self:computeWorldVertices (x, y, slot, worldVertices)
|
||||
local skeletonBones = slot.skeleton.bones
|
||||
local skeletonBones = slot.bone.skeleton.bones
|
||||
x,y=slot.bone.skeleton.x,slot.bone.skeleton.y
|
||||
local weights = self.weights
|
||||
local bones = self.bones
|
||||
|
||||
local w, v, b, f = 0, 0, 0, 0
|
||||
local n = bones.length
|
||||
local w, v, b, f = 1, 1, 1, 1
|
||||
local n = #bones
|
||||
local wx, wy, bone, vx, vy, weight
|
||||
if #slot.attachmentVertices == 0 then
|
||||
while v < n do
|
||||
if slot.attachmentVerticesCount == 0 then
|
||||
while v <= n do
|
||||
wx = 0
|
||||
wy = 0
|
||||
local nn = bones[v] + v
|
||||
v = v + 1
|
||||
while v <= nn do
|
||||
bone = skeletonBones[bones[v]]
|
||||
bone = skeletonBones[bones[v] + 1]
|
||||
vx = weights[b]
|
||||
vy = weights[b + 1]
|
||||
weight = weights[b + 2]
|
||||
@ -104,13 +104,13 @@ function SkinnedMeshAttachment.new (name)
|
||||
end
|
||||
else
|
||||
local ffd = slot.attachmentVertices
|
||||
while v < n do
|
||||
while v <= n do
|
||||
wx = 0
|
||||
wy = 0
|
||||
local nn = bones[v] + v
|
||||
v = v + 1
|
||||
while v <= nn do
|
||||
bone = skeletonBones[bones[v]]
|
||||
bone = skeletonBones[bones[v] + 1]
|
||||
vx = weights[b] + ffd[f]
|
||||
vy = weights[b + 1] + ffd[f + 1]
|
||||
weight = weights[b + 2]
|
||||
|
||||
@ -41,7 +41,7 @@ public class SkeletonDataAsset : ScriptableObject {
|
||||
public tk2dSpriteCollectionData spriteCollection;
|
||||
#endif
|
||||
public TextAsset skeletonJSON;
|
||||
public float scale = 1;
|
||||
public float scale = 0.01f;
|
||||
public String[] fromAnimation;
|
||||
public String[] toAnimation;
|
||||
public float[] duration;
|
||||
|
||||
@ -50,8 +50,8 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector {
|
||||
m_isPrefab = true;
|
||||
}
|
||||
|
||||
protected override void gui () {
|
||||
base.gui();
|
||||
protected override void DrawInspectorGUI () {
|
||||
base.DrawInspectorGUI();
|
||||
|
||||
SkeletonAnimation component = (SkeletonAnimation)target;
|
||||
if (!component.valid)
|
||||
|
||||
@ -21,8 +21,8 @@ public class SkeletonAnimatorInspector : SkeletonRendererInspector {
|
||||
isPrefab = true;
|
||||
}
|
||||
|
||||
protected override void gui () {
|
||||
base.gui();
|
||||
protected override void DrawInspectorGUI () {
|
||||
base.DrawInspectorGUI();
|
||||
|
||||
EditorGUILayout.PropertyField(layerMixModes, true);
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ public class SkeletonRendererInspector : Editor {
|
||||
sortingLayerIDProperty = rendererSerializedObject.FindProperty("m_SortingLayerID");
|
||||
}
|
||||
|
||||
protected virtual void gui () {
|
||||
protected virtual void DrawInspectorGUI () {
|
||||
SkeletonRenderer component = (SkeletonRenderer)target;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(skeletonDataAsset);
|
||||
@ -134,7 +134,7 @@ public class SkeletonRendererInspector : Editor {
|
||||
EditorGUILayout.PropertyField(submeshSeparators, true);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(meshes,
|
||||
new GUIContent("Render Meshes", "Disable to optimize rendering for skeletons that don't use meshes"));
|
||||
new GUIContent("Render Mesh Attachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"));
|
||||
EditorGUILayout.PropertyField(immutableTriangles,
|
||||
new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
|
||||
EditorGUILayout.Space();
|
||||
@ -154,7 +154,7 @@ public class SkeletonRendererInspector : Editor {
|
||||
|
||||
override public void OnInspectorGUI () {
|
||||
serializedObject.Update();
|
||||
gui();
|
||||
DrawInspectorGUI();
|
||||
if (serializedObject.ApplyModifiedProperties() ||
|
||||
(Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed")
|
||||
) {
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93a799664eb27fd4183aed06719b306c
|
||||
folderAsset: yes
|
||||
timeCreated: 1455486322
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,500 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Spine;
|
||||
using Spine.Unity;
|
||||
|
||||
public class ArraysSubmeshedMeshGenerator : Spine.Unity.ISubmeshedMeshGenerator {
|
||||
|
||||
readonly List<Slot> separators = new List<Slot>();
|
||||
public List<Slot> Separators { get { return this.separators; } }
|
||||
|
||||
public bool generateNormals;
|
||||
public bool generateTangents;
|
||||
|
||||
public float zSpacing = 0f;
|
||||
|
||||
public SubmeshedMeshInstructions GenerateInstructions (Skeleton skeleton) {
|
||||
if (skeleton == null) throw new System.ArgumentNullException("skeleton");
|
||||
|
||||
// Count vertices and submesh triangles.
|
||||
int runningVertexCount = 0;
|
||||
|
||||
int submeshTriangleCount = 0;
|
||||
int submeshFirstVertex = 0;
|
||||
int submeshVertexCount = 0;
|
||||
int submeshStartSlotIndex = 0;
|
||||
Material lastMaterial = null;
|
||||
|
||||
var drawOrder = skeleton.drawOrder;
|
||||
var drawOrderItems = drawOrder.Items;
|
||||
int drawOrderCount = drawOrder.Count;
|
||||
int separatorCount = separators.Count;
|
||||
|
||||
var instructionList = this.currentInstructions.submeshInstructions;
|
||||
instructionList.Clear();
|
||||
|
||||
currentInstructions.attachmentList.Clear();
|
||||
|
||||
for (int i = 0; i < drawOrderCount; i++) {
|
||||
var slot = drawOrderItems[i];
|
||||
var attachment = slot.attachment;
|
||||
|
||||
object rendererObject; // An AtlasRegion in plain Spine-Unity. eventual source of Material object.
|
||||
int attachmentVertexCount, attachmentTriangleCount;
|
||||
|
||||
var regionAttachment = attachment as RegionAttachment;
|
||||
if (regionAttachment != null) {
|
||||
rendererObject = regionAttachment.RendererObject;
|
||||
attachmentVertexCount = 4;
|
||||
attachmentTriangleCount = 6;
|
||||
} else {
|
||||
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 WeightedMeshAttachment;
|
||||
if (skinnedMeshAttachment != null) {
|
||||
rendererObject = skinnedMeshAttachment.RendererObject;
|
||||
attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
|
||||
attachmentTriangleCount = skinnedMeshAttachment.triangles.Length;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var attachmentMaterial = (Material)((AtlasRegion)rendererObject).page.rendererObject;
|
||||
|
||||
// Populate submesh when material changes. (or when forced to separate by a submeshSeparator)
|
||||
if (( runningVertexCount > 0 && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) ||
|
||||
//if (( lastMaterial != null && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) ||
|
||||
( separatorCount > 0 && separators.Contains(slot) )) {
|
||||
|
||||
instructionList.Add(
|
||||
new SubmeshInstructions {
|
||||
skeleton = skeleton,
|
||||
material = lastMaterial,
|
||||
triangleCount = submeshTriangleCount,
|
||||
vertexCount = submeshVertexCount,
|
||||
startSlot = submeshStartSlotIndex,
|
||||
endSlot = i,
|
||||
firstVertexIndex = submeshFirstVertex
|
||||
}
|
||||
);
|
||||
|
||||
// Prepare for next submesh
|
||||
submeshTriangleCount = 0;
|
||||
submeshVertexCount = 0;
|
||||
submeshFirstVertex = runningVertexCount;
|
||||
submeshStartSlotIndex = i;
|
||||
}
|
||||
lastMaterial = attachmentMaterial;
|
||||
|
||||
submeshTriangleCount += attachmentTriangleCount;
|
||||
submeshVertexCount += attachmentVertexCount;
|
||||
runningVertexCount += attachmentVertexCount;
|
||||
|
||||
currentInstructions.attachmentList.Add(attachment);
|
||||
}
|
||||
|
||||
instructionList.Add(
|
||||
new SubmeshInstructions {
|
||||
skeleton = skeleton,
|
||||
material = lastMaterial,
|
||||
triangleCount = submeshTriangleCount,
|
||||
vertexCount = submeshVertexCount,
|
||||
startSlot = submeshStartSlotIndex,
|
||||
endSlot = drawOrderCount,
|
||||
firstVertexIndex = submeshFirstVertex
|
||||
}
|
||||
);
|
||||
|
||||
currentInstructions.vertexCount = runningVertexCount;
|
||||
return currentInstructions;
|
||||
}
|
||||
|
||||
public SubmeshedMesh GenerateMesh (SubmeshedMeshInstructions meshInstructions) {
|
||||
var smartMesh = doubleBufferedSmartMesh.GetNextMesh();
|
||||
var mesh = smartMesh.mesh;
|
||||
|
||||
int submeshCount = meshInstructions.submeshInstructions.Count;
|
||||
|
||||
var instructionList = meshInstructions.submeshInstructions;
|
||||
float zSpacing = this.zSpacing;
|
||||
float[] attVertBuffer = this.attachmentVertexBuffer;
|
||||
Vector2[] uvs = this.meshUVs;
|
||||
Color32[] colors32 = this.meshColors32;
|
||||
Color32 color;
|
||||
|
||||
// Ensure correct buffer sizes.
|
||||
Vector3[] vertices = this.meshVertices;
|
||||
|
||||
bool newVertices = vertices == null || meshInstructions.vertexCount > vertices.Length;
|
||||
int instructionVertexCount = meshInstructions.vertexCount;
|
||||
if (newVertices) {
|
||||
this.meshVertices = vertices = new Vector3[instructionVertexCount];
|
||||
this.meshColors32 = colors32 = new Color32[instructionVertexCount];
|
||||
this.meshUVs = uvs = new Vector2[instructionVertexCount];
|
||||
} else {
|
||||
var zero = Vector3.zero;
|
||||
for (int i = instructionVertexCount, n = this.meshVertices.Length; i < n; i++)
|
||||
vertices[i] = zero;
|
||||
}
|
||||
|
||||
bool newSubmeshBuffers = submeshBuffers.Count < submeshCount;
|
||||
if (newSubmeshBuffers) {
|
||||
submeshBuffers.GrowIfNeeded(submeshCount);
|
||||
for (int i = submeshBuffers.Count; submeshBuffers.Count < submeshCount; i++) {
|
||||
submeshBuffers.Add(new SubmeshTriangleBuffer(instructionList.Items[i].triangleCount));
|
||||
//submeshBuffers.Items[i] = new SubmeshTriangleBuffer(tc);
|
||||
//submeshBuffers.Count = i;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 meshBoundsMin;
|
||||
Vector3 meshBoundsMax;
|
||||
|
||||
int attachmentCount = meshInstructions.attachmentList.Count;
|
||||
|
||||
// Initial values for manual Mesh Bounds calculation
|
||||
if (meshInstructions.attachmentList.Count <= 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 * (attachmentCount - 1);
|
||||
} else {
|
||||
meshBoundsMin.z = zSpacing * (attachmentCount - 1);
|
||||
meshBoundsMax.z = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool structureDoesntMatch = newVertices || newSubmeshBuffers || smartMesh.StructureDoesntMatch(meshInstructions);
|
||||
|
||||
if (structureDoesntMatch) {
|
||||
mesh.Clear();
|
||||
|
||||
if (submeshCount == sharedMaterials.Length)
|
||||
meshInstructions.FillMaterialArray(this.sharedMaterials);
|
||||
else
|
||||
this.sharedMaterials = meshInstructions.GetNewMaterialArray();
|
||||
}
|
||||
|
||||
int vertexIndex = 0;
|
||||
|
||||
// For each submesh, add vertex data from attachments.
|
||||
for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
|
||||
var currentSubmeshInstruction = instructionList.Items[submeshIndex];
|
||||
var skeleton = currentSubmeshInstruction.skeleton;
|
||||
var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
|
||||
float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
|
||||
|
||||
for (int slotIndex = currentSubmeshInstruction.startSlot, endSlot = currentSubmeshInstruction.endSlot; slotIndex < endSlot; slotIndex++) {
|
||||
var slot = skeletonDrawOrderItems[slotIndex];
|
||||
var attachment = slot.attachment;
|
||||
float z = slotIndex * zSpacing;
|
||||
|
||||
var regionAttachment = attachment as RegionAttachment;
|
||||
if (regionAttachment != null) {
|
||||
regionAttachment.ComputeWorldVertices(slot.bone, attVertBuffer);
|
||||
|
||||
float x1 = attVertBuffer[RegionAttachment.X1], y1 = attVertBuffer[RegionAttachment.Y1];
|
||||
float x2 = attVertBuffer[RegionAttachment.X2], y2 = attVertBuffer[RegionAttachment.Y2];
|
||||
float x3 = attVertBuffer[RegionAttachment.X3], y3 = attVertBuffer[RegionAttachment.Y3];
|
||||
float x4 = attVertBuffer[RegionAttachment.X4], y4 = attVertBuffer[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;
|
||||
colors32[vertexIndex] = color; colors32[vertexIndex + 1] = color; colors32[vertexIndex + 2] = color; colors32[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 {
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
int meshVertexCount = meshAttachment.vertices.Length;
|
||||
if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
|
||||
meshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
||||
|
||||
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[] attachmentUVs = meshAttachment.uvs;
|
||||
for (int iii = 0; iii < meshVertexCount; iii += 2) {
|
||||
float x = attVertBuffer[iii], y = attVertBuffer[iii + 1];
|
||||
vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z;
|
||||
colors32[vertexIndex] = color; uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 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;
|
||||
|
||||
vertexIndex++;
|
||||
}
|
||||
} else {
|
||||
var weightedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
if (weightedMeshAttachment != null) {
|
||||
int meshVertexCount = weightedMeshAttachment.uvs.Length;
|
||||
if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
|
||||
weightedMeshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
||||
|
||||
color.a = (byte)(a * slot.a * weightedMeshAttachment.a);
|
||||
color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a);
|
||||
color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a);
|
||||
color.b = (byte)(b * slot.b * weightedMeshAttachment.b * color.a);
|
||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
|
||||
float[] attachmentUVs = weightedMeshAttachment.uvs;
|
||||
for (int iii = 0; iii < meshVertexCount; iii += 2) {
|
||||
float x = attVertBuffer[iii], y = attVertBuffer[iii + 1];
|
||||
vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z;
|
||||
colors32[vertexIndex] = color;
|
||||
uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 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;
|
||||
|
||||
vertexIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push triangles in this submesh
|
||||
if (structureDoesntMatch) {
|
||||
smartMesh.mesh.Clear(); // rebuild triangle array.
|
||||
|
||||
var currentSubmesh = submeshBuffers.Items[submeshIndex];
|
||||
bool isLastSubmesh = (submeshIndex == submeshCount - 1);
|
||||
|
||||
int triangleCount = currentSubmesh.triangleCount = currentSubmeshInstruction.triangleCount;
|
||||
int trianglesCapacity = currentSubmesh.triangles.Length;
|
||||
|
||||
int[] triangles = currentSubmesh.triangles;
|
||||
if (isLastSubmesh) {
|
||||
if (trianglesCapacity > triangleCount) {
|
||||
for (int i = triangleCount; i < trianglesCapacity; i++)
|
||||
triangles[i] = 0;
|
||||
}
|
||||
} else if (trianglesCapacity != triangleCount) {
|
||||
triangles = currentSubmesh.triangles = new int[triangleCount];
|
||||
currentSubmesh.triangleCount = 0;
|
||||
}
|
||||
|
||||
// Iterate through submesh slots and store the triangles.
|
||||
int triangleIndex = 0;
|
||||
int afv = currentSubmeshInstruction.firstVertexIndex; // attachment first vertex
|
||||
|
||||
for (int i = currentSubmeshInstruction.startSlot, n = currentSubmeshInstruction.endSlot; i < n; i++) {
|
||||
var attachment = skeletonDrawOrderItems[i].attachment;
|
||||
|
||||
if (attachment is RegionAttachment) {
|
||||
triangles[triangleIndex] = afv; triangles[triangleIndex + 1] = afv + 2; triangles[triangleIndex + 2] = afv + 1;
|
||||
triangles[triangleIndex + 3] = afv + 2; triangles[triangleIndex + 4] = afv + 3; triangles[triangleIndex + 5] = afv + 1;
|
||||
|
||||
triangleIndex += 6;
|
||||
afv += 4;
|
||||
} else {
|
||||
int[] attachmentTriangles;
|
||||
int attachmentVertexCount;
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2
|
||||
attachmentTriangles = meshAttachment.triangles;
|
||||
} else {
|
||||
var weightedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
if (weightedMeshAttachment != null) {
|
||||
attachmentVertexCount = weightedMeshAttachment.uvs.Length >> 1; // length/2
|
||||
attachmentTriangles = weightedMeshAttachment.triangles;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
|
||||
triangles[triangleIndex] = afv + attachmentTriangles[ii];
|
||||
|
||||
afv += attachmentVertexCount;
|
||||
}
|
||||
} // Done adding current submesh triangles
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin);
|
||||
Vector3 meshCenter = meshBoundsMin + meshBoundsExtents * 0.5f;
|
||||
|
||||
smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, meshInstructions);
|
||||
mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
|
||||
|
||||
if (structureDoesntMatch) {
|
||||
|
||||
if (generateNormals) {
|
||||
int vertexCount = meshInstructions.vertexCount;
|
||||
Vector3[] normals = new Vector3[vertexCount];
|
||||
Vector3 normal = new Vector3(0, 0, -1);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
normals[i] = normal;
|
||||
mesh.normals = normals;
|
||||
|
||||
if (generateTangents) {
|
||||
Vector4[] tangents = new Vector4[vertexCount];
|
||||
Vector4 tangent = new Vector4(1, 0, 0, -1);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
tangents[i] = tangent;
|
||||
mesh.tangents = tangents;
|
||||
}
|
||||
}
|
||||
|
||||
// push new triangles if doesn't match.
|
||||
mesh.subMeshCount = submeshCount;
|
||||
for (int i = 0; i < submeshCount; i++)
|
||||
mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);
|
||||
}
|
||||
|
||||
|
||||
return new SubmeshedMesh(smartMesh.mesh, sharedMaterials);
|
||||
}
|
||||
|
||||
#region Internals
|
||||
readonly DoubleBufferedSmartMesh doubleBufferedSmartMesh = new DoubleBufferedSmartMesh();
|
||||
readonly SubmeshedMeshInstructions currentInstructions = new SubmeshedMeshInstructions();
|
||||
|
||||
float[] attachmentVertexBuffer = new float[8];
|
||||
Vector3[] meshVertices;
|
||||
Color32[] meshColors32;
|
||||
Vector2[] meshUVs;
|
||||
Material[] sharedMaterials = new Material[0];
|
||||
readonly ExposedList<SubmeshTriangleBuffer> submeshBuffers = new ExposedList<SubmeshTriangleBuffer>();
|
||||
#endregion
|
||||
|
||||
#region Types
|
||||
class SubmeshTriangleBuffer {
|
||||
public int[] triangles;
|
||||
public int triangleCount;
|
||||
|
||||
public SubmeshTriangleBuffer (int triangleCount) {
|
||||
triangles = new int[triangleCount];
|
||||
this.triangleCount = triangleCount;
|
||||
}
|
||||
}
|
||||
|
||||
class DoubleBufferedSmartMesh {
|
||||
readonly SmartMesh mesh1 = new SmartMesh();
|
||||
readonly SmartMesh mesh2 = new SmartMesh();
|
||||
bool usingMesh1;
|
||||
|
||||
public SmartMesh GetNextMesh () {
|
||||
usingMesh1 = !usingMesh1;
|
||||
return usingMesh1 ? mesh1 : mesh2;
|
||||
}
|
||||
}
|
||||
|
||||
// A SmartMesh is a Mesh (with submeshes) that knows what attachments and instructions were used to generate it.
|
||||
class SmartMesh {
|
||||
public readonly Mesh mesh = SpineMesh.NewMesh();
|
||||
readonly ExposedList<Attachment> attachmentsUsed = new ExposedList<Attachment>();
|
||||
readonly ExposedList<SubmeshInstructions> instructionsUsed = new ExposedList<SubmeshInstructions>();
|
||||
|
||||
public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, SubmeshedMeshInstructions instructions) {
|
||||
mesh.vertices = verts;
|
||||
mesh.uv = uvs;
|
||||
mesh.colors32 = colors;
|
||||
|
||||
attachmentsUsed.Clear();
|
||||
attachmentsUsed.GrowIfNeeded(instructions.attachmentList.Capacity);
|
||||
attachmentsUsed.Count = instructions.attachmentList.Count;
|
||||
instructions.attachmentList.CopyTo(attachmentsUsed.Items);
|
||||
|
||||
instructionsUsed.Clear();
|
||||
instructionsUsed.GrowIfNeeded(instructions.submeshInstructions.Capacity);
|
||||
instructionsUsed.Count = instructions.submeshInstructions.Count;
|
||||
instructions.submeshInstructions.CopyTo(instructionsUsed.Items);
|
||||
}
|
||||
|
||||
public bool StructureDoesntMatch (SubmeshedMeshInstructions instructions) {
|
||||
// Check count inequality.
|
||||
if (instructions.attachmentList.Count != this.attachmentsUsed.Count) return true;
|
||||
if (instructions.submeshInstructions.Count != this.instructionsUsed.Count) return true;
|
||||
|
||||
//Debug.Log("broadphase matched");
|
||||
|
||||
// Check each attachment.
|
||||
var attachmentsPassed = instructions.attachmentList.Items;
|
||||
var myAttachments = this.attachmentsUsed.Items;
|
||||
for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
|
||||
if (attachmentsPassed[i] != myAttachments[i]) return true;
|
||||
|
||||
//Debug.Log("attachments matched");
|
||||
|
||||
// Check each submesh for equal arrangement.
|
||||
var instructionListItems = instructions.submeshInstructions.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.skeleton != rhs.skeleton ||
|
||||
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;
|
||||
}
|
||||
|
||||
//Debug.Log("structure matched");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 412898b02d5caaf489b3ffb29e4ae1c0
|
||||
timeCreated: 1455407003
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,69 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spine.Unity {
|
||||
public interface ISubmeshedMeshGenerator {
|
||||
/// <summary>Generates instructions for how to generate the submeshed mesh based on the given state of the
|
||||
/// skeleton. The returned instructions can be used to generate a whole submeshed mesh or individual submeshes.</summary>
|
||||
SubmeshedMeshInstructions GenerateInstructions (Skeleton skeleton);
|
||||
|
||||
/// <summary>Returns a SubmeshedMesh (a mesh and a material array coupled in a struct).
|
||||
/// Call GenerateInstructions to get the SubmeshedMeshInstructions to pass into this.</summary>
|
||||
SubmeshedMesh GenerateMesh (SubmeshedMeshInstructions wholeMeshInstruction);
|
||||
|
||||
/// <summary>A list of slots that mark the end of a submesh. The slot after it will be the start of a new submesh.</summary>
|
||||
List<Slot> Separators { get; }
|
||||
}
|
||||
|
||||
public interface ISingleSubmeshGenerator {
|
||||
void FillMesh (SubmeshInstructions instructions, Mesh meshToFill);
|
||||
}
|
||||
|
||||
/// <summary>A Submeshed mesh is a return type so the mesh with
|
||||
/// multiple submeshes can be coupled with a material array to render its submeshes.</summary>
|
||||
public struct SubmeshedMesh {
|
||||
public readonly Mesh mesh;
|
||||
public readonly Material[] materials;
|
||||
public SubmeshedMesh (Mesh mesh, Material[] materials) {
|
||||
this.mesh = mesh;
|
||||
this.materials = materials;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Primarily a collection of Submesh Instructions. This constitutes instructions for how to construct a mesh containing submeshes.</summary>
|
||||
public class SubmeshedMeshInstructions {
|
||||
public readonly ExposedList<SubmeshInstructions> submeshInstructions = new ExposedList<SubmeshInstructions>();
|
||||
public readonly ExposedList<Attachment> attachmentList = new ExposedList<Attachment>();
|
||||
public int vertexCount = -1;
|
||||
|
||||
/// <summary>Allocates a new material array to render this mesh and its constituent submeshes.</summary>
|
||||
public Material[] GetNewMaterialArray () {
|
||||
var materials = new Material[submeshInstructions.Count];
|
||||
FillMaterialArray(materials);
|
||||
return materials;
|
||||
}
|
||||
|
||||
/// <summary>Fills a given array with the materials needed to render this submeshed mesh.</summary>
|
||||
public void FillMaterialArray (Material[] materialArray) {
|
||||
var instructionsItems = submeshInstructions.Items;
|
||||
for (int i = 0, n = materialArray.Length; i < n; i++)
|
||||
materialArray[i] = instructionsItems[i].material;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Instructions for how to generate a mesh or submesh out of a range of slots in a given skeleton.</summary>
|
||||
public struct SubmeshInstructions {
|
||||
public Skeleton skeleton;
|
||||
public int startSlot;
|
||||
public int endSlot;
|
||||
|
||||
// Cached values because they are determined in the process of generating instructions,
|
||||
// but could otherwise be pulled from accessing attachments, checking materials and counting tris and verts.
|
||||
public Material material;
|
||||
public int triangleCount;
|
||||
public int vertexCount;
|
||||
|
||||
// Vertex index offset. Used by submesh generation if part of a bigger mesh.
|
||||
public int firstVertexIndex;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c518552ea622e98418ce673c5febc468
|
||||
timeCreated: 1455406760
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -13,7 +13,7 @@ public class SkeletonGraphicInspector : Editor {
|
||||
SerializedProperty material_, color_;
|
||||
SerializedProperty skeletonDataAsset_, initialSkinName_;
|
||||
SerializedProperty startingAnimation_, startingLoop_, timeScale_, freeze_;
|
||||
#if !PREUNITY_5_2
|
||||
#if !PREUNITY_5_2
|
||||
SerializedProperty raycastTarget_;
|
||||
|
||||
SkeletonGraphic thisSkeletonGraphic;
|
||||
@ -38,17 +38,12 @@ public class SkeletonGraphicInspector : Editor {
|
||||
freeze_ = so.FindProperty("freeze");
|
||||
}
|
||||
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
|
||||
var s = thisSkeletonGraphic;
|
||||
s.skeletonDataAsset = SkeletonGraphicInspector.ObjectField<SkeletonDataAsset>(skeletonDataAsset_);
|
||||
s.material = SkeletonGraphicInspector.ObjectField<Material>(material_);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
thisSkeletonGraphic.color = EditorGUILayout.ColorField(color_.displayName, color_.colorValue);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
SkeletonGraphicInspector.ForceUpdateHack(thisSkeletonGraphic.transform);
|
||||
|
||||
EditorGUILayout.PropertyField(skeletonDataAsset_);
|
||||
EditorGUILayout.PropertyField(material_);
|
||||
EditorGUILayout.PropertyField(color_);
|
||||
|
||||
if (thisSkeletonGraphic.skeletonDataAsset == null) {
|
||||
EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info);
|
||||
@ -62,49 +57,34 @@ public class SkeletonGraphicInspector : Editor {
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(startingAnimation_);
|
||||
s.startingLoop = SkeletonGraphicInspector.BoolField(startingLoop_);
|
||||
s.timeScale = EditorGUILayout.FloatField(timeScale_.displayName, timeScale_.floatValue);
|
||||
EditorGUILayout.PropertyField(startingLoop_);
|
||||
EditorGUILayout.PropertyField(timeScale_);
|
||||
EditorGUILayout.Space();
|
||||
s.freeze = SkeletonGraphicInspector.BoolField(freeze_);
|
||||
EditorGUILayout.PropertyField(freeze_);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("UI", EditorStyles.boldLabel);
|
||||
s.raycastTarget = SkeletonGraphicInspector.BoolField(raycastTarget_);
|
||||
EditorGUILayout.PropertyField(raycastTarget_);
|
||||
|
||||
}
|
||||
bool wasChanged = EditorGUI.EndChangeCheck();
|
||||
|
||||
#region HAX - Thanks, Unity
|
||||
// colors weren't updating in realtime in the custom inspector.
|
||||
// Why the hell do I have to do this??
|
||||
/// <summary>Use this when scene repaint and proper explicit update methods don't work.</summary>
|
||||
public static void ForceUpdateHack (Transform t) {
|
||||
var origValue = t.localScale;
|
||||
t.localScale = new Vector3(11f, 22f, 33f);
|
||||
t.localScale = origValue;
|
||||
if (wasChanged) {
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
// Hack for Unity 5.3 problem with PropertyField
|
||||
public static T ObjectField<T> (SerializedProperty property) where T : UnityEngine.Object {
|
||||
return (T)EditorGUILayout.ObjectField(property.displayName, property.objectReferenceValue, typeof(T), false);
|
||||
}
|
||||
|
||||
public static bool BoolField (SerializedProperty property) {
|
||||
return EditorGUILayout.Toggle(property.displayName, property.boolValue);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Menus
|
||||
[MenuItem ("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")]
|
||||
[MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")]
|
||||
static void MatchRectTransformWithBounds (MenuCommand command) {
|
||||
var skeletonGraphic = (SkeletonGraphic)command.context;
|
||||
var mesh = skeletonGraphic.SpineMeshGenerator.LastGeneratedMesh;
|
||||
var mesh = skeletonGraphic.SpineMeshGenerator.LastGeneratedMesh;
|
||||
|
||||
var bounds = mesh.bounds;
|
||||
var size = bounds.size;
|
||||
var center = bounds.center;
|
||||
var p = new Vector2(
|
||||
0.5f - (center.x / size.x),
|
||||
0.5f - (center.y / size.y)
|
||||
);
|
||||
0.5f - (center.x / size.x),
|
||||
0.5f - (center.y / size.y)
|
||||
);
|
||||
|
||||
skeletonGraphic.rectTransform.sizeDelta = size;
|
||||
skeletonGraphic.rectTransform.pivot = p;
|
||||
@ -112,8 +92,12 @@ public class SkeletonGraphicInspector : Editor {
|
||||
|
||||
public static Material DefaultSkeletonGraphicMaterial {
|
||||
get {
|
||||
var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material"); if (guids.Length <= 0) return null;
|
||||
var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]); if (string.IsNullOrEmpty(firstAssetPath)) return null;
|
||||
var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material");
|
||||
if (guids.Length <= 0)
|
||||
return null;
|
||||
var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
if (string.IsNullOrEmpty(firstAssetPath))
|
||||
return null;
|
||||
var firstMaterial = AssetDatabase.LoadAssetAtPath<Material>(firstAssetPath);
|
||||
return firstMaterial;
|
||||
}
|
||||
@ -204,6 +188,8 @@ public class SkeletonGraphicInspector : Editor {
|
||||
graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial;
|
||||
return go;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -89,6 +89,10 @@ public class SkeletonGraphic : MaskableGraphic {
|
||||
|
||||
protected override void Reset () {
|
||||
base.Reset();
|
||||
if (canvas == null) {
|
||||
Debug.LogWarningFormat("SkeletonGraphic requires a Canvas to be visible. Move this GameObject ({0}) in the Hierarchy so it becomes a child of a Canvas.", gameObject.name);
|
||||
}
|
||||
|
||||
if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic (Premultiply Alpha)")) {
|
||||
Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material.");
|
||||
}
|
||||
|
||||
@ -54,6 +54,10 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
// Submesh Separation
|
||||
[SpineSlot] public string[] submeshSeparators = new string[0];
|
||||
[HideInInspector] public List<Slot> submeshSeparatorSlots = new List<Slot>();
|
||||
|
||||
// Custom Slot Material
|
||||
[System.NonSerialized] private readonly Dictionary<Slot, Material> customSlotMaterials = new Dictionary<Slot, Material>();
|
||||
public Dictionary<Slot, Material> CustomSlotMaterials { get { return customSlotMaterials; } }
|
||||
#endregion
|
||||
|
||||
[System.NonSerialized] public bool valid;
|
||||
@ -207,6 +211,8 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
drawOrder.Count != storedAttachments.Count || // Number of slots changed (when does this happen?)
|
||||
immutableTriangles != storedState.immutableTriangles; // Immutable Triangles flag changed.
|
||||
|
||||
bool isCustomMaterialsPopulated = customSlotMaterials.Count > 0;
|
||||
|
||||
for (int i = 0; i < drawOrderCount; i++) {
|
||||
Slot slot = drawOrderItems[i];
|
||||
Bone bone = slot.bone;
|
||||
@ -252,13 +258,22 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
}
|
||||
|
||||
#if !SPINE_TK2D
|
||||
Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
|
||||
// Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; // For no customSlotMaterials
|
||||
|
||||
Material material;
|
||||
if (isCustomMaterialsPopulated) {
|
||||
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
|
||||
|
||||
// Populate submesh when material changes. (or when forced to separate by a submeshSeparator)
|
||||
if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) ||
|
||||
if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) ||
|
||||
(submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) {
|
||||
|
||||
workingSubmeshArguments.Add(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user