From e3cdea94a092f6ca77f36ed94832a26da5c8d6a7 Mon Sep 17 00:00:00 2001 From: badlogic Date: Sun, 11 Sep 2016 13:12:26 +0200 Subject: [PATCH 1/4] [c] Closes #696, leak in spine-c binary loader --- spine-c/src/spine/SkeletonBinary.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/spine-c/src/spine/SkeletonBinary.c b/spine-c/src/spine/SkeletonBinary.c index 5a0485ac0..06eb15fa6 100644 --- a/spine-c/src/spine/SkeletonBinary.c +++ b/spine-c/src/spine/SkeletonBinary.c @@ -656,7 +656,11 @@ spAttachment* spSkeletonBinary_readAttachment(spSkeletonBinary* self, _dataInput int i; spAttachmentType type; const char* name = readString(input); - if (!name) MALLOC_STR(name, attachmentName); + int freeName = name != 0; + if (!name) { + freeName = 0; + MALLOC_STR(name, attachmentName); + } type = (spAttachmentType)readByte(input); @@ -680,6 +684,7 @@ spAttachment* spSkeletonBinary_readAttachment(spSkeletonBinary* self, _dataInput readColor(input, ®ion->r, ®ion->g, ®ion->b, ®ion->a); spRegionAttachment_updateOffset(region); spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment); + if (freeName) FREE(name); return attachment; } case SP_ATTACHMENT_BOUNDING_BOX: { @@ -689,6 +694,7 @@ spAttachment* spSkeletonBinary_readAttachment(spSkeletonBinary* self, _dataInput _readVertices(self, input, SUB_CAST(spVertexAttachment, attachment), vertexCount); if (nonessential) readInt(input); /* Skip color. */ spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment); + if (freeName) FREE(name); return attachment; } case SP_ATTACHMENT_MESH: { @@ -717,6 +723,7 @@ spAttachment* spSkeletonBinary_readAttachment(spSkeletonBinary* self, _dataInput mesh->height = 0; } spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment); + if (freeName) FREE(name); return attachment; } case SP_ATTACHMENT_LINKED_MESH: { @@ -738,6 +745,7 @@ spAttachment* spSkeletonBinary_readAttachment(spSkeletonBinary* self, _dataInput mesh->height = readFloat(input) * self->scale; } _spSkeletonBinary_addLinkedMesh(self, mesh, skinName, slotIndex, parent); + if (freeName) FREE(name); return attachment; } case SP_ATTACHMENT_PATH: { @@ -755,10 +763,12 @@ spAttachment* spSkeletonBinary_readAttachment(spSkeletonBinary* self, _dataInput path->lengths[i] = readFloat(input) * self->scale; } if (nonessential) readInt(input); /* Skip color. */ + if (freeName) FREE(name); return attachment; } } + if (freeName) FREE(name); return 0; } From 48c5b06676c10454e52eed53dbfff7d2d6a3ee5f Mon Sep 17 00:00:00 2001 From: badlogic Date: Sun, 11 Sep 2016 13:26:51 +0200 Subject: [PATCH 2/4] [cocos2dx] Closes #611, cocos2d-x v3 auto-batcher does it's work, makes SkeletonBatch a lot simpler --- .../example/Classes/BatchingExample.cpp | 4 - spine-cocos2dx/src/spine/SkeletonBatch.cpp | 170 ++++++++---------- spine-cocos2dx/src/spine/SkeletonBatch.h | 75 ++++---- 3 files changed, 103 insertions(+), 146 deletions(-) diff --git a/spine-cocos2dx/example/Classes/BatchingExample.cpp b/spine-cocos2dx/example/Classes/BatchingExample.cpp index fe919fa25..0577fd077 100644 --- a/spine-cocos2dx/example/Classes/BatchingExample.cpp +++ b/spine-cocos2dx/example/Classes/BatchingExample.cpp @@ -44,10 +44,6 @@ Scene* BatchingExample::scene () { bool BatchingExample::init () { if (!LayerColor::initWithColor(Color4B(128, 128, 128, 255))) return false; - // To avoid the SkeletonBatch buffer from being resized, set this to the number of vertices ever rendered in one frame. - // BatchingExample needs ~3200, but let's set it low to test the buffer resizing. - SkeletonBatch::setBufferSize(512); - // Load the texture atlas. _atlas = spAtlas_createFromFile("spineboy.atlas", 0); CCASSERT(_atlas, "Error reading atlas file."); diff --git a/spine-cocos2dx/src/spine/SkeletonBatch.cpp b/spine-cocos2dx/src/spine/SkeletonBatch.cpp index 1237f0d74..19ea80171 100644 --- a/spine-cocos2dx/src/spine/SkeletonBatch.cpp +++ b/spine-cocos2dx/src/spine/SkeletonBatch.cpp @@ -1,10 +1,10 @@ /****************************************************************************** * Spine Runtimes Software License * Version 2.3 - * + * * Copyright (c) 2013-2015, Esoteric Software * All rights reserved. - * + * * You are granted a perpetual, non-exclusive, non-sublicensable and * non-transferable license to use, install, execute and perform the Spine * Runtimes Software (the "Software") and derivative works solely for personal @@ -16,7 +16,7 @@ * or other intellectual property or proprietary rights notices on or in the * Software, including any copy thereof. Redistributions in binary or source * form must include this license and terms. - * + * * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO @@ -38,102 +38,72 @@ USING_NS_CC; using std::max; namespace spine { - -static SkeletonBatch* instance = nullptr; - -void SkeletonBatch::setBufferSize (int vertexCount) { - if (instance) delete instance; - instance = new SkeletonBatch(vertexCount); -} - -SkeletonBatch* SkeletonBatch::getInstance () { - if (!instance) instance = new SkeletonBatch(8192); - return instance; -} -void SkeletonBatch::destroyInstance () { - if (instance) { - delete instance; - instance = nullptr; + static SkeletonBatch* instance = nullptr; + + SkeletonBatch* SkeletonBatch::getInstance () { + if (!instance) instance = new SkeletonBatch(); + return instance; } -} - -SkeletonBatch::SkeletonBatch (int capacity) : - _capacity(capacity), _position(0) -{ - _buffer = new V3F_C4B_T2F[capacity]; - _firstCommand = new Command(); - _command = _firstCommand; - - Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_AFTER_DRAW_RESET_POSITION, [this](EventCustom* eventCustom){ - this->update(0); - });; -} - -SkeletonBatch::~SkeletonBatch () { - Director::getInstance()->getEventDispatcher()->removeCustomEventListeners(EVENT_AFTER_DRAW_RESET_POSITION); - - Command* command = _firstCommand; - while (command) { - Command* next = command->next; - delete command; - command = next; - } - - delete [] _buffer; -} - -void SkeletonBatch::update (float delta) { - _position = 0; - _command = _firstCommand; -} - -void SkeletonBatch::addCommand (cocos2d::Renderer* renderer, float globalZOrder, GLuint textureID, GLProgramState* glProgramState, - BlendFunc blendFunc, const TrianglesCommand::Triangles& triangles, const Mat4& transform, uint32_t transformFlags -) { - if (_position + triangles.vertCount > _capacity) { - int newCapacity = max(_capacity + _capacity / 2, _position + triangles.vertCount); - V3F_C4B_T2F* newBuffer = new V3F_C4B_T2F[newCapacity]; - memcpy(newBuffer, _buffer, _position); - - int newPosition = 0; - Command* command = _firstCommand; - while (newPosition < _position) { - command->triangles->verts = newBuffer + newPosition; - newPosition += command->triangles->vertCount; - command = command->next; - } - - delete [] _buffer; - _buffer = newBuffer; - _capacity = newCapacity; - } - - memcpy(_buffer + _position, triangles.verts, sizeof(V3F_C4B_T2F) * triangles.vertCount); - _command->triangles->verts = _buffer + _position; - _position += triangles.vertCount; - - _command->triangles->vertCount = triangles.vertCount; - _command->triangles->indexCount = triangles.indexCount; - _command->triangles->indices = triangles.indices; - - _command->trianglesCommand->init(globalZOrder, textureID, glProgramState, blendFunc, *_command->triangles, transform, transformFlags); - renderer->addCommand(_command->trianglesCommand); - - if (!_command->next) _command->next = new Command(); - _command = _command->next; -} - -SkeletonBatch::Command::Command () : - next(nullptr) -{ - trianglesCommand = new TrianglesCommand(); - triangles = new TrianglesCommand::Triangles(); -} - -SkeletonBatch::Command::~Command () { - delete triangles; - delete trianglesCommand; -} - -} + + void SkeletonBatch::destroyInstance () { + if (instance) { + delete instance; + instance = nullptr; + } + } + + SkeletonBatch::SkeletonBatch () + { + _firstCommand = new Command(); + _command = _firstCommand; + + Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_AFTER_DRAW_RESET_POSITION, [this](EventCustom* eventCustom){ + this->update(0); + });; + } + + SkeletonBatch::~SkeletonBatch () { + Director::getInstance()->getEventDispatcher()->removeCustomEventListeners(EVENT_AFTER_DRAW_RESET_POSITION); + + Command* command = _firstCommand; + while (command) { + Command* next = command->next; + delete command; + command = next; + } + } + + void SkeletonBatch::update (float delta) { + _command = _firstCommand; + } + + void SkeletonBatch::addCommand (cocos2d::Renderer* renderer, float globalZOrder, GLuint textureID, GLProgramState* glProgramState, + BlendFunc blendFunc, const TrianglesCommand::Triangles& triangles, const Mat4& transform, uint32_t transformFlags + ) { + _command->triangles->verts = triangles.verts; + + _command->triangles->vertCount = triangles.vertCount; + _command->triangles->indexCount = triangles.indexCount; + _command->triangles->indices = triangles.indices; + + _command->trianglesCommand->init(globalZOrder, textureID, glProgramState, blendFunc, *_command->triangles, transform); + renderer->addCommand(_command->trianglesCommand); + + if (!_command->next) _command->next = new Command(); + _command = _command->next; + } + + SkeletonBatch::Command::Command () : + next(nullptr) + { + trianglesCommand = new TrianglesCommand(); + triangles = new TrianglesCommand::Triangles(); + } + + SkeletonBatch::Command::~Command () { + delete triangles; + delete trianglesCommand; + } + +} \ No newline at end of file diff --git a/spine-cocos2dx/src/spine/SkeletonBatch.h b/spine-cocos2dx/src/spine/SkeletonBatch.h index 98419bef1..6c2069ce4 100644 --- a/spine-cocos2dx/src/spine/SkeletonBatch.h +++ b/spine-cocos2dx/src/spine/SkeletonBatch.h @@ -1,10 +1,10 @@ /****************************************************************************** * Spine Runtimes Software License * Version 2.3 - * + * * Copyright (c) 2013-2015, Esoteric Software * All rights reserved. - * + * * You are granted a perpetual, non-exclusive, non-sublicensable and * non-transferable license to use, install, execute and perform the Spine * Runtimes Software (the "Software") and derivative works solely for personal @@ -16,7 +16,7 @@ * or other intellectual property or proprietary rights notices on or in the * Software, including any copy thereof. Redistributions in binary or source * form must include this license and terms. - * + * * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO @@ -36,45 +36,36 @@ #include "cocos2d.h" namespace spine { - -class SkeletonBatch { -public: - /* Sets the max number of vertices that can be drawn in a single frame. The buffer will grow automatically as needed, but - * setting it to the appropriate is more efficient. Best to call before getInstance is called for the first time. Default is - * 8192. */ - static void setBufferSize (int vertexCount); - - static SkeletonBatch* getInstance (); - static void destroyInstance (); - - void update (float delta); - - void addCommand (cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, - cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand:: Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); - -protected: - SkeletonBatch (int capacity); - virtual ~SkeletonBatch (); - - cocos2d::V3F_C4B_T2F* _buffer; - int _capacity; - int _position; - - class Command { - public: - Command (); - virtual ~Command (); - - cocos2d::TrianglesCommand* trianglesCommand; - cocos2d::TrianglesCommand::Triangles* triangles; - Command* next; - }; - - Command* _firstCommand; - Command* _command; -}; - + class SkeletonBatch { + public: + static SkeletonBatch* getInstance (); + + static void destroyInstance (); + + void update (float delta); + + void addCommand (cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, + cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand:: Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); + + protected: + SkeletonBatch (); + virtual ~SkeletonBatch (); + + class Command { + public: + Command (); + virtual ~Command (); + + cocos2d::TrianglesCommand* trianglesCommand; + cocos2d::TrianglesCommand::Triangles* triangles; + Command* next; + }; + + Command* _firstCommand; + Command* _command; + }; + } -#endif // SPINE_SKELETONBATCH_H_ +#endif // SPINE_SKELETONBATCH_H_ \ No newline at end of file From c1535080c8e79ec7b1983090bb335da0d08d0ba5 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 11 Sep 2016 22:08:43 +0800 Subject: [PATCH 3/4] [unity] Unity Sprite slices for atlas textures. The new "Apply Regions as Texture Sprite Slices" button adds Sprite slices to all atlas page Textures of an AtlasAsset in Unity Editor. Clicking the button updates existing sprites if it detects name-matches. Adds ones that don't exist. It does not remove non-matches. Sprite slices are not auto-applied on atlas import. Users have to opt-in by clicking the button. This is also to communicate that spine-unity does not currently rely on the Unity sprite system. Unity 5.5 has promising API changes to this end but this cannot be a standard spine-unity feature yet. This replaces the functionality of the old Region Baking as prefabs with mesh assets. Original functionality has been precompiler-defined out. Region baking code will be removed in a future version. --- .../Asset Types/Editor/AtlasAssetInspector.cs | 93 +++++++++++++++++-- 1 file changed, 87 insertions(+), 6 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs b/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs index fb5672492..6c831ebd4 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs @@ -29,6 +29,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ //#define BAKE_ALL_BUTTON +//#define REGION_BAKING_MESH using System; using System.Collections.Generic; @@ -44,8 +45,6 @@ namespace Spine.Unity.Editor { public class AtlasAssetInspector : UnityEditor.Editor { private SerializedProperty atlasFile, materials; private AtlasAsset atlasAsset; - private List baked; - private List bakedObjects; void OnEnable () { SpineEditorUtilities.ConfirmInitialization(); @@ -53,8 +52,14 @@ namespace Spine.Unity.Editor { materials = serializedObject.FindProperty("materials"); materials.isExpanded = true; atlasAsset = (AtlasAsset)target; + #if REGION_BAKING_MESH UpdateBakedList(); + #endif } + + #if REGION_BAKING_MESH + private List baked; + private List bakedObjects; void UpdateBakedList () { AtlasAsset asset = (AtlasAsset)target; @@ -78,13 +83,72 @@ namespace Spine.Unity.Editor { } } } + #endif + + static public void UpdateSpriteSlices (Texture texture, Atlas atlas) { + string texturePath = AssetDatabase.GetAssetPath(texture.GetInstanceID()); + var t = (TextureImporter)TextureImporter.GetAtPath(texturePath); + t.spriteImportMode = SpriteImportMode.Multiple; + var spriteSheet = t.spritesheet; + var sprites = new List(spriteSheet); + + FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.NonPublic); + var regions = (List)field.GetValue(atlas); + int textureHeight = texture.height; + char[] FilenameDelimiter = {'.'}; + int updatedCount = 0; + int addedCount = 0; + + foreach (var r in regions) { + int width, height; + if (r.rotate) { + width = r.height; + height = r.width; + } else { + width = r.width; + height = r.height; + } + + int x = r.x; + int y = textureHeight - height - r.y; + + string pageName = r.page.name.Split(FilenameDelimiter, StringSplitOptions.RemoveEmptyEntries)[0]; + string textureName = texture.name; + bool pageMatch = string.Equals(pageName, textureName,StringComparison.Ordinal); + int spriteIndex = pageMatch ? sprites.FindIndex( + (s) => string.Equals(s.name, r.name, StringComparison.Ordinal) + ) : -1; + bool matchFound = spriteIndex >= 0; + + if (matchFound) { + var s = sprites[spriteIndex]; + s.rect = new Rect(x, y, width, height); + sprites[spriteIndex] = s; + updatedCount++; + } else { + if (pageMatch) { + sprites.Add(new SpriteMetaData { + name = r.name, + pivot = new Vector2(0.5f, 0.5f), + rect = new Rect(x, y, width, height) + }); + addedCount++; + } + } + } + + t.spritesheet = sprites.ToArray(); + EditorUtility.SetDirty(t); + AssetDatabase.ImportAsset(texturePath, ImportAssetOptions.ForceUpdate); + Debug.Log(string.Format("Applied sprite slices to {2}. {0} added. {1} updated.", addedCount, updatedCount, texture.name)); + } override public void OnInspectorGUI () { serializedObject.Update(); - AtlasAsset asset = (AtlasAsset)target; - + atlasAsset = atlasAsset ?? (AtlasAsset)target; EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(atlasFile); + EditorGUILayout.PropertyField(materials, true); if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties(); @@ -103,6 +167,22 @@ namespace Spine.Unity.Editor { } } + if (atlasFile.objectReferenceValue != null) { + if (GUILayout.Button( + new GUIContent( + "Apply Regions as Texture Sprite Slices", + "Adds Sprite slices to atlas texture(s). " + + "Updates existing slices if ones with matching names exist. \n\n" + + "If your atlas was exported with Premultiply Alpha, " + + "your SpriteRenderer should use the generated Spine _Material asset (or any Material with a PMA shader) instead of Sprites-Default.") + , GUILayout.Height(70f))) { + var atlas = atlasAsset.GetAtlas(); + foreach (var m in atlasAsset.materials) + UpdateSpriteSlices(m.mainTexture, atlas); + } + } + + #if REGION_BAKING_MESH if (atlasFile.objectReferenceValue != null) { Atlas atlas = asset.GetAtlas(); FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); @@ -206,13 +286,14 @@ namespace Spine.Unity.Editor { } #endif - + } + #endif if (serializedObject.ApplyModifiedProperties() || (UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed") ) { - asset.Reset(); + atlasAsset.Reset(); } } } From 7b03506b24475105c1aaf9a57e95b1c2d1415d7a Mon Sep 17 00:00:00 2001 From: John Date: Sun, 11 Sep 2016 22:24:03 +0800 Subject: [PATCH 4/4] [unity] Added sprite slices button feedback. --- .../Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs b/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs index 6c831ebd4..4531e5be8 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs @@ -140,6 +140,7 @@ namespace Spine.Unity.Editor { t.spritesheet = sprites.ToArray(); EditorUtility.SetDirty(t); AssetDatabase.ImportAsset(texturePath, ImportAssetOptions.ForceUpdate); + EditorGUIUtility.PingObject(texture); Debug.Log(string.Format("Applied sprite slices to {2}. {0} added. {1} updated.", addedCount, updatedCount, texture.name)); }