diff --git a/.gitignore b/.gitignore index 4cc1cdd79..251406bde 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ spine-cocos2dx/example/cocos2d spine-cocos2dx/example/proj.win32/spine-cocos2d-x.VC.opendb spine-cocos2dx/example/proj.win32/spine-cocos2d-x.VC.db xcuserdata/ +xcshareddata/ spine-cocos2d-objc/cocos2d/* spine-cocos2d-objc/spine-cocos2d-iphone-objc.xcodeproj/project.xcworkspace/xcshareddata/ diff --git a/spine-cocos2dx/example/Classes/AppDelegate.cpp b/spine-cocos2dx/example/Classes/AppDelegate.cpp index 6b6def318..df1721723 100644 --- a/spine-cocos2dx/example/Classes/AppDelegate.cpp +++ b/spine-cocos2dx/example/Classes/AppDelegate.cpp @@ -74,7 +74,7 @@ bool AppDelegate::applicationDidFinishLaunching () { glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER); #endif - Size frameSize = glview->getFrameSize(); + cocos2d::Size frameSize = glview->getFrameSize(); vector searchPath; diff --git a/spine-cocos2dx/example/Classes/BatchingExample.cpp b/spine-cocos2dx/example/Classes/BatchingExample.cpp index e14073d9a..b4d79d793 100644 --- a/spine-cocos2dx/example/Classes/BatchingExample.cpp +++ b/spine-cocos2dx/example/Classes/BatchingExample.cpp @@ -70,14 +70,14 @@ bool BatchingExample::init () { int xMin = _contentSize.width * 0.10f, xMax = _contentSize.width * 0.90f; int yMin = 0, yMax = _contentSize.height * 0.7f; - for (int i = 0, j = 0; i < NUM_SKELETONS; i++) { + for (int i = 0; i < NUM_SKELETONS; i++) { // Each skeleton node shares the same atlas, skeleton data, and mix times. SkeletonAnimation* skeletonNode = SkeletonAnimation::createWithData(_skeletonData, false); skeletonNode->setAnimationStateData(_stateData); skeletonNode->setAnimation(0, "walk", true); skeletonNode->addAnimation(0, "jump", true, RandomHelper::random_int(0, 300) / 100.0f); - skeletonNode->addAnimation(0, "run", true); + skeletonNode->addAnimation(0, "run", true); // alternative setting two color tint for groups of 10 skeletons // should end up with #skeletons / 10 batches diff --git a/spine-cocos2dx/example/Classes/CoinExample.cpp b/spine-cocos2dx/example/Classes/CoinExample.cpp index bc8e3d6e5..23be0d077 100644 --- a/spine-cocos2dx/example/Classes/CoinExample.cpp +++ b/spine-cocos2dx/example/Classes/CoinExample.cpp @@ -44,14 +44,14 @@ bool CoinExample::init () { if (!LayerColor::initWithColor(Color4B(128, 128, 128, 255))) return false; skeletonNode = SkeletonAnimation::createWithBinaryFile("coin-pro.skel", "coin.atlas", 1); - skeletonNode->setAnimation(0, "rotate", true); - // skeletonNode->setTwoColorTint(true); - + skeletonNode->setAnimation(0, "rotate", true); + // skeletonNode->setTwoColorTint(true); + skeletonNode->setPosition(Vec2(_contentSize.width / 2, 100)); addChild(skeletonNode); scheduleUpdate(); - + EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool { if (!skeletonNode->getDebugBonesEnabled()) diff --git a/spine-cocos2dx/example/Classes/GoblinsExample.cpp b/spine-cocos2dx/example/Classes/GoblinsExample.cpp index 351979903..764222351 100644 --- a/spine-cocos2dx/example/Classes/GoblinsExample.cpp +++ b/spine-cocos2dx/example/Classes/GoblinsExample.cpp @@ -51,7 +51,7 @@ bool GoblinsExample::init () { addChild(skeletonNode); scheduleUpdate(); - + EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool { if (!skeletonNode->getDebugBonesEnabled()) diff --git a/spine-cocos2dx/example/Classes/RaptorExample.cpp b/spine-cocos2dx/example/Classes/RaptorExample.cpp index fa2b7bc77..c84eade5e 100644 --- a/spine-cocos2dx/example/Classes/RaptorExample.cpp +++ b/spine-cocos2dx/example/Classes/RaptorExample.cpp @@ -34,9 +34,9 @@ USING_NS_CC; using namespace spine; - -PowInterpolation pow2(2); -PowOutInterpolation powOut2(2); + +PowInterpolation pow2(2); +PowOutInterpolation powOut2(2); SwirlVertexEffect effect(400, powOut2); Scene* RaptorExample::scene () { @@ -67,7 +67,7 @@ bool RaptorExample::init () { listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool { if (!skeletonNode->getDebugBonesEnabled()) { skeletonNode->setDebugBonesEnabled(true); - skeletonNode->setDebugMeshesEnabled(true); + skeletonNode->setDebugMeshesEnabled(true); } else if (skeletonNode->getTimeScale() == 1) skeletonNode->setTimeScale(0.3f); else @@ -80,8 +80,8 @@ bool RaptorExample::init () { } void RaptorExample::update(float fDelta) { - swirlTime += fDelta; - float percent = spine::MathUtil::fmod(swirlTime, 2); - if (percent > 1) percent = 1 - (percent - 1); + swirlTime += fDelta; + float percent = spine::MathUtil::fmod(swirlTime, 2); + if (percent > 1) percent = 1 - (percent - 1); effect.setAngle(pow2.interpolate(-60.0f, 60.0f, percent)); } diff --git a/spine-cocos2dx/example/Classes/SkeletonRendererSeparatorExample.cpp b/spine-cocos2dx/example/Classes/SkeletonRendererSeparatorExample.cpp index cb48f2a9a..59cc2c051 100644 --- a/spine-cocos2dx/example/Classes/SkeletonRendererSeparatorExample.cpp +++ b/spine-cocos2dx/example/Classes/SkeletonRendererSeparatorExample.cpp @@ -42,39 +42,39 @@ Scene* SkeletonRendererSeparatorExample::scene () { bool SkeletonRendererSeparatorExample::init () { if (!LayerColor::initWithColor(Color4B(128, 128, 128, 255))) return false; - - // Spineboy's back, which will manage the animation and GPU resources + + // Spineboy's back, which will manage the animation and GPU resources // will render only the front slots of Spineboy - backNode = SkeletonAnimation::createWithJsonFile("spineboy-ess.json", "spineboy.atlas", 0.6f); - backNode->setMix("walk", "jump", 0.4); - backNode->setAnimation(0, "walk", true); - backNode->setSlotsRange(backNode->findSlot("rear-upper-arm")->getData().getIndex(), backNode->findSlot("rear-shin")->getData().getIndex()); - backNode->setPosition(Vec2(_contentSize.width / 2, 20)); - - // A simple rectangle to go between the front and back slots of Spineboy - betweenNode = DrawNode::create(); - Vec2 rect[4]; - rect[0] = Vec2(0, 0); - rect[1] = Vec2(40, 0); - rect[2] = Vec2(40, 200); - rect[3] = Vec2(0, 200); - betweenNode->drawPolygon(rect, 4, Color4F(1, 0, 0, 1), 1, Color4F(1, 0, 0, 1)); - betweenNode->setPosition(Vec2(_contentSize.width / 2 + 30, 20)); - - // Spineboy's front, doesn't manage any skeleton, animation or GPU resources, but simply - // renders the back slots of Spineboy. The skeleton, animatio state and GPU resources - // are shared with the front node! - frontNode = SkeletonRenderer::createWithSkeleton(backNode->getSkeleton()); - frontNode->setSlotsRange(frontNode->findSlot("neck")->getData().getIndex(), -1); - frontNode->setPosition(Vec2(_contentSize.width / 2, 20)); - - // Add the front, between and back node in the correct order to this scene - addChild(backNode); - addChild(betweenNode); + backNode = SkeletonAnimation::createWithJsonFile("spineboy-ess.json", "spineboy.atlas", 0.6f); + backNode->setMix("walk", "jump", 0.4); + backNode->setAnimation(0, "walk", true); + backNode->setSlotsRange(backNode->findSlot("rear-upper-arm")->getData().getIndex(), backNode->findSlot("rear-shin")->getData().getIndex()); + backNode->setPosition(Vec2(_contentSize.width / 2, 20)); + + // A simple rectangle to go between the front and back slots of Spineboy + betweenNode = DrawNode::create(); + Vec2 rect[4]; + rect[0] = Vec2(0, 0); + rect[1] = Vec2(40, 0); + rect[2] = Vec2(40, 200); + rect[3] = Vec2(0, 200); + betweenNode->drawPolygon(rect, 4, Color4F(1, 0, 0, 1), 1, Color4F(1, 0, 0, 1)); + betweenNode->setPosition(Vec2(_contentSize.width / 2 + 30, 20)); + + // Spineboy's front, doesn't manage any skeleton, animation or GPU resources, but simply + // renders the back slots of Spineboy. The skeleton, animatio state and GPU resources + // are shared with the front node! + frontNode = SkeletonRenderer::createWithSkeleton(backNode->getSkeleton()); + frontNode->setSlotsRange(frontNode->findSlot("neck")->getData().getIndex(), -1); + frontNode->setPosition(Vec2(_contentSize.width / 2, 20)); + + // Add the front, between and back node in the correct order to this scene + addChild(backNode); + addChild(betweenNode); addChild(frontNode); scheduleUpdate(); - + EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool { if (!backNode->getDebugBonesEnabled()) diff --git a/spine-cocos2dx/example/Classes/SpineboyExample.cpp b/spine-cocos2dx/example/Classes/SpineboyExample.cpp index 42b3d0e90..9e1f49a7d 100644 --- a/spine-cocos2dx/example/Classes/SpineboyExample.cpp +++ b/spine-cocos2dx/example/Classes/SpineboyExample.cpp @@ -68,7 +68,7 @@ bool SpineboyExample::init () { skeletonNode->setMix("jump", "run", 0.4); skeletonNode->setAnimation(0, "walk", true); TrackEntry* jumpEntry = skeletonNode->addAnimation(0, "jump", false, 1); - skeletonNode->addAnimation(0, "run", true); + skeletonNode->addAnimation(0, "run", true); skeletonNode->setTrackStartListener(jumpEntry, [] (TrackEntry* entry) { log("jumped!"); @@ -81,7 +81,7 @@ bool SpineboyExample::init () { addChild(skeletonNode); scheduleUpdate(); - + EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool { if (!skeletonNode->getDebugBonesEnabled()) diff --git a/spine-cocos2dx/example/Classes/TankExample.cpp b/spine-cocos2dx/example/Classes/TankExample.cpp index a8245ea85..482790a5d 100644 --- a/spine-cocos2dx/example/Classes/TankExample.cpp +++ b/spine-cocos2dx/example/Classes/TankExample.cpp @@ -50,7 +50,7 @@ bool TankExample::init () { addChild(skeletonNode); scheduleUpdate(); - + EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool { if (!skeletonNode->getDebugBonesEnabled()) diff --git a/spine-cocos2dx/src/spine/SkeletonRenderer.h b/spine-cocos2dx/src/spine/SkeletonRenderer.h index 20fd4fe40..edff7c942 100644 --- a/spine-cocos2dx/src/spine/SkeletonRenderer.h +++ b/spine-cocos2dx/src/spine/SkeletonRenderer.h @@ -35,129 +35,129 @@ #include "cocos2d.h" namespace spine { - -class AttachmentVertices; - -/* Draws a skeleton. */ -class SkeletonRenderer: public cocos2d::Node, public cocos2d::BlendProtocol { -public: - CREATE_FUNC(SkeletonRenderer); - static SkeletonRenderer* createWithSkeleton(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false); - static SkeletonRenderer* createWithData (SkeletonData* skeletonData, bool ownsSkeletonData = false); - static SkeletonRenderer* createWithFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1); - static SkeletonRenderer* createWithFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); - - virtual void update (float deltaTime) override; - virtual void draw (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags) override; - virtual void drawDebug (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags); - virtual cocos2d::Rect getBoundingBox () const override; - virtual void onEnter () override; - virtual void onExit () override; - - Skeleton* getSkeleton(); - - void setTimeScale(float scale); - float getTimeScale() const; - - /* */ - void setDebugSlotsEnabled(bool enabled); - bool getDebugSlotsEnabled() const; - - void setDebugBonesEnabled(bool enabled); - bool getDebugBonesEnabled() const; - void setDebugMeshesEnabled(bool enabled); - bool getDebugMeshesEnabled() const; - - // --- Convenience methods for common Skeleton_* functions. - void updateWorldTransform (); - - void setToSetupPose (); - void setBonesToSetupPose (); - void setSlotsToSetupPose (); - - /* Returns 0 if the bone was not found. */ - Bone* findBone (const std::string& boneName) const; - /* Returns 0 if the slot was not found. */ - Slot* findSlot (const std::string& slotName) const; + class AttachmentVertices; - /* Sets the skin used to look up attachments not found in the SkeletonData defaultSkin. Attachments from the new skin are - * attached if the corresponding attachment from the old skin was attached. - * @param skin May be empty string ("") for no skin.*/ - void setSkin (const std::string& skinName); - /** @param skin May be 0 for no skin.*/ - void setSkin (const char* skinName); + /* Draws a skeleton. */ + class SkeletonRenderer: public cocos2d::Node, public cocos2d::BlendProtocol { + public: + CREATE_FUNC(SkeletonRenderer); + static SkeletonRenderer* createWithSkeleton(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false); + static SkeletonRenderer* createWithData (SkeletonData* skeletonData, bool ownsSkeletonData = false); + static SkeletonRenderer* createWithFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1); + static SkeletonRenderer* createWithFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); + + virtual void update (float deltaTime) override; + virtual void draw (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags) override; + virtual void drawDebug (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags); + virtual cocos2d::Rect getBoundingBox () const override; + virtual void onEnter () override; + virtual void onExit () override; + + Skeleton* getSkeleton(); + + void setTimeScale(float scale); + float getTimeScale() const; + + /* */ + void setDebugSlotsEnabled(bool enabled); + bool getDebugSlotsEnabled() const; + + void setDebugBonesEnabled(bool enabled); + bool getDebugBonesEnabled() const; + + void setDebugMeshesEnabled(bool enabled); + bool getDebugMeshesEnabled() const; + + // --- Convenience methods for common Skeleton_* functions. + void updateWorldTransform (); + + void setToSetupPose (); + void setBonesToSetupPose (); + void setSlotsToSetupPose (); + + /* Returns 0 if the bone was not found. */ + Bone* findBone (const std::string& boneName) const; + /* Returns 0 if the slot was not found. */ + Slot* findSlot (const std::string& slotName) const; + + /* Sets the skin used to look up attachments not found in the SkeletonData defaultSkin. Attachments from the new skin are + * attached if the corresponding attachment from the old skin was attached. + * @param skin May be empty string ("") for no skin.*/ + void setSkin (const std::string& skinName); + /** @param skin May be 0 for no skin.*/ + void setSkin (const char* skinName); + + /* Returns 0 if the slot or attachment was not found. */ + Attachment* getAttachment (const std::string& slotName, const std::string& attachmentName) const; + /* Returns false if the slot or attachment was not found. + * @param attachmentName May be empty string ("") for no attachment. */ + bool setAttachment (const std::string& slotName, const std::string& attachmentName); + /* @param attachmentName May be 0 for no attachment. */ + bool setAttachment (const std::string& slotName, const char* attachmentName); + + /* Enables/disables two color tinting for this instance. May break batching */ + void setTwoColorTint(bool enabled); + /* Whether two color tinting is enabled */ + bool isTwoColorTint(); + + /* Sets the vertex effect to be used, set to 0 to disable vertex effects */ + void setVertexEffect(VertexEffect* effect); + + /* Sets the range of slots that should be rendered. Use -1, -1 to clear the range */ + void setSlotsRange(int startSlotIndex, int endSlotIndex); + + // --- BlendProtocol + virtual void setBlendFunc (const cocos2d::BlendFunc& blendFunc)override; + virtual const cocos2d::BlendFunc& getBlendFunc () const override; + virtual void setOpacityModifyRGB (bool value) override; + virtual bool isOpacityModifyRGB () const override; + + // Frees global memory used for temporay vertex transformations. + static void destroyScratchBuffers(); + + CC_CONSTRUCTOR_ACCESS: + SkeletonRenderer (); + SkeletonRenderer(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false, bool ownsAtlas = false); + SkeletonRenderer (SkeletonData* skeletonData, bool ownsSkeletonData = false); + SkeletonRenderer (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1); + SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); + + virtual ~SkeletonRenderer (); + + void initWithSkeleton(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false, bool ownsAtlas = false); + void initWithData (SkeletonData* skeletonData, bool ownsSkeletonData = false); + void initWithJsonFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1); + void initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); + void initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1); + void initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); + + virtual void initialize (); + + protected: + void setSkeletonData (SkeletonData* skeletonData, bool ownsSkeletonData); + void setupGLProgramState(bool twoColorTintEnabled); + + bool _ownsSkeletonData; + bool _ownsSkeleton; + bool _ownsAtlas; + Atlas* _atlas; + AttachmentLoader* _attachmentLoader; + cocos2d::CustomCommand _debugCommand; + cocos2d::BlendFunc _blendFunc; + bool _premultipliedAlpha; + Skeleton* _skeleton; + float _timeScale; + bool _debugSlots; + bool _debugBones; + bool _debugMeshes; + SkeletonClipping* _clipper; + VertexEffect* _effect; + + int _startSlotIndex; + int _endSlotIndex; + }; - /* Returns 0 if the slot or attachment was not found. */ - Attachment* getAttachment (const std::string& slotName, const std::string& attachmentName) const; - /* Returns false if the slot or attachment was not found. - * @param attachmentName May be empty string ("") for no attachment. */ - bool setAttachment (const std::string& slotName, const std::string& attachmentName); - /* @param attachmentName May be 0 for no attachment. */ - bool setAttachment (const std::string& slotName, const char* attachmentName); - - /* Enables/disables two color tinting for this instance. May break batching */ - void setTwoColorTint(bool enabled); - /* Whether two color tinting is enabled */ - bool isTwoColorTint(); - - /* Sets the vertex effect to be used, set to 0 to disable vertex effects */ - void setVertexEffect(VertexEffect* effect); - - /* Sets the range of slots that should be rendered. Use -1, -1 to clear the range */ - void setSlotsRange(int startSlotIndex, int endSlotIndex); - - // --- BlendProtocol - virtual void setBlendFunc (const cocos2d::BlendFunc& blendFunc)override; - virtual const cocos2d::BlendFunc& getBlendFunc () const override; - virtual void setOpacityModifyRGB (bool value) override; - virtual bool isOpacityModifyRGB () const override; - - // Frees global memory used for temporay vertex transformations. - static void destroyScratchBuffers(); - -CC_CONSTRUCTOR_ACCESS: - SkeletonRenderer (); - SkeletonRenderer(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false, bool ownsAtlas = false); - SkeletonRenderer (SkeletonData* skeletonData, bool ownsSkeletonData = false); - SkeletonRenderer (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1); - SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); - - virtual ~SkeletonRenderer (); - - void initWithSkeleton(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false, bool ownsAtlas = false); - void initWithData (SkeletonData* skeletonData, bool ownsSkeletonData = false); - void initWithJsonFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1); - void initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); - void initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1); - void initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); - - virtual void initialize (); - -protected: - void setSkeletonData (SkeletonData* skeletonData, bool ownsSkeletonData); - void setupGLProgramState(bool twoColorTintEnabled); - - bool _ownsSkeletonData; - bool _ownsSkeleton; - bool _ownsAtlas; - Atlas* _atlas; - AttachmentLoader* _attachmentLoader; - cocos2d::CustomCommand _debugCommand; - cocos2d::BlendFunc _blendFunc; - bool _premultipliedAlpha; - Skeleton* _skeleton; - float _timeScale; - bool _debugSlots; - bool _debugBones; - bool _debugMeshes; - SkeletonClipping* _clipper; - VertexEffect* _effect; - - int _startSlotIndex; - int _endSlotIndex; -}; - } #endif /* SPINE_SKELETONRENDERER_H_ */ diff --git a/spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp b/spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp index b370ed875..dcf648897 100644 --- a/spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp +++ b/spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp @@ -57,7 +57,7 @@ void TwoColorTrianglesCommand::init(float globalOrder, GLuint textureID, GLProgr if(_triangles.indexCount % 3 != 0) { int count = _triangles.indexCount; _triangles.indexCount = count / 3 * 3; - CCLOGERROR("Resize indexCount from %zd to %zd, size must be multiple times of 3", count, _triangles.indexCount); + CCLOGERROR("Resize indexCount from %d to %d, size must be multiple times of 3", count, _triangles.indexCount); } _mv = mv; diff --git a/spine-csharp/src/Bone.cs b/spine-csharp/src/Bone.cs index 21b471894..acc1a42ed 100644 --- a/spine-csharp/src/Bone.cs +++ b/spine-csharp/src/Bone.cs @@ -329,10 +329,11 @@ namespace Spine { public float WorldToLocalRotation (float worldRotation) { float sin = MathUtils.SinDeg(worldRotation), cos = MathUtils.CosDeg(worldRotation); - return MathUtils.Atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.RadDeg; + return MathUtils.Atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.RadDeg + rotation - shearX; } public float LocalToWorldRotation (float localRotation) { + localRotation -= rotation - shearX; float sin = MathUtils.SinDeg(localRotation), cos = MathUtils.CosDeg(localRotation); return MathUtils.Atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.RadDeg; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java index 2d4a1ec6d..3d309ce4f 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java @@ -127,11 +127,11 @@ public class AnimationState { float nextTime = current.trackLast - next.delay; if (nextTime >= 0) { next.delay = 0; - next.trackTime = nextTime + delta * next.timeScale; + next.trackTime = (nextTime / current.timeScale + delta) * next.timeScale; current.trackTime += currentDelta; setCurrent(i, next, true); while (next.mixingFrom != null) { - next.mixTime += currentDelta; + next.mixTime += delta; next = next.mixingFrom; } continue; @@ -182,15 +182,8 @@ public class AnimationState { return finished; } - // If to has 0 timeScale and is not the first entry, remove the mix and apply it one more time to return to the setup pose. - if (to.timeScale == 0 && to.mixingTo != null) { - to.timeScale = 1; - to.mixTime = 0; - to.mixDuration = 0; - } - from.trackTime += delta * from.timeScale; - to.mixTime += delta * to.timeScale; + to.mixTime += delta; return false; } @@ -424,10 +417,10 @@ public class AnimationState { } } - /** Removes all animations from all tracks, leaving skeletons in their previous pose. + /** Removes all animations from all tracks, leaving skeletons in their current pose. *

* It may be desired to use {@link AnimationState#setEmptyAnimations(float)} to mix the skeletons back to the setup pose, - * rather than leaving them in their previous pose. */ + * rather than leaving them in their current pose. */ public void clearTracks () { boolean oldDrainDisabled = queue.drainDisabled; queue.drainDisabled = true; @@ -438,10 +431,10 @@ public class AnimationState { queue.drain(); } - /** Removes all animations from the track, leaving skeletons in their previous pose. + /** Removes all animations from the track, leaving skeletons in their current pose. *

* It may be desired to use {@link AnimationState#setEmptyAnimation(int, float)} to mix the skeletons back to the setup pose, - * rather than leaving them in their previous pose. */ + * rather than leaving them in their current pose. */ public void clearTrack (int trackIndex) { if (trackIndex >= tracks.size) return; TrackEntry current = tracks.get(trackIndex); @@ -534,9 +527,10 @@ public class AnimationState { /** Adds an animation to be played after the current or last queued animation for a track. If the track is empty, it is * equivalent to calling {@link #setAnimation(int, Animation, boolean)}. - * @param delay Seconds to begin this animation after the start of the previous animation. If <= 0, uses the duration of the - * previous track entry minus any mix duration plus the specified delay. If the previous entry is - * looping, its next loop completion is used instead of the duration. + * @param delay If > 0, sets {@link TrackEntry#getDelay()}. If <= 0, the delay set is the duration of the previous track entry + * minus any mix duration (from the {@link AnimationStateData}) plus the specified delay (ie the mix + * ends at (delay = 0) or before (delay < 0) the previous track entry duration). If the + * previous entry is looping, its next loop completion is used instead of its duration. * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose(TrackEntry)} event occurs. */ public TrackEntry addAnimation (int trackIndex, Animation animation, boolean loop, float delay) { @@ -598,9 +592,10 @@ public class AnimationState { * {@link #setEmptyAnimation(int, float)}. *

* See {@link #setEmptyAnimation(int, float)}. - * @param delay Seconds to begin this animation after the start of the previous animation. If <= 0, uses the duration of the - * previous track entry minus any mix duration plus the specified delay. If the previous entry is - * looping, its next loop completion is used instead of the duration. + * @param delay If > 0, sets {@link TrackEntry#getDelay()}. If <= 0, the delay set is the duration of the previous track entry + * minus any mix duration plus the specified delay (ie the mix ends at (delay = 0) or + * before (delay < 0) the previous track entry duration). If the previous entry is looping, its next + * loop completion is used instead of its duration. * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose(TrackEntry)} event occurs. */ public TrackEntry addEmptyAnimation (int trackIndex, float mixDuration, float delay) { @@ -764,8 +759,8 @@ public class AnimationState { queue.clear(); } - /** Multiplier for the delta time when the animation state is updated, causing time for all animations to play slower or - * faster. Defaults to 1. + /** Multiplier for the delta time when the animation state is updated, causing time for all animations and mixes to play slower + * or faster. Defaults to 1. *

* See TrackEntry {@link TrackEntry#getTimeScale()} for affecting a single animation. */ public float getTimeScale () { @@ -859,9 +854,12 @@ public class AnimationState { this.loop = loop; } - /** Seconds to postpone playing the animation. When a track entry is the current track entry, delay postpones - * incrementing the {@link #getTrackTime()}. When a track entry is queued, delay is the time from the start of - * the previous animation to when the track entry will become the current track entry. */ + /** Seconds to postpone playing the animation. When this track entry is the current track entry, delay + * postpones incrementing the {@link #getTrackTime()}. When this track entry is queued, delay is the time from + * the start of the previous animation to when this track entry will become the current track entry (ie when the previous + * track entry {@link TrackEntry#getTrackTime()} >= this track entry's delay). + *

+ * {@link #getTimeScale()} affects the delay. */ public float getDelay () { return delay; } @@ -943,10 +941,15 @@ public class AnimationState { return Math.min(trackTime + animationStart, animationEnd); } - /** Multiplier for the delta time when the animation state is updated, causing time for this animation to pass slower or + /** Multiplier for the delta time when this track entry is updated, causing time for this animation to pass slower or * faster. Defaults to 1. *

- * If timeScale is 0, any {@link #getMixDuration()} will be ignored. + * {@link #getMixTime()} is not affected by track entry time scale, so {@link #getMixDuration()} may need to be adjusted to + * match the animation speed. + *

+ * When using {@link AnimationState#addAnimation(int, Animation, boolean, float)} with a delay <= 0, note the + * {@link #getDelay()} is set using the mix duration from the {@link AnimationStateData}, assuming time scale to be 1. If + * the time scale is not 1, the delay may need to be adjusted. *

* See AnimationState {@link AnimationState#getTimeScale()} for affecting all animations. */ public float getTimeScale () { @@ -970,11 +973,11 @@ public class AnimationState { this.listener = listener; } - /** Values < 1 mix this animation with the setup pose or the skeleton's previous pose. Defaults to 1, which overwrites the - * skeleton's previous pose with this animation. + /** Values < 1 mix this animation with the skeleton's current pose (usually the pose resulting from lower tracks). Defaults + * to 1, which overwrites the skeleton's current pose with this animation. *

- * Typically track 0 is used to completely pose the skeleton, then alpha can be used on higher tracks. It doesn't make sense - * to use alpha on track 0 if the skeleton pose is from the last frame render. */ + * Typically track 0 is used to completely pose the skeleton, then alpha is used on higher tracks. It doesn't make sense to + * use alpha on track 0 if the skeleton pose is from the last frame render. */ public float getAlpha () { return alpha; } @@ -984,7 +987,7 @@ public class AnimationState { } /** When the mix percentage ({@link #getMixTime()} / {@link #getMixDuration()}) is less than the - * eventThreshold, event timelines for the animation being mixed out will be applied. Defaults to 0, so event + * eventThreshold, event timelines are applied while this animation is being mixed out. Defaults to 0, so event * timelines are not applied for an animation being mixed out. */ public float getEventThreshold () { return eventThreshold; @@ -995,8 +998,8 @@ public class AnimationState { } /** When the mix percentage ({@link #getMixTime()} / {@link #getMixDuration()}) is less than the - * attachmentThreshold, attachment timelines for the animation being mixed out will be applied. Defaults to 0, - * so attachment timelines are not applied for an animation being mixed out. */ + * attachmentThreshold, attachment timelines are applied while this animation is being mixed out. Defaults to + * 0, so attachment timelines are not applied for an animation being mixed out. */ public float getAttachmentThreshold () { return attachmentThreshold; } @@ -1006,7 +1009,7 @@ public class AnimationState { } /** When the mix percentage ({@link #getMixTime()} / {@link #getMixDuration()}) is less than the - * drawOrderThreshold, draw order timelines for the animation being mixed out will be applied. Defaults to 0, + * drawOrderThreshold, draw order timelines are applied while this animation is being mixed out. Defaults to 0, * so draw order timelines are not applied for an animation being mixed out. */ public float getDrawOrderThreshold () { return drawOrderThreshold; @@ -1046,7 +1049,8 @@ public class AnimationState { * track entry only before {@link AnimationState#update(float)} is first called. *

* When using {@link AnimationState#addAnimation(int, Animation, boolean, float)} with a delay <= 0, note the - * {@link #getDelay()} is set using the mix duration from the {@link AnimationStateData}. */ + * {@link #getDelay()} is set using the mix duration from the {@link AnimationStateData}, not a mix duration set + * afterward. */ public float getMixDuration () { return mixDuration; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java index 1994c6911..77955c4ff 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -547,11 +547,12 @@ public class Bone implements Updatable { /** Transforms a world rotation to a local rotation. */ public float worldToLocalRotation (float worldRotation) { float sin = sinDeg(worldRotation), cos = cosDeg(worldRotation); - return atan2(a * sin - c * cos, d * cos - b * sin) * radDeg; + return atan2(a * sin - c * cos, d * cos - b * sin) * radDeg + rotation - shearX; } /** Transforms a local rotation to a world rotation. */ public float localToWorldRotation (float localRotation) { + localRotation -= rotation - shearX; float sin = sinDeg(localRotation), cos = cosDeg(localRotation); return atan2(cos * c + sin * d, cos * a + sin * b) * radDeg; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java index 377f034b3..e759d75e3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java @@ -430,14 +430,23 @@ public class PathConstraint implements Constraint { private void addCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float[] out, int o, boolean tangents) { - if (p < epsilon || Float.isNaN(p)) p = epsilon; + if (p < epsilon || Float.isNaN(p)) { + out[o] = x1; + out[o + 1] = y1; + out[o + 2] = (float)Math.atan2(cy1 - y1, cx1 - x1); + return; + } float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; out[o] = x; out[o + 1] = y; - if (tangents) - out[o + 2] = (float)Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); + if (tangents) { + if (p < 0.001f) + out[o + 2] = (float)Math.atan2(cy1 - y1, cx1 - x1); + else + out[o + 2] = (float)Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); + } } public int getOrder () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java index 19a9de0db..59159f4e8 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java @@ -70,12 +70,12 @@ public class SkeletonRenderer { * previous blend function is not restored, since that could result in unnecessary flushes, depending on what is rendered * next. */ public void draw (Batch batch, Skeleton skeleton) { - if (batch instanceof PolygonSpriteBatch) { - draw((PolygonSpriteBatch)batch, skeleton); - return; - } else if (batch instanceof TwoColorPolygonBatch) { + if (batch instanceof TwoColorPolygonBatch) { draw((TwoColorPolygonBatch)batch, skeleton); return; + } else if (batch instanceof PolygonSpriteBatch) { + draw((PolygonSpriteBatch)batch, skeleton); + return; } VertexEffect vertexEffect = this.vertexEffect; @@ -357,7 +357,7 @@ public class SkeletonRenderer { FloatArray clippedVertices = clipper.getClippedVertices(); ShortArray clippedTriangles = clipper.getClippedTriangles(); if (vertexEffect != null) applyVertexEffect(clippedVertices.items, clippedVertices.size, 6, light, dark); - batch.draw(texture, clippedVertices.items, 0, clippedVertices.size, clippedTriangles.items, 0, + batch.drawTwoColor(texture, clippedVertices.items, 0, clippedVertices.size, clippedTriangles.items, 0, clippedTriangles.size); } else { if (vertexEffect != null) { @@ -386,7 +386,7 @@ public class SkeletonRenderer { vertices[v + 3] = uvs[u + 1]; } } - batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length); + batch.drawTwoColor(texture, vertices, 0, verticesLength, triangles, 0, triangles.length); } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java index c1d8cf652..dd0de951c 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java @@ -31,10 +31,15 @@ package com.esotericsoftware.spine.utils; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.*; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.Mesh; import com.badlogic.gdx.graphics.Mesh.VertexDataType; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.VertexAttribute; import com.badlogic.gdx.graphics.VertexAttributes.Usage; import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.PolygonBatch; import com.badlogic.gdx.graphics.g2d.PolygonRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.glutils.ShaderProgram; @@ -43,13 +48,18 @@ import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.utils.NumberUtils; -public class TwoColorPolygonBatch implements Batch { +/** A batch that renders polygons and performs tinting using a light and dark color. + *

+ * Because an additional vertex attribute is used, the {@link Batch} and {@link PolygonBatch} methods that accept float[] vertex + * data do not perform two color tinting. {@link #drawTwoColor(Texture, float[], int, int)} and + * {@link #drawTwoColor(Texture, float[], int, int, short[], int, int)} are provided to accept float[] vertex data that contains + * two colors per vertex. */ +public class TwoColorPolygonBatch implements PolygonBatch { static final int VERTEX_SIZE = 2 + 1 + 1 + 2; static final int SPRITE_SIZE = 4 * VERTEX_SIZE; private final Mesh mesh; private final float[] vertices; - private final float[] tempSpriteVertices = new float[SPRITE_SIZE]; private final short[] triangles; private final Matrix4 transformMatrix = new Matrix4(); private final Matrix4 projectionMatrix = new Matrix4(); @@ -313,30 +323,6 @@ public class TwoColorPolygonBatch implements Batch { this.vertexIndex = vertexIndex; } - public void draw (Texture texture, float[] polygonVertices, int verticesOffset, int verticesCount, short[] polygonTriangles, - int trianglesOffset, int trianglesCount) { - if (!drawing) throw new IllegalStateException("begin must be called before draw."); - - final short[] triangles = this.triangles; - final float[] vertices = this.vertices; - - if (texture != lastTexture) { - switchTexture(texture); - } else if (triangleIndex + trianglesCount > triangles.length || vertexIndex + verticesCount > vertices.length) // - flush(); - - int triangleIndex = this.triangleIndex; - final int vertexIndex = this.vertexIndex; - final int startVertex = vertexIndex / 6; - - for (int i = trianglesOffset, n = i + trianglesCount; i < n; i++) - triangles[triangleIndex++] = (short)(polygonTriangles[i] + startVertex); - this.triangleIndex = triangleIndex; - - System.arraycopy(polygonVertices, verticesOffset, vertices, vertexIndex, verticesCount); - this.vertexIndex += verticesCount; - } - @Override public void draw (Texture texture, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation, int srcX, int srcY, int srcWidth, int srcHeight, boolean flipX, boolean flipY) { @@ -746,28 +732,74 @@ public class TwoColorPolygonBatch implements Batch { this.vertexIndex = idx; } - /** Draws a rectangle using the given vertices. There must be 4 vertices, each made up of 6 elements in this order: x, y, - * lightColor, darkColor, u, v. The {@link #getColor()} and {@link #getDarkColor()} from the TwoColorPolygonBatch is not + /** Draws polygons using the given vertices and triangles. There must be 4 vertices, each made up of 6 elements in this order: + * x, y, lightColor, darkColor, u, v. The {@link #getColor()} and {@link #getDarkColor()} from the TwoColorPolygonBatch is not * applied. */ - @Override - public void draw (Texture texture, float[] spriteVertices, int offset, int count) { + public void drawTwoColor (Texture texture, float[] polygonVertices, int verticesOffset, int verticesCount, + short[] polygonTriangles, int trianglesOffset, int trianglesCount) { if (!drawing) throw new IllegalStateException("begin must be called before draw."); - // odds are this is a sprite, we meed to convert it - if (spriteVertices.length == 20 && offset == 0 && count == 20) { - final float[] vertices = tempSpriteVertices; - int idx = 0; - for (int i = 0; i < 20; i += 5) { - vertices[idx++] = spriteVertices[i]; - vertices[idx++] = spriteVertices[i + 1]; - vertices[idx++] = spriteVertices[i + 2]; - vertices[idx++] = 0; // dark - vertices[idx++] = spriteVertices[i + 3]; - vertices[idx++] = spriteVertices[i + 4]; - } - spriteVertices = vertices; - count = SPRITE_SIZE; + final short[] triangles = this.triangles; + final float[] vertices = this.vertices; + + if (texture != lastTexture) { + switchTexture(texture); + } else if (triangleIndex + trianglesCount > triangles.length || vertexIndex + verticesCount > vertices.length) // + flush(); + + int triangleIndex = this.triangleIndex; + final int vertexIndex = this.vertexIndex; + final int startVertex = vertexIndex / 6; + + for (int i = trianglesOffset, n = i + trianglesCount; i < n; i++) + triangles[triangleIndex++] = (short)(polygonTriangles[i] + startVertex); + this.triangleIndex = triangleIndex; + + System.arraycopy(polygonVertices, verticesOffset, vertices, vertexIndex, verticesCount); + this.vertexIndex += verticesCount; + } + + /** Draws polygons using the given vertices and triangles in the {@link PolygonBatch} format. There must be 4 vertices, each + * made up of 5 elements in this order: x, y, color, u, v. The {@link #getColor()} and {@link #getDarkColor()} from the + * TwoColorPolygonBatch is not applied. */ + public void draw (Texture texture, float[] polygonVertices, int verticesOffset, int verticesCount, short[] polygonTriangles, + int trianglesOffset, int trianglesCount) { + if (!drawing) throw new IllegalStateException("begin must be called before draw."); + + final short[] triangles = this.triangles; + final float[] vertices = this.vertices; + + if (texture != lastTexture) { + switchTexture(texture); + } else if (triangleIndex + trianglesCount > triangles.length || vertexIndex + verticesCount / 5 * 6 > vertices.length) // + flush(); + + int triangleIndex = this.triangleIndex; + final int vertexIndex = this.vertexIndex; + final int startVertex = vertexIndex / 6; + + for (int i = trianglesOffset, n = i + trianglesCount; i < n; i++) + triangles[triangleIndex++] = (short)(polygonTriangles[i] + startVertex); + this.triangleIndex = triangleIndex; + + int idx = this.vertexIndex; + for (int i = verticesOffset, n = verticesOffset + verticesCount; i < n; i += 5) { + vertices[idx++] = polygonVertices[i]; + vertices[idx++] = polygonVertices[i + 1]; + vertices[idx++] = polygonVertices[i + 2]; + vertices[idx++] = 0; // dark + vertices[idx++] = polygonVertices[i + 3]; + vertices[idx++] = polygonVertices[i + 4]; } + this.vertexIndex = idx; + } + + /** Draws rectangles using the given vertices. There must be 4 vertices, each made up of 6 elements in this order: x, y, + * lightColor, darkColor, u, v. The {@link #getColor()} and {@link #getDarkColor()} from the TwoColorPolygonBatch is not + * applied. */ + public void drawTwoColor (Texture texture, float[] spriteVertices, int offset, int count) { + if (!drawing) throw new IllegalStateException("begin must be called before draw."); + final short[] triangles = this.triangles; final float[] vertices = this.vertices; @@ -794,6 +826,47 @@ public class TwoColorPolygonBatch implements Batch { this.vertexIndex += count; } + /** Draws rectangles using the given vertices in the {@link Batch} format. There must be 4 vertices, each made up of 5 elements + * in this order: x, y, color, u, v. The {@link #getColor()} and {@link #getDarkColor()} from the TwoColorPolygonBatch is not + * applied. */ + @Override + public void draw (Texture texture, float[] spriteVertices, int offset, int count) { + if (!drawing) throw new IllegalStateException("begin must be called before draw."); + + final short[] triangles = this.triangles; + final float[] vertices = this.vertices; + + final int triangleCount = count / 20 * 6; + if (texture != lastTexture) + switchTexture(texture); + else if (triangleIndex + triangleCount > triangles.length || vertexIndex + count / 5 * 6 > vertices.length) // + flush(); + + final int vertexIndex = this.vertexIndex; + int triangleIndex = this.triangleIndex; + short vertex = (short)(vertexIndex / VERTEX_SIZE); + for (int n = triangleIndex + triangleCount; triangleIndex < n; triangleIndex += 6, vertex += 4) { + triangles[triangleIndex] = vertex; + triangles[triangleIndex + 1] = (short)(vertex + 1); + triangles[triangleIndex + 2] = (short)(vertex + 2); + triangles[triangleIndex + 3] = (short)(vertex + 2); + triangles[triangleIndex + 4] = (short)(vertex + 3); + triangles[triangleIndex + 5] = vertex; + } + this.triangleIndex = triangleIndex; + + int idx = this.vertexIndex; + for (int i = offset, n = offset + count; i < n; i += 5) { + vertices[idx++] = spriteVertices[i]; + vertices[idx++] = spriteVertices[i + 1]; + vertices[idx++] = spriteVertices[i + 2]; + vertices[idx++] = 0; // dark + vertices[idx++] = spriteVertices[i + 3]; + vertices[idx++] = spriteVertices[i + 4]; + } + this.vertexIndex = idx; + } + @Override public void draw (TextureRegion region, float x, float y) { draw(region, x, y, region.getRegionWidth(), region.getRegionHeight()); diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs index 282ffdfdd..00cc87aa5 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs @@ -244,6 +244,10 @@ namespace Spine.Unity.Editor { const string DEFAULT_ZSPACING_KEY = "SPINE_DEFAULT_ZSPACING"; public static float defaultZSpacing = DEFAULT_DEFAULT_ZSPACING; + const bool DEFAULT_DEFAULT_INSTANTIATE_LOOP = true; + const string DEFAULT_INSTANTIATE_LOOP_KEY = "SPINE_DEFAULT_INSTANTIATE_LOOP"; + public static bool defaultInstantiateLoop = DEFAULT_DEFAULT_INSTANTIATE_LOOP; + const bool DEFAULT_SHOW_HIERARCHY_ICONS = true; const string SHOW_HIERARCHY_ICONS_KEY = "SPINE_SHOW_HIERARCHY_ICONS"; public static bool showHierarchyIcons = DEFAULT_SHOW_HIERARCHY_ICONS; @@ -327,6 +331,11 @@ namespace Spine.Unity.Editor { if (EditorGUI.EndChangeCheck()) EditorPrefs.SetFloat(DEFAULT_ZSPACING_KEY, defaultZSpacing); + EditorGUI.BeginChangeCheck(); + defaultInstantiateLoop = EditorGUILayout.Toggle(new GUIContent("Default Loop", "Spawn Spine GameObjects with loop enabled."), defaultInstantiateLoop); + if (EditorGUI.EndChangeCheck()) + EditorPrefs.SetBool(DEFAULT_INSTANTIATE_LOOP_KEY, defaultInstantiateLoop); + EditorGUILayout.Space(); EditorGUILayout.LabelField("Handles and Gizmos", EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); @@ -1246,6 +1255,7 @@ namespace Spine.Unity.Editor { throw e; } + newSkeletonAnimation.loop = SpineEditorUtilities.Preferences.defaultInstantiateLoop; newSkeletonAnimation.skeleton.Update(0); newSkeletonAnimation.state.Update(0); newSkeletonAnimation.state.Apply(newSkeletonAnimation.skeleton); diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs index 573051889..29a31dfdf 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs @@ -195,6 +195,7 @@ namespace Spine.Unity.Editor { skin = skin ?? data.DefaultSkin ?? data.Skins.Items[0]; graphic.MeshGenerator.settings.zSpacing = SpineEditorUtilities.Preferences.defaultZSpacing; + graphic.startingLoop = SpineEditorUtilities.Preferences.defaultInstantiateLoop; graphic.Initialize(false); if (skin != null) graphic.Skeleton.SetSkin(skin); graphic.initialSkinName = skin.Name; diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs index e1b798f52..4f339a7e2 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs @@ -258,12 +258,12 @@ namespace Spine.Unity.Editor { GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale); SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren(); foreach (SkeletonUtilityBone utilBone in newUtilityBones) - SkeletonGameObjectsInspector.AttachIcon(utilBone); + SkeletonUtilityInspector.AttachIcon(utilBone); } } else { var bone = (Bone)obj; GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale); - SkeletonGameObjectsInspector.AttachIcon(go.GetComponent()); + SkeletonUtilityInspector.AttachIcon(go.GetComponent()); Selection.activeGameObject = go; EditorGUIUtility.PingObject(go); } @@ -272,7 +272,7 @@ namespace Spine.Unity.Editor { void SpawnOverride () { GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale); go.name = go.name + " [Override]"; - SkeletonGameObjectsInspector.AttachIcon(go.GetComponent()); + SkeletonUtilityInspector.AttachIcon(go.GetComponent()); Selection.activeGameObject = go; EditorGUIUtility.PingObject(go); } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs index 4ccc85a6e..5734a7a7c 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs @@ -41,17 +41,17 @@ namespace Spine.Unity.Editor { using Icons = SpineEditorUtilities.Icons; [CustomEditor(typeof(SkeletonUtility))] - public class SkeletonGameObjectsInspector : UnityEditor.Editor { + public class SkeletonUtilityInspector : UnityEditor.Editor { - SkeletonUtility skeletonGameObjects; + SkeletonUtility skeletonUtility; Skeleton skeleton; SkeletonRenderer skeletonRenderer; bool isPrefab; readonly GUIContent SpawnHierarchyButtonLabel = new GUIContent("Spawn Hierarchy", Icons.skeleton); void OnEnable () { - skeletonGameObjects = (SkeletonUtility)target; - skeletonRenderer = skeletonGameObjects.GetComponent(); + skeletonUtility = (SkeletonUtility)target; + skeletonRenderer = skeletonUtility.GetComponent(); skeleton = skeletonRenderer.Skeleton; if (skeleton == null) { @@ -78,7 +78,7 @@ namespace Spine.Unity.Editor { EditorGUILayout.PropertyField(serializedObject.FindProperty("boneRoot"), SpineInspectorUtility.TempContent("Skeleton Root")); - bool hasRootBone = skeletonGameObjects.boneRoot != null; + bool hasRootBone = skeletonUtility.boneRoot != null; if (!hasRootBone) EditorGUILayout.HelpBox("No hierarchy found. Use Spawn Hierarchy to generate GameObjects for bones.", MessageType.Info); @@ -90,9 +90,9 @@ namespace Spine.Unity.Editor { if (hasRootBone) { if (SpineInspectorUtility.CenteredButton(new GUIContent("Remove Hierarchy"))) { - Undo.RegisterCompleteObjectUndo(skeletonGameObjects, "Remove Hierarchy"); - Undo.DestroyObjectImmediate(skeletonGameObjects.boneRoot.gameObject); - skeletonGameObjects.boneRoot = null; + Undo.RegisterCompleteObjectUndo(skeletonUtility, "Remove Hierarchy"); + Undo.DestroyObjectImmediate(skeletonUtility.boneRoot.gameObject); + skeletonUtility.boneRoot = null; } } } @@ -134,23 +134,23 @@ namespace Spine.Unity.Editor { } void SpawnFollowHierarchy () { - Selection.activeGameObject = skeletonGameObjects.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true); - AttachIconsToChildren(skeletonGameObjects.boneRoot); + Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); } void SpawnFollowHierarchyRootOnly () { - Selection.activeGameObject = skeletonGameObjects.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true); - AttachIconsToChildren(skeletonGameObjects.boneRoot); + Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); } void SpawnOverrideHierarchy () { - Selection.activeGameObject = skeletonGameObjects.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true); - AttachIconsToChildren(skeletonGameObjects.boneRoot); + Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); } void SpawnOverrideHierarchyRootOnly () { - Selection.activeGameObject = skeletonGameObjects.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true); - AttachIconsToChildren(skeletonGameObjects.boneRoot); + Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); } } diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs index 6e70f270c..53bbc5a3f 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs @@ -172,6 +172,13 @@ namespace Spine.Unity { if (skeleton != null) skeleton.SetToSetupPose(); } + ///

+ /// Sets a minimum buffer size for the internal MeshGenerator to prevent excess allocations during animation. + /// + public void EnsureMeshGeneratorCapacity (int minimumVertexCount) { + meshGenerator.EnsureVertexCapacity(minimumVertexCount); + } + /// /// Initialize this component. Attempts to load the SkeletonData and creates the internal Skeleton object and buffers. /// If set to true, it will overwrite internal objects if they were already generated. Otherwise, the initialized component will ignore subsequent calls to initialize. diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SpineMesh.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SpineMesh.cs index 9b76040c5..ea080b115 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SpineMesh.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SpineMesh.cs @@ -455,12 +455,13 @@ namespace Spine.Unity { public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = true) { var settings = this.settings; - if (submeshes.Count - 1 < submeshIndex) { - submeshes.Resize(submeshIndex + 1); - if (submeshes.Items[submeshIndex] == null) - submeshes.Items[submeshIndex] = new ExposedList(); - } + int newSubmeshCount = submeshIndex + 1; + if (submeshes.Items.Length < newSubmeshCount) + submeshes.Resize(newSubmeshCount); + submeshes.Count = newSubmeshCount; var submesh = submeshes.Items[submeshIndex]; + if (submesh == null) + submeshes.Items[submeshIndex] = submesh = new ExposedList(); submesh.Clear(false); var skeleton = instruction.skeleton; @@ -570,10 +571,13 @@ namespace Spine.Unity { // Add data to vertex buffers { int newVertexCount = ovc + attachmentVertexCount; - if (newVertexCount > vertexBuffer.Items.Length) { // Manual ExposedList.Resize() - Array.Resize(ref vertexBuffer.Items, newVertexCount); - Array.Resize(ref uvBuffer.Items, newVertexCount); - Array.Resize(ref colorBuffer.Items, newVertexCount); + int oldArraySize = vertexBuffer.Items.Length; + if (newVertexCount > oldArraySize) { + int newArraySize = (int)(oldArraySize * 1.3f); + if (newArraySize < newVertexCount) newArraySize = newVertexCount; + Array.Resize(ref vertexBuffer.Items, newArraySize); + Array.Resize(ref uvBuffer.Items, newArraySize); + Array.Resize(ref colorBuffer.Items, newArraySize); } vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = newVertexCount; } @@ -1057,6 +1061,38 @@ namespace Spine.Unity { } #endregion + public void EnsureVertexCapacity (int minimumVertexCount, bool inlcudeTintBlack = false, bool includeTangents = false, bool includeNormals = false) { + if (minimumVertexCount > vertexBuffer.Items.Length) { + Array.Resize(ref vertexBuffer.Items, minimumVertexCount); + Array.Resize(ref uvBuffer.Items, minimumVertexCount); + Array.Resize(ref colorBuffer.Items, minimumVertexCount); + + if (inlcudeTintBlack) { + if (uv2 == null) { + uv2 = new ExposedList(minimumVertexCount); + uv3 = new ExposedList(minimumVertexCount); + } + uv2.Resize(minimumVertexCount); + uv3.Resize(minimumVertexCount); + } + + if (includeNormals) { + if (normals == null) + normals = new Vector3[minimumVertexCount]; + else + Array.Resize(ref normals, minimumVertexCount); + + } + + if (includeTangents) { + if (tangents == null) + tangents = new Vector4[minimumVertexCount]; + else + Array.Resize(ref tangents, minimumVertexCount); + } + } + } + public void TrimExcess () { vertexBuffer.TrimExcess(); uvBuffer.TrimExcess(); @@ -1437,13 +1473,13 @@ namespace Spine.Unity { this.hasActiveClipping = other.hasActiveClipping; this.rawVertexCount = other.rawVertexCount; this.attachments.Clear(false); - this.attachments.GrowIfNeeded(other.attachments.Capacity); + this.attachments.EnsureCapacity(other.attachments.Capacity); this.attachments.Count = other.attachments.Count; other.attachments.CopyTo(this.attachments.Items); #endif this.submeshInstructions.Clear(false); - this.submeshInstructions.GrowIfNeeded(other.submeshInstructions.Capacity); + this.submeshInstructions.EnsureCapacity(other.submeshInstructions.Capacity); this.submeshInstructions.Count = other.submeshInstructions.Count; other.submeshInstructions.CopyTo(this.submeshInstructions.Items); } diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Shaders/Spine-Special-Skeleton-Grayscale.shader b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Shaders/Spine-Special-Skeleton-Grayscale.shader new file mode 100644 index 000000000..112c46ebb --- /dev/null +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Shaders/Spine-Special-Skeleton-Grayscale.shader @@ -0,0 +1,105 @@ +// - Unlit +// - Premultiplied Alpha Blending (Optional straight alpha input) +// - Double-sided, no depth + +Shader "Spine/Special/Skeleton Grayscale" { + Properties { + _GrayPhase ("Phase", Range(0, 1)) = 1 + [NoScaleOffset] _MainTex ("MainTex", 2D) = "white" {} + _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0 + } + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" } + Blend One OneMinusSrcAlpha + Cull Off + ZWrite Off + Lighting Off + + Pass { + CGPROGRAM + #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + sampler2D _MainTex; + float _GrayPhase; + + struct VertexInput { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + struct VertexOutput { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + VertexOutput vert (VertexInput v) { + VertexOutput o = (VertexOutput)0; + o.uv = v.uv; + o.vertexColor = v.vertexColor; + o.pos = UnityObjectToClipPos(v.vertex); + return o; + } + + float4 frag (VertexOutput i) : COLOR { + float4 rawColor = tex2D(_MainTex,i.uv); + float finalAlpha = (rawColor.a * i.vertexColor.a); + + #if defined(_STRAIGHT_ALPHA_INPUT) + rawColor.rgb *= rawColor.a; + #endif + + rawColor.rgb *= i.vertexColor.rgb; + + float3 finalColor = lerp(rawColor.rgb, dot(rawColor.rgb, float3(0.3, 0.59, 0.11)), _GrayPhase); + return fixed4(finalColor, finalAlpha); + } + ENDCG + } + + Pass { + Name "Caster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + ZWrite On + ZTest LEqual + + Fog { Mode Off } + Cull Off + Lighting Off + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_shadowcaster + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + sampler2D _MainTex; + fixed _Cutoff; + + struct VertexOutput { + V2F_SHADOW_CASTER; + float2 uv : TEXCOORD1; + }; + + VertexOutput vert (appdata_base v) { + VertexOutput o; + o.uv = v.texcoord; + TRANSFER_SHADOW_CASTER(o) + return o; + } + + float4 frag (VertexOutput i) : COLOR { + fixed4 texcol = tex2D(_MainTex, i.uv); + clip(texcol.a - _Cutoff); + SHADOW_CASTER_FRAGMENT(i) + } + ENDCG + } + } + FallBack "Diffuse" +} \ No newline at end of file diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Shaders/Spine-Special-Skeleton-Grayscale.shader.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Shaders/Spine-Special-Skeleton-Grayscale.shader.meta new file mode 100644 index 000000000..6db4577e4 --- /dev/null +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Shaders/Spine-Special-Skeleton-Grayscale.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: ea7e7c05f36541b4bb280f98ebda8ba1 +timeCreated: 1492385797 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/SkeletonExtensions.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/SkeletonExtensions.cs index 48460214f..bd4286a51 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/SkeletonExtensions.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/SkeletonExtensions.cs @@ -557,7 +557,8 @@ namespace Spine { var drawOrder = skeleton.drawOrder; drawOrder.Clear(false); - drawOrder.GrowIfNeeded(n); + drawOrder.EnsureCapacity(n); + drawOrder.Count = n; System.Array.Copy(slotsItems, drawOrder.Items, n); }