Merge branch 'master' into spine-ue4

This commit is contained in:
badlogic 2016-12-01 11:51:39 +01:00
commit 6d84a04de9
21 changed files with 1672 additions and 1457 deletions

View File

@ -126,7 +126,7 @@ public class Atlas {
region.splits = new Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3]));
if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits
region.pads = Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3]));
region.pads = new Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3]));
reader.readTuple(tuple);
}

View File

@ -179,4 +179,44 @@ void MemoryTestFixture::reproduceIssue_777()
DisposeAll(skeleton, state, stateData, skeletonData, atlas);
}
spSkeleton* skeleton = nullptr;
static void spineAnimStateHandler(spAnimationState* state, int type, spTrackEntry* entry, spEvent* event)
{
if (type == SP_ANIMATION_COMPLETE)
{
spAnimationState_setAnimationByName(state, 0, "walk", false);
spAnimationState_update(state, 0);
spAnimationState_apply(state, skeleton);
}
}
void MemoryTestFixture::reproduceIssue_Loop()
{
spAtlas* atlas = nullptr;
spSkeletonData* skeletonData = nullptr;
spAnimationStateData* stateData = nullptr;
spAnimationState* state = nullptr;
//////////////////////////////////////////////////////////////////////////
// Initialize Animations
LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
///////////////////////////////////////////////////////////////////////////
if (state)
state->listener = (spAnimationStateListener)&spineAnimStateHandler;
spAnimationState_setAnimationByName(state, 0, "walk", false);
// run normal update
for (int i = 0; i < 50; ++i) {
const float timeSlice = 1.0f / 60.0f;
spSkeleton_update(skeleton, timeSlice);
spAnimationState_update(state, timeSlice);
spAnimationState_apply(state, skeleton);
}
DisposeAll(skeleton, state, stateData, skeletonData, atlas);
}

View File

@ -17,6 +17,7 @@ public:
// Comment out here to disable individual test cases
TEST_CASE(reproduceIssue_776);
TEST_CASE(reproduceIssue_777);
TEST_CASE(reproduceIssue_Loop);
initialize();
}
@ -29,6 +30,7 @@ public:
public:
void reproduceIssue_776();
void reproduceIssue_777();
void reproduceIssue_Loop(); // http://esotericsoftware.com/forum/spine-c-3-5-animation-jerking-7451
//////////////////////////////////////////////////////////////////////////
// test fixture setup

View File

@ -216,6 +216,9 @@ public class SkeletonViewer extends ApplicationAdapter {
if (ui.skinList.getSelected() != null) skeleton.setSkin(ui.skinList.getSelected());
setAnimation();
// ui.animationList.clearListeners();
// state.setAnimation(0, "walk", true);
}
void setAnimation () {

View File

@ -75,8 +75,8 @@ setup a development environment, follow these steps.
* **WebGL**: `tsc -w -p tsconfig.webgl.json`, builds `core/src` and `webgl/src`, outputs `build/spine-webgl.js|d.ts|js.map`
* **Canvas**: `tsc -w -p tsconfig.canvas.json`, builds `core/src` and `canvas/src`, outputs `build/spine-canvas.js|d.ts|js.map`
* **THREE.JS**: `tsc -w -p tsconfig.threejs.json`, builds `core/src` and `threejs/src`, outputs `build/spine-threejs.js|d.ts|js.map`
* **Widget**: `tsc -w -p tsconfig.widget.json`, builds `core/src` and `widget/src`, outputs `build/spine-widget.js|d.ts|js.map`
6. Open the `spine-ts` folder in Visual Studio Code. VS Code will use the `tsconfig.json` file all source files from core and all
* **Widget**: `tsc -w -p tsconfig.widget.json`, builds `core/src` and `widget/src`, outputs `build/spine-widget.js|d.ts|js.map`
6. Open the `spine-ts` folder in Visual Studio Code. VS Code will use the `tsconfig.json` file all source files from core and all
backends for your development pleasure. The actual JavaScript output is still created by the command line TypeScript compiler process from the previous step.
Each backend contains an `example/` folder with an `index.html` file that demonstrates the respective backend. For development, we
@ -90,7 +90,7 @@ python -m SimpleHTTPServer
Then navigate to `http://localhost:8000/webgl/example`, `http://localhost:8000/canvas/example`, `http://localhost:8000/threejs/example` or `http://localhost:8000/widget/example`
### Using the Widget
To easily display Spine animations on your website, you can use the spine-ts Widget backend.
To easily display Spine animations on your website, you can use the spine-ts Widget backend.
1. Export your Spine animation with a texture atlas and put the resulting `.json`, `.atlas` and `.png` files on your server.
2. Copy the `build/spine-widget.js` file to your server and include it on your website `<script src="spine-widget.js"></script>`, adjusting the src to match the location of the file on your server.
@ -114,7 +114,7 @@ To specify the configuration of a Spine Widget via HTML, you can use these HTML
* `data-fit-to-canvas`: optional, whether to fit the animation to the canvas size or not. Defaults to `true` if omitted, in which case `data-scale`, `data-x` and `data-y` are irrelevant. This setting calculates the setup pose bounding box using the specified skin to center and scale the animation on the canvas.
* `data-background-color`: optional, the background color to use. Defaults to `#000000` if omitted.
* `data-premultiplied-alpha`: optional, whether the atlas pages use premultiplied alpha or not. Defaults to `false` if omitted.
* `data-debug`: optional, whether to show debug information such as bones, attachments, etc. Defaults to `false` if omitted.
* `data-debug`: optional, whether to show debug information such as bones, attachments, etc. Defaults to `false` if omitted.
You can specify these as attribuets on the HTML element like this:
@ -139,12 +139,12 @@ Then create a new `spine.SpineWidget`, providing a [`SpineWidgetConfiguration`](
```JavaScript
new spine.SpineWidget("my-widget", {
json: "assets/spineboy.json",
atlas: "assets/spineboy.atlas",
animation: "run",
atlas: "assets/spineboy.atlas",
animation: "run",
backgroundColor: "#000000",
success: function (widget) {
var animIndex = 0;
widget.canvas.onclick = function () {
widget.canvas.onclick = function () {
animIndex++;
let animations = widget.skeleton.data.animations;
if (animIndex >= animations.length) animIndex = 0;
@ -160,6 +160,7 @@ The configuration object has the following fields:
* `atlas`: required, path to the `.atlas` file, absolute or relative, e.g. "assets/animation.atlas"
* `animation`: required, the name of the animation to play back
* `imagesPath`: optional, the location of images on the server to load atlas pages from. If omitted, atlas `.png` page files are loaded relative to the `.atlas` file.
* `atlasPages`: optional, the list of atlas page images, e.g. `atlasPages: ["assets/page1.png", "assets/page2.png"]` when using code, or `data-atlas-pages="assets/page1.png,assets/page2.png"` on case of HTML instantiation. Use this if you have a multi-page atlas. If ommited, only one atlas page image is loaded based on the atlas file name, replacing `.atlas` with `.png`.
* `skin`: optional, the name of the skin to use. Defaults to `default` if omitted.
* `loop`: optional, whether to loop the animation or not. Defaults to `true` if omitted.
* `scale`: optional, the scaling factor to apply when loading the `.json` file. Defaults to `1` if omitted. Irrelevant if `data-fit-to-canavs` is `true`.

View File

@ -1543,6 +1543,7 @@ declare module spine {
atlas: string;
animation: string;
imagesPath: string;
atlasPages: string[];
skin: string;
loop: boolean;
scale: number;

View File

@ -7899,7 +7899,14 @@ var spine;
var assets = this.assetManager = new spine.webgl.AssetManager(gl);
assets.loadText(config.atlas);
assets.loadText(config.json);
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
if (config.atlasPages == null) {
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
}
else {
for (var i = 0; i < config.atlasPages.length; i++) {
assets.loadTexture(config.atlasPages[i]);
}
}
requestAnimationFrame(function () { _this.load(); });
}
SpineWidget.prototype.validateConfig = function (config) {
@ -8082,6 +8089,8 @@ var spine;
config.animation = widget.getAttribute("data-animation");
if (widget.getAttribute("data-images-path"))
config.imagesPath = widget.getAttribute("data-images-path");
if (widget.getAttribute("data-atlas-pages"))
config.atlasPages = widget.getAttribute("data-atlas-pages").split(",");
if (widget.getAttribute("data-skin"))
config.skin = widget.getAttribute("data-skin");
if (widget.getAttribute("data-loop"))

File diff suppressed because one or more lines are too long

View File

@ -1473,6 +1473,7 @@ declare module spine {
atlas: string;
animation: string;
imagesPath: string;
atlasPages: string[];
skin: string;
loop: boolean;
scale: number;

View File

@ -7478,7 +7478,14 @@ var spine;
var assets = this.assetManager = new spine.webgl.AssetManager(gl);
assets.loadText(config.atlas);
assets.loadText(config.json);
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
if (config.atlasPages == null) {
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
}
else {
for (var i = 0; i < config.atlasPages.length; i++) {
assets.loadTexture(config.atlasPages[i]);
}
}
requestAnimationFrame(function () { _this.load(); });
}
SpineWidget.prototype.validateConfig = function (config) {
@ -7661,6 +7668,8 @@ var spine;
config.animation = widget.getAttribute("data-animation");
if (widget.getAttribute("data-images-path"))
config.imagesPath = widget.getAttribute("data-images-path");
if (widget.getAttribute("data-atlas-pages"))
config.atlasPages = widget.getAttribute("data-atlas-pages").split(",");
if (widget.getAttribute("data-skin"))
config.skin = widget.getAttribute("data-skin");
if (widget.getAttribute("data-loop"))

File diff suppressed because one or more lines are too long

View File

@ -80,7 +80,13 @@ module spine {
let assets = this.assetManager = new spine.webgl.AssetManager(gl);
assets.loadText(config.atlas);
assets.loadText(config.json);
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
if (config.atlasPages == null) {
assets.loadTexture(config.atlas.replace(".atlas", ".png"));
} else {
for (let i = 0; i < config.atlasPages.length; i++) {
assets.loadTexture(config.atlasPages[i]);
}
}
requestAnimationFrame(() => { this.load(); });
}
@ -264,6 +270,7 @@ module spine {
config.json = widget.getAttribute("data-json");
config.animation = widget.getAttribute("data-animation");
if (widget.getAttribute("data-images-path")) config.imagesPath = widget.getAttribute("data-images-path");
if (widget.getAttribute("data-atlas-pages")) config.atlasPages = widget.getAttribute("data-atlas-pages").split(",");
if (widget.getAttribute("data-skin")) config.skin = widget.getAttribute("data-skin");
if (widget.getAttribute("data-loop")) config.loop = widget.getAttribute("data-loop") === "true";
if (widget.getAttribute("data-scale")) config.scale = parseFloat(widget.getAttribute("data-scale"));
@ -303,6 +310,7 @@ module spine {
atlas: string;
animation: string;
imagesPath: string;
atlasPages: string[];
skin = "default";
loop = true;
scale = 1.0;

View File

@ -233,7 +233,7 @@ MonoBehaviour:
skeletonDataAsset: {fileID: 11400000, guid: 44691b56ed7d1f04da0cbc2a52a91b8d, type: 2}
initialSkinName: default
separatorSlotNames:
- front_foot
- --B
zSpacing: 0
renderMeshes: 1
immutableTriangles: 0
@ -402,7 +402,7 @@ Transform:
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 565117361}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -1.3629907, y: 3.7230203, z: 0}
m_LocalPosition: {x: -1.3626752, y: 3.7231483, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_Children: []
@ -2989,7 +2989,7 @@ MonoBehaviour:
skeletonDataAsset: {fileID: 11400000, guid: 44691b56ed7d1f04da0cbc2a52a91b8d, type: 2}
initialSkinName: default
separatorSlotNames:
- front_upper_arm
- --A
zSpacing: 0
renderMeshes: 1
immutableTriangles: 0

File diff suppressed because it is too large Load Diff

View File

@ -8,21 +8,123 @@ Material:
m_PrefabInternal: {fileID: 0}
m_Name: spineboy_Material
m_Shader: {fileID: 4800000, guid: 1e8a610c9e01c3648bac42585e5fc676, type: 3}
m_ShaderKeywords:
m_LightmapFlags: 5
m_ShaderKeywords: _EMISSION
m_LightmapFlags: 1
m_CustomRenderQueue: -1
stringTagMap: {}
m_SavedProperties:
serializedVersion: 2
m_TexEnvs:
- first:
name: _BumpMap
second:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- first:
name: _DetailAlbedoMap
second:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- first:
name: _DetailMask
second:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- first:
name: _DetailNormalMap
second:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- first:
name: _EmissionMap
second:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- first:
name: _MainTex
second:
m_Texture: {fileID: 2800000, guid: 49bb65eefe08e424bbf7a38bc98ec638, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- first:
name: _MetallicGlossMap
second:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- first:
name: _OcclusionMap
second:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- first:
name: _ParallaxMap
second:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- first:
name: _BumpScale
second: 1
- first:
name: _Cutoff
second: 0.1
m_Colors: []
- first:
name: _DetailNormalMapScale
second: 1
- first:
name: _DstBlend
second: 0
- first:
name: _GlossMapScale
second: 1
- first:
name: _Glossiness
second: 0.5
- first:
name: _GlossyReflections
second: 1
- first:
name: _Metallic
second: 0
- first:
name: _Mode
second: 0
- first:
name: _OcclusionStrength
second: 1
- first:
name: _Parallax
second: 0.02
- first:
name: _SmoothnessTextureChannel
second: 0
- first:
name: _SpecularHighlights
second: 1
- first:
name: _SrcBlend
second: 1
- first:
name: _UVSec
second: 0
- first:
name: _ZWrite
second: 1
m_Colors:
- first:
name: _Black
second: {r: 0, g: 0, b: 0, a: 0}
- first:
name: _Color
second: {r: 1, g: 1, b: 1, a: 1}
- first:
name: _EmissionColor
second: {r: 0, g: 0, b: 0, a: 1}

View File

@ -12,7 +12,7 @@ Material:
m_LightmapFlags: 5
m_CustomRenderQueue: 3000
stringTagMap:
RenderType: Transparent
RenderType: Sprite
m_SavedProperties:
serializedVersion: 2
m_TexEnvs:

View File

@ -55,7 +55,7 @@ namespace Spine.Unity.Modules {
MeshRenderer mainMeshRenderer;
public bool copyPropertyBlock = false;
[Tooltip("Copies MeshRenderer flags into ")]
[Tooltip("Copies MeshRenderer flags into each parts renderer")]
public bool copyMeshRendererFlags = false;
public List<Spine.Unity.Modules.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
@ -125,7 +125,7 @@ namespace Spine.Unity.Modules {
int rendererCount = partsRenderers.Count;
if (rendererCount <= 0) return;
int rendererIndex = 0;
if (copyPropertyBlock)
mainMeshRenderer.GetPropertyBlock(copiedBlock);
@ -134,11 +134,13 @@ namespace Spine.Unity.Modules {
var submeshInstructionsItems = submeshInstructions.Items;
int lastSubmeshInstruction = submeshInstructions.Count - 1;
var currentRenderer = partsRenderers[rendererIndex];
bool addNormals = skeletonRenderer.calculateNormals;
bool addTangents = skeletonRenderer.calculateTangents;
bool pmaVertexColors = skeletonRenderer.pmaVertexColors;
int rendererIndex = 0;
var currentRenderer = partsRenderers[rendererIndex];
for (int si = 0, start = 0; si <= lastSubmeshInstruction; si++) {
if (submeshInstructionsItems[si].forceSeparate || si == lastSubmeshInstruction) {
// Apply properties

View File

@ -267,6 +267,7 @@ namespace Spine.Unity {
bool isCustomSlotMaterialsPopulated = customSlotMaterials.Count > 0;
#endif
bool hasSeparators = separatorSlots.Count > 0;
int vertexCount = 0;
int submeshVertexCount = 0;
int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
@ -281,8 +282,9 @@ namespace Spine.Unity {
workingFlipsItems[i] = flip;
#endif
object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
object rendererObject = null; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
int attachmentVertexCount, attachmentTriangleCount;
bool noRender = false;
var regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) {
@ -290,56 +292,85 @@ namespace Spine.Unity {
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;
if (!renderMeshes) {
noRender = true;
attachmentVertexCount = 0;
attachmentTriangleCount = 0;
//continue;
} else {
continue;
var meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
rendererObject = meshAttachment.RendererObject;
attachmentVertexCount = meshAttachment.worldVerticesLength >> 1;
attachmentTriangleCount = meshAttachment.triangles.Length;
} else {
noRender = true;
attachmentVertexCount = 0;
attachmentTriangleCount = 0;
//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;
// Slot with a separator/new material will become the starting slot of the next new instruction.
bool forceSeparate = (hasSeparators && separatorSlots.Contains(slot));
if (noRender) {
if (forceSeparate && vertexCount > 0 && this.generateMeshOverride != null) {
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;
}
} else {
#if !SPINE_TK2D
Material material;
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
if (vertexCount > 0 && (forceSeparate || lastMaterial.GetInstanceID() != material.GetInstanceID())) {
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;
}
// Update state for the next iteration.
lastMaterial = material;
submeshTriangleCount += attachmentTriangleCount;
vertexCount += attachmentVertexCount;
submeshVertexCount += attachmentVertexCount;
}
if (submeshVertexCount != 0) {