mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Merge branch '3.7-beta' into 3.7-beta-cpp
This commit is contained in:
commit
bb1bdd7046
1
.gitignore
vendored
1
.gitignore
vendored
@ -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.opendb
|
||||||
spine-cocos2dx/example/proj.win32/spine-cocos2d-x.VC.db
|
spine-cocos2dx/example/proj.win32/spine-cocos2d-x.VC.db
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
|
xcshareddata/
|
||||||
|
|
||||||
spine-cocos2d-objc/cocos2d/*
|
spine-cocos2d-objc/cocos2d/*
|
||||||
spine-cocos2d-objc/spine-cocos2d-iphone-objc.xcodeproj/project.xcworkspace/xcshareddata/
|
spine-cocos2d-objc/spine-cocos2d-iphone-objc.xcodeproj/project.xcworkspace/xcshareddata/
|
||||||
|
|||||||
@ -74,7 +74,7 @@ bool AppDelegate::applicationDidFinishLaunching () {
|
|||||||
glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER);
|
glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Size frameSize = glview->getFrameSize();
|
cocos2d::Size frameSize = glview->getFrameSize();
|
||||||
|
|
||||||
vector<string> searchPath;
|
vector<string> searchPath;
|
||||||
|
|
||||||
|
|||||||
@ -70,14 +70,14 @@ bool BatchingExample::init () {
|
|||||||
|
|
||||||
int xMin = _contentSize.width * 0.10f, xMax = _contentSize.width * 0.90f;
|
int xMin = _contentSize.width * 0.10f, xMax = _contentSize.width * 0.90f;
|
||||||
int yMin = 0, yMax = _contentSize.height * 0.7f;
|
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.
|
// Each skeleton node shares the same atlas, skeleton data, and mix times.
|
||||||
SkeletonAnimation* skeletonNode = SkeletonAnimation::createWithData(_skeletonData, false);
|
SkeletonAnimation* skeletonNode = SkeletonAnimation::createWithData(_skeletonData, false);
|
||||||
skeletonNode->setAnimationStateData(_stateData);
|
skeletonNode->setAnimationStateData(_stateData);
|
||||||
|
|
||||||
skeletonNode->setAnimation(0, "walk", true);
|
skeletonNode->setAnimation(0, "walk", true);
|
||||||
skeletonNode->addAnimation(0, "jump", true, RandomHelper::random_int(0, 300) / 100.0f);
|
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
|
// alternative setting two color tint for groups of 10 skeletons
|
||||||
// should end up with #skeletons / 10 batches
|
// should end up with #skeletons / 10 batches
|
||||||
|
|||||||
@ -44,14 +44,14 @@ bool CoinExample::init () {
|
|||||||
if (!LayerColor::initWithColor(Color4B(128, 128, 128, 255))) return false;
|
if (!LayerColor::initWithColor(Color4B(128, 128, 128, 255))) return false;
|
||||||
|
|
||||||
skeletonNode = SkeletonAnimation::createWithBinaryFile("coin-pro.skel", "coin.atlas", 1);
|
skeletonNode = SkeletonAnimation::createWithBinaryFile("coin-pro.skel", "coin.atlas", 1);
|
||||||
skeletonNode->setAnimation(0, "rotate", true);
|
skeletonNode->setAnimation(0, "rotate", true);
|
||||||
// skeletonNode->setTwoColorTint(true);
|
// skeletonNode->setTwoColorTint(true);
|
||||||
|
|
||||||
skeletonNode->setPosition(Vec2(_contentSize.width / 2, 100));
|
skeletonNode->setPosition(Vec2(_contentSize.width / 2, 100));
|
||||||
addChild(skeletonNode);
|
addChild(skeletonNode);
|
||||||
|
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
|
|
||||||
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
||||||
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
||||||
if (!skeletonNode->getDebugBonesEnabled())
|
if (!skeletonNode->getDebugBonesEnabled())
|
||||||
|
|||||||
@ -51,7 +51,7 @@ bool GoblinsExample::init () {
|
|||||||
addChild(skeletonNode);
|
addChild(skeletonNode);
|
||||||
|
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
|
|
||||||
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
||||||
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
||||||
if (!skeletonNode->getDebugBonesEnabled())
|
if (!skeletonNode->getDebugBonesEnabled())
|
||||||
|
|||||||
@ -34,9 +34,9 @@
|
|||||||
|
|
||||||
USING_NS_CC;
|
USING_NS_CC;
|
||||||
using namespace spine;
|
using namespace spine;
|
||||||
|
|
||||||
PowInterpolation pow2(2);
|
PowInterpolation pow2(2);
|
||||||
PowOutInterpolation powOut2(2);
|
PowOutInterpolation powOut2(2);
|
||||||
SwirlVertexEffect effect(400, powOut2);
|
SwirlVertexEffect effect(400, powOut2);
|
||||||
|
|
||||||
Scene* RaptorExample::scene () {
|
Scene* RaptorExample::scene () {
|
||||||
@ -67,7 +67,7 @@ bool RaptorExample::init () {
|
|||||||
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
||||||
if (!skeletonNode->getDebugBonesEnabled()) {
|
if (!skeletonNode->getDebugBonesEnabled()) {
|
||||||
skeletonNode->setDebugBonesEnabled(true);
|
skeletonNode->setDebugBonesEnabled(true);
|
||||||
skeletonNode->setDebugMeshesEnabled(true);
|
skeletonNode->setDebugMeshesEnabled(true);
|
||||||
} else if (skeletonNode->getTimeScale() == 1)
|
} else if (skeletonNode->getTimeScale() == 1)
|
||||||
skeletonNode->setTimeScale(0.3f);
|
skeletonNode->setTimeScale(0.3f);
|
||||||
else
|
else
|
||||||
@ -80,8 +80,8 @@ bool RaptorExample::init () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RaptorExample::update(float fDelta) {
|
void RaptorExample::update(float fDelta) {
|
||||||
swirlTime += fDelta;
|
swirlTime += fDelta;
|
||||||
float percent = spine::MathUtil::fmod(swirlTime, 2);
|
float percent = spine::MathUtil::fmod(swirlTime, 2);
|
||||||
if (percent > 1) percent = 1 - (percent - 1);
|
if (percent > 1) percent = 1 - (percent - 1);
|
||||||
effect.setAngle(pow2.interpolate(-60.0f, 60.0f, percent));
|
effect.setAngle(pow2.interpolate(-60.0f, 60.0f, percent));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,39 +42,39 @@ Scene* SkeletonRendererSeparatorExample::scene () {
|
|||||||
|
|
||||||
bool SkeletonRendererSeparatorExample::init () {
|
bool SkeletonRendererSeparatorExample::init () {
|
||||||
if (!LayerColor::initWithColor(Color4B(128, 128, 128, 255))) return false;
|
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
|
// will render only the front slots of Spineboy
|
||||||
backNode = SkeletonAnimation::createWithJsonFile("spineboy-ess.json", "spineboy.atlas", 0.6f);
|
backNode = SkeletonAnimation::createWithJsonFile("spineboy-ess.json", "spineboy.atlas", 0.6f);
|
||||||
backNode->setMix("walk", "jump", 0.4);
|
backNode->setMix("walk", "jump", 0.4);
|
||||||
backNode->setAnimation(0, "walk", true);
|
backNode->setAnimation(0, "walk", true);
|
||||||
backNode->setSlotsRange(backNode->findSlot("rear-upper-arm")->getData().getIndex(), backNode->findSlot("rear-shin")->getData().getIndex());
|
backNode->setSlotsRange(backNode->findSlot("rear-upper-arm")->getData().getIndex(), backNode->findSlot("rear-shin")->getData().getIndex());
|
||||||
backNode->setPosition(Vec2(_contentSize.width / 2, 20));
|
backNode->setPosition(Vec2(_contentSize.width / 2, 20));
|
||||||
|
|
||||||
// A simple rectangle to go between the front and back slots of Spineboy
|
// A simple rectangle to go between the front and back slots of Spineboy
|
||||||
betweenNode = DrawNode::create();
|
betweenNode = DrawNode::create();
|
||||||
Vec2 rect[4];
|
Vec2 rect[4];
|
||||||
rect[0] = Vec2(0, 0);
|
rect[0] = Vec2(0, 0);
|
||||||
rect[1] = Vec2(40, 0);
|
rect[1] = Vec2(40, 0);
|
||||||
rect[2] = Vec2(40, 200);
|
rect[2] = Vec2(40, 200);
|
||||||
rect[3] = Vec2(0, 200);
|
rect[3] = Vec2(0, 200);
|
||||||
betweenNode->drawPolygon(rect, 4, Color4F(1, 0, 0, 1), 1, Color4F(1, 0, 0, 1));
|
betweenNode->drawPolygon(rect, 4, Color4F(1, 0, 0, 1), 1, Color4F(1, 0, 0, 1));
|
||||||
betweenNode->setPosition(Vec2(_contentSize.width / 2 + 30, 20));
|
betweenNode->setPosition(Vec2(_contentSize.width / 2 + 30, 20));
|
||||||
|
|
||||||
// Spineboy's front, doesn't manage any skeleton, animation or GPU resources, but simply
|
// 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
|
// renders the back slots of Spineboy. The skeleton, animatio state and GPU resources
|
||||||
// are shared with the front node!
|
// are shared with the front node!
|
||||||
frontNode = SkeletonRenderer::createWithSkeleton(backNode->getSkeleton());
|
frontNode = SkeletonRenderer::createWithSkeleton(backNode->getSkeleton());
|
||||||
frontNode->setSlotsRange(frontNode->findSlot("neck")->getData().getIndex(), -1);
|
frontNode->setSlotsRange(frontNode->findSlot("neck")->getData().getIndex(), -1);
|
||||||
frontNode->setPosition(Vec2(_contentSize.width / 2, 20));
|
frontNode->setPosition(Vec2(_contentSize.width / 2, 20));
|
||||||
|
|
||||||
// Add the front, between and back node in the correct order to this scene
|
// Add the front, between and back node in the correct order to this scene
|
||||||
addChild(backNode);
|
addChild(backNode);
|
||||||
addChild(betweenNode);
|
addChild(betweenNode);
|
||||||
addChild(frontNode);
|
addChild(frontNode);
|
||||||
|
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
|
|
||||||
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
||||||
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
||||||
if (!backNode->getDebugBonesEnabled())
|
if (!backNode->getDebugBonesEnabled())
|
||||||
|
|||||||
@ -68,7 +68,7 @@ bool SpineboyExample::init () {
|
|||||||
skeletonNode->setMix("jump", "run", 0.4);
|
skeletonNode->setMix("jump", "run", 0.4);
|
||||||
skeletonNode->setAnimation(0, "walk", true);
|
skeletonNode->setAnimation(0, "walk", true);
|
||||||
TrackEntry* jumpEntry = skeletonNode->addAnimation(0, "jump", false, 1);
|
TrackEntry* jumpEntry = skeletonNode->addAnimation(0, "jump", false, 1);
|
||||||
skeletonNode->addAnimation(0, "run", true);
|
skeletonNode->addAnimation(0, "run", true);
|
||||||
|
|
||||||
skeletonNode->setTrackStartListener(jumpEntry, [] (TrackEntry* entry) {
|
skeletonNode->setTrackStartListener(jumpEntry, [] (TrackEntry* entry) {
|
||||||
log("jumped!");
|
log("jumped!");
|
||||||
@ -81,7 +81,7 @@ bool SpineboyExample::init () {
|
|||||||
addChild(skeletonNode);
|
addChild(skeletonNode);
|
||||||
|
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
|
|
||||||
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
||||||
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
||||||
if (!skeletonNode->getDebugBonesEnabled())
|
if (!skeletonNode->getDebugBonesEnabled())
|
||||||
|
|||||||
@ -50,7 +50,7 @@ bool TankExample::init () {
|
|||||||
addChild(skeletonNode);
|
addChild(skeletonNode);
|
||||||
|
|
||||||
scheduleUpdate();
|
scheduleUpdate();
|
||||||
|
|
||||||
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
|
||||||
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
listener->onTouchBegan = [this] (Touch* touch, cocos2d::Event* event) -> bool {
|
||||||
if (!skeletonNode->getDebugBonesEnabled())
|
if (!skeletonNode->getDebugBonesEnabled())
|
||||||
|
|||||||
@ -35,129 +35,129 @@
|
|||||||
#include "cocos2d.h"
|
#include "cocos2d.h"
|
||||||
|
|
||||||
namespace spine {
|
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);
|
class AttachmentVertices;
|
||||||
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
|
/* Draws a skeleton. */
|
||||||
* attached if the corresponding attachment from the old skin was attached.
|
class SkeletonRenderer: public cocos2d::Node, public cocos2d::BlendProtocol {
|
||||||
* @param skin May be empty string ("") for no skin.*/
|
public:
|
||||||
void setSkin (const std::string& skinName);
|
CREATE_FUNC(SkeletonRenderer);
|
||||||
/** @param skin May be 0 for no skin.*/
|
static SkeletonRenderer* createWithSkeleton(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false);
|
||||||
void setSkin (const char* skinName);
|
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_ */
|
#endif /* SPINE_SKELETONRENDERER_H_ */
|
||||||
|
|||||||
@ -57,7 +57,7 @@ void TwoColorTrianglesCommand::init(float globalOrder, GLuint textureID, GLProgr
|
|||||||
if(_triangles.indexCount % 3 != 0) {
|
if(_triangles.indexCount % 3 != 0) {
|
||||||
int count = _triangles.indexCount;
|
int count = _triangles.indexCount;
|
||||||
_triangles.indexCount = count / 3 * 3;
|
_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;
|
_mv = mv;
|
||||||
|
|
||||||
|
|||||||
@ -329,10 +329,11 @@ namespace Spine {
|
|||||||
|
|
||||||
public float WorldToLocalRotation (float worldRotation) {
|
public float WorldToLocalRotation (float worldRotation) {
|
||||||
float sin = MathUtils.SinDeg(worldRotation), cos = MathUtils.CosDeg(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) {
|
public float LocalToWorldRotation (float localRotation) {
|
||||||
|
localRotation -= rotation - shearX;
|
||||||
float sin = MathUtils.SinDeg(localRotation), cos = MathUtils.CosDeg(localRotation);
|
float sin = MathUtils.SinDeg(localRotation), cos = MathUtils.CosDeg(localRotation);
|
||||||
return MathUtils.Atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.RadDeg;
|
return MathUtils.Atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.RadDeg;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,11 +127,11 @@ public class AnimationState {
|
|||||||
float nextTime = current.trackLast - next.delay;
|
float nextTime = current.trackLast - next.delay;
|
||||||
if (nextTime >= 0) {
|
if (nextTime >= 0) {
|
||||||
next.delay = 0;
|
next.delay = 0;
|
||||||
next.trackTime = nextTime + delta * next.timeScale;
|
next.trackTime = (nextTime / current.timeScale + delta) * next.timeScale;
|
||||||
current.trackTime += currentDelta;
|
current.trackTime += currentDelta;
|
||||||
setCurrent(i, next, true);
|
setCurrent(i, next, true);
|
||||||
while (next.mixingFrom != null) {
|
while (next.mixingFrom != null) {
|
||||||
next.mixTime += currentDelta;
|
next.mixTime += delta;
|
||||||
next = next.mixingFrom;
|
next = next.mixingFrom;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -182,15 +182,8 @@ public class AnimationState {
|
|||||||
return finished;
|
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;
|
from.trackTime += delta * from.timeScale;
|
||||||
to.mixTime += delta * to.timeScale;
|
to.mixTime += delta;
|
||||||
return false;
|
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.
|
||||||
* <p>
|
* <p>
|
||||||
* It may be desired to use {@link AnimationState#setEmptyAnimations(float)} to mix the skeletons back to the setup 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 () {
|
public void clearTracks () {
|
||||||
boolean oldDrainDisabled = queue.drainDisabled;
|
boolean oldDrainDisabled = queue.drainDisabled;
|
||||||
queue.drainDisabled = true;
|
queue.drainDisabled = true;
|
||||||
@ -438,10 +431,10 @@ public class AnimationState {
|
|||||||
queue.drain();
|
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.
|
||||||
* <p>
|
* <p>
|
||||||
* It may be desired to use {@link AnimationState#setEmptyAnimation(int, float)} to mix the skeletons back to the setup 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) {
|
public void clearTrack (int trackIndex) {
|
||||||
if (trackIndex >= tracks.size) return;
|
if (trackIndex >= tracks.size) return;
|
||||||
TrackEntry current = tracks.get(trackIndex);
|
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
|
/** 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)}.
|
* 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
|
* @param delay If > 0, sets {@link TrackEntry#getDelay()}. If <= 0, the delay set is the duration of the previous track entry
|
||||||
* previous track entry minus any mix duration plus the specified <code>delay</code>. If the previous entry is
|
* minus any mix duration (from the {@link AnimationStateData}) plus the specified <code>delay</code> (ie the mix
|
||||||
* looping, its next loop completion is used instead of the duration.
|
* ends at (<code>delay</code> = 0) or before (<code>delay</code> < 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
|
* @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. */
|
* after the {@link AnimationStateListener#dispose(TrackEntry)} event occurs. */
|
||||||
public TrackEntry addAnimation (int trackIndex, Animation animation, boolean loop, float delay) {
|
public TrackEntry addAnimation (int trackIndex, Animation animation, boolean loop, float delay) {
|
||||||
@ -598,9 +592,10 @@ public class AnimationState {
|
|||||||
* {@link #setEmptyAnimation(int, float)}.
|
* {@link #setEmptyAnimation(int, float)}.
|
||||||
* <p>
|
* <p>
|
||||||
* See {@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
|
* @param delay If > 0, sets {@link TrackEntry#getDelay()}. If <= 0, the delay set is the duration of the previous track entry
|
||||||
* previous track entry minus any mix duration plus the specified <code>delay</code>. If the previous entry is
|
* minus any mix duration plus the specified <code>delay</code> (ie the mix ends at (<code>delay</code> = 0) or
|
||||||
* looping, its next loop completion is used instead of the duration.
|
* before (<code>delay</code> < 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
|
* @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. */
|
* after the {@link AnimationStateListener#dispose(TrackEntry)} event occurs. */
|
||||||
public TrackEntry addEmptyAnimation (int trackIndex, float mixDuration, float delay) {
|
public TrackEntry addEmptyAnimation (int trackIndex, float mixDuration, float delay) {
|
||||||
@ -764,8 +759,8 @@ public class AnimationState {
|
|||||||
queue.clear();
|
queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Multiplier for the delta time when the animation state is updated, causing time for all animations to play slower or
|
/** Multiplier for the delta time when the animation state is updated, causing time for all animations and mixes to play slower
|
||||||
* faster. Defaults to 1.
|
* or faster. Defaults to 1.
|
||||||
* <p>
|
* <p>
|
||||||
* See TrackEntry {@link TrackEntry#getTimeScale()} for affecting a single animation. */
|
* See TrackEntry {@link TrackEntry#getTimeScale()} for affecting a single animation. */
|
||||||
public float getTimeScale () {
|
public float getTimeScale () {
|
||||||
@ -859,9 +854,12 @@ public class AnimationState {
|
|||||||
this.loop = loop;
|
this.loop = loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Seconds to postpone playing the animation. When a track entry is the current track entry, <code>delay</code> postpones
|
/** Seconds to postpone playing the animation. When this track entry is the current track entry, <code>delay</code>
|
||||||
* incrementing the {@link #getTrackTime()}. When a track entry is queued, <code>delay</code> is the time from the start of
|
* postpones incrementing the {@link #getTrackTime()}. When this track entry is queued, <code>delay</code> is the time from
|
||||||
* the previous animation to when the track entry will become the current track entry. */
|
* 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 <code>delay</code>).
|
||||||
|
* <p>
|
||||||
|
* {@link #getTimeScale()} affects the delay. */
|
||||||
public float getDelay () {
|
public float getDelay () {
|
||||||
return delay;
|
return delay;
|
||||||
}
|
}
|
||||||
@ -943,10 +941,15 @@ public class AnimationState {
|
|||||||
return Math.min(trackTime + animationStart, animationEnd);
|
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.
|
* faster. Defaults to 1.
|
||||||
* <p>
|
* <p>
|
||||||
* If <code>timeScale</code> 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.
|
||||||
|
* <p>
|
||||||
|
* When using {@link AnimationState#addAnimation(int, Animation, boolean, float)} with a <code>delay</code> <= 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.
|
||||||
* <p>
|
* <p>
|
||||||
* See AnimationState {@link AnimationState#getTimeScale()} for affecting all animations. */
|
* See AnimationState {@link AnimationState#getTimeScale()} for affecting all animations. */
|
||||||
public float getTimeScale () {
|
public float getTimeScale () {
|
||||||
@ -970,11 +973,11 @@ public class AnimationState {
|
|||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Values < 1 mix this animation with the setup pose or the skeleton's previous pose. Defaults to 1, which overwrites the
|
/** Values < 1 mix this animation with the skeleton's current pose (usually the pose resulting from lower tracks). Defaults
|
||||||
* skeleton's previous pose with this animation.
|
* to 1, which overwrites the skeleton's current pose with this animation.
|
||||||
* <p>
|
* <p>
|
||||||
* Typically track 0 is used to completely pose the skeleton, then alpha can be used on higher tracks. It doesn't make sense
|
* Typically track 0 is used to completely pose the skeleton, then alpha is used on higher tracks. It doesn't make sense to
|
||||||
* to use alpha on track 0 if the skeleton pose is from the last frame render. */
|
* use alpha on track 0 if the skeleton pose is from the last frame render. */
|
||||||
public float getAlpha () {
|
public float getAlpha () {
|
||||||
return alpha;
|
return alpha;
|
||||||
}
|
}
|
||||||
@ -984,7 +987,7 @@ public class AnimationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** When the mix percentage ({@link #getMixTime()} / {@link #getMixDuration()}) is less than the
|
/** When the mix percentage ({@link #getMixTime()} / {@link #getMixDuration()}) is less than the
|
||||||
* <code>eventThreshold</code>, event timelines for the animation being mixed out will be applied. Defaults to 0, so event
|
* <code>eventThreshold</code>, 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. */
|
* timelines are not applied for an animation being mixed out. */
|
||||||
public float getEventThreshold () {
|
public float getEventThreshold () {
|
||||||
return eventThreshold;
|
return eventThreshold;
|
||||||
@ -995,8 +998,8 @@ public class AnimationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** When the mix percentage ({@link #getMixTime()} / {@link #getMixDuration()}) is less than the
|
/** When the mix percentage ({@link #getMixTime()} / {@link #getMixDuration()}) is less than the
|
||||||
* <code>attachmentThreshold</code>, attachment timelines for the animation being mixed out will be applied. Defaults to 0,
|
* <code>attachmentThreshold</code>, attachment timelines are applied while this animation is being mixed out. Defaults to
|
||||||
* so attachment timelines are not applied for an animation being mixed out. */
|
* 0, so attachment timelines are not applied for an animation being mixed out. */
|
||||||
public float getAttachmentThreshold () {
|
public float getAttachmentThreshold () {
|
||||||
return attachmentThreshold;
|
return attachmentThreshold;
|
||||||
}
|
}
|
||||||
@ -1006,7 +1009,7 @@ public class AnimationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** When the mix percentage ({@link #getMixTime()} / {@link #getMixDuration()}) is less than the
|
/** When the mix percentage ({@link #getMixTime()} / {@link #getMixDuration()}) is less than the
|
||||||
* <code>drawOrderThreshold</code>, draw order timelines for the animation being mixed out will be applied. Defaults to 0,
|
* <code>drawOrderThreshold</code>, 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. */
|
* so draw order timelines are not applied for an animation being mixed out. */
|
||||||
public float getDrawOrderThreshold () {
|
public float getDrawOrderThreshold () {
|
||||||
return drawOrderThreshold;
|
return drawOrderThreshold;
|
||||||
@ -1046,7 +1049,8 @@ public class AnimationState {
|
|||||||
* track entry only before {@link AnimationState#update(float)} is first called.
|
* track entry only before {@link AnimationState#update(float)} is first called.
|
||||||
* <p>
|
* <p>
|
||||||
* When using {@link AnimationState#addAnimation(int, Animation, boolean, float)} with a <code>delay</code> <= 0, note the
|
* When using {@link AnimationState#addAnimation(int, Animation, boolean, float)} with a <code>delay</code> <= 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 () {
|
public float getMixDuration () {
|
||||||
return mixDuration;
|
return mixDuration;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -547,11 +547,12 @@ public class Bone implements Updatable {
|
|||||||
/** Transforms a world rotation to a local rotation. */
|
/** Transforms a world rotation to a local rotation. */
|
||||||
public float worldToLocalRotation (float worldRotation) {
|
public float worldToLocalRotation (float worldRotation) {
|
||||||
float sin = sinDeg(worldRotation), cos = cosDeg(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. */
|
/** Transforms a local rotation to a world rotation. */
|
||||||
public float localToWorldRotation (float localRotation) {
|
public float localToWorldRotation (float localRotation) {
|
||||||
|
localRotation -= rotation - shearX;
|
||||||
float sin = sinDeg(localRotation), cos = cosDeg(localRotation);
|
float sin = sinDeg(localRotation), cos = cosDeg(localRotation);
|
||||||
return atan2(cos * c + sin * d, cos * a + sin * b) * radDeg;
|
return atan2(cos * c + sin * d, cos * a + sin * b) * radDeg;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
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) {
|
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 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 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;
|
float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
|
||||||
out[o] = x;
|
out[o] = x;
|
||||||
out[o + 1] = y;
|
out[o + 1] = y;
|
||||||
if (tangents)
|
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 (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 () {
|
public int getOrder () {
|
||||||
|
|||||||
@ -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
|
* previous blend function is not restored, since that could result in unnecessary flushes, depending on what is rendered
|
||||||
* next. */
|
* next. */
|
||||||
public void draw (Batch batch, Skeleton skeleton) {
|
public void draw (Batch batch, Skeleton skeleton) {
|
||||||
if (batch instanceof PolygonSpriteBatch) {
|
if (batch instanceof TwoColorPolygonBatch) {
|
||||||
draw((PolygonSpriteBatch)batch, skeleton);
|
|
||||||
return;
|
|
||||||
} else if (batch instanceof TwoColorPolygonBatch) {
|
|
||||||
draw((TwoColorPolygonBatch)batch, skeleton);
|
draw((TwoColorPolygonBatch)batch, skeleton);
|
||||||
return;
|
return;
|
||||||
|
} else if (batch instanceof PolygonSpriteBatch) {
|
||||||
|
draw((PolygonSpriteBatch)batch, skeleton);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VertexEffect vertexEffect = this.vertexEffect;
|
VertexEffect vertexEffect = this.vertexEffect;
|
||||||
@ -357,7 +357,7 @@ public class SkeletonRenderer {
|
|||||||
FloatArray clippedVertices = clipper.getClippedVertices();
|
FloatArray clippedVertices = clipper.getClippedVertices();
|
||||||
ShortArray clippedTriangles = clipper.getClippedTriangles();
|
ShortArray clippedTriangles = clipper.getClippedTriangles();
|
||||||
if (vertexEffect != null) applyVertexEffect(clippedVertices.items, clippedVertices.size, 6, light, dark);
|
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);
|
clippedTriangles.size);
|
||||||
} else {
|
} else {
|
||||||
if (vertexEffect != null) {
|
if (vertexEffect != null) {
|
||||||
@ -386,7 +386,7 @@ public class SkeletonRenderer {
|
|||||||
vertices[v + 3] = uvs[u + 1];
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,10 +31,15 @@
|
|||||||
package com.esotericsoftware.spine.utils;
|
package com.esotericsoftware.spine.utils;
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
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.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.VertexAttributes.Usage;
|
||||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
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.PolygonRegion;
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
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.math.Matrix4;
|
||||||
import com.badlogic.gdx.utils.NumberUtils;
|
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.
|
||||||
|
* <p>
|
||||||
|
* 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 VERTEX_SIZE = 2 + 1 + 1 + 2;
|
||||||
static final int SPRITE_SIZE = 4 * VERTEX_SIZE;
|
static final int SPRITE_SIZE = 4 * VERTEX_SIZE;
|
||||||
|
|
||||||
private final Mesh mesh;
|
private final Mesh mesh;
|
||||||
private final float[] vertices;
|
private final float[] vertices;
|
||||||
private final float[] tempSpriteVertices = new float[SPRITE_SIZE];
|
|
||||||
private final short[] triangles;
|
private final short[] triangles;
|
||||||
private final Matrix4 transformMatrix = new Matrix4();
|
private final Matrix4 transformMatrix = new Matrix4();
|
||||||
private final Matrix4 projectionMatrix = new Matrix4();
|
private final Matrix4 projectionMatrix = new Matrix4();
|
||||||
@ -313,30 +323,6 @@ public class TwoColorPolygonBatch implements Batch {
|
|||||||
this.vertexIndex = vertexIndex;
|
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
|
@Override
|
||||||
public void draw (Texture texture, float x, float y, float originX, float originY, float width, float height, float scaleX,
|
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) {
|
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;
|
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,
|
/** Draws polygons using the given vertices and triangles. There must be 4 vertices, each made up of 6 elements in this order:
|
||||||
* lightColor, darkColor, u, v. The {@link #getColor()} and {@link #getDarkColor()} from the TwoColorPolygonBatch is not
|
* x, y, lightColor, darkColor, u, v. The {@link #getColor()} and {@link #getDarkColor()} from the TwoColorPolygonBatch is not
|
||||||
* applied. */
|
* applied. */
|
||||||
@Override
|
public void drawTwoColor (Texture texture, float[] polygonVertices, int verticesOffset, int verticesCount,
|
||||||
public void draw (Texture texture, float[] spriteVertices, int offset, int count) {
|
short[] polygonTriangles, int trianglesOffset, int trianglesCount) {
|
||||||
if (!drawing) throw new IllegalStateException("begin must be called before draw.");
|
if (!drawing) throw new IllegalStateException("begin must be called before draw.");
|
||||||
|
|
||||||
// odds are this is a sprite, we meed to convert it
|
final short[] triangles = this.triangles;
|
||||||
if (spriteVertices.length == 20 && offset == 0 && count == 20) {
|
final float[] vertices = this.vertices;
|
||||||
final float[] vertices = tempSpriteVertices;
|
|
||||||
int idx = 0;
|
if (texture != lastTexture) {
|
||||||
for (int i = 0; i < 20; i += 5) {
|
switchTexture(texture);
|
||||||
vertices[idx++] = spriteVertices[i];
|
} else if (triangleIndex + trianglesCount > triangles.length || vertexIndex + verticesCount > vertices.length) //
|
||||||
vertices[idx++] = spriteVertices[i + 1];
|
flush();
|
||||||
vertices[idx++] = spriteVertices[i + 2];
|
|
||||||
vertices[idx++] = 0; // dark
|
int triangleIndex = this.triangleIndex;
|
||||||
vertices[idx++] = spriteVertices[i + 3];
|
final int vertexIndex = this.vertexIndex;
|
||||||
vertices[idx++] = spriteVertices[i + 4];
|
final int startVertex = vertexIndex / 6;
|
||||||
}
|
|
||||||
spriteVertices = vertices;
|
for (int i = trianglesOffset, n = i + trianglesCount; i < n; i++)
|
||||||
count = SPRITE_SIZE;
|
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 short[] triangles = this.triangles;
|
||||||
final float[] vertices = this.vertices;
|
final float[] vertices = this.vertices;
|
||||||
|
|
||||||
@ -794,6 +826,47 @@ public class TwoColorPolygonBatch implements Batch {
|
|||||||
this.vertexIndex += count;
|
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
|
@Override
|
||||||
public void draw (TextureRegion region, float x, float y) {
|
public void draw (TextureRegion region, float x, float y) {
|
||||||
draw(region, x, y, region.getRegionWidth(), region.getRegionHeight());
|
draw(region, x, y, region.getRegionWidth(), region.getRegionHeight());
|
||||||
|
|||||||
@ -244,6 +244,10 @@ namespace Spine.Unity.Editor {
|
|||||||
const string DEFAULT_ZSPACING_KEY = "SPINE_DEFAULT_ZSPACING";
|
const string DEFAULT_ZSPACING_KEY = "SPINE_DEFAULT_ZSPACING";
|
||||||
public static float defaultZSpacing = DEFAULT_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 bool DEFAULT_SHOW_HIERARCHY_ICONS = true;
|
||||||
const string SHOW_HIERARCHY_ICONS_KEY = "SPINE_SHOW_HIERARCHY_ICONS";
|
const string SHOW_HIERARCHY_ICONS_KEY = "SPINE_SHOW_HIERARCHY_ICONS";
|
||||||
public static bool showHierarchyIcons = DEFAULT_SHOW_HIERARCHY_ICONS;
|
public static bool showHierarchyIcons = DEFAULT_SHOW_HIERARCHY_ICONS;
|
||||||
@ -327,6 +331,11 @@ namespace Spine.Unity.Editor {
|
|||||||
if (EditorGUI.EndChangeCheck())
|
if (EditorGUI.EndChangeCheck())
|
||||||
EditorPrefs.SetFloat(DEFAULT_ZSPACING_KEY, defaultZSpacing);
|
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.Space();
|
||||||
EditorGUILayout.LabelField("Handles and Gizmos", EditorStyles.boldLabel);
|
EditorGUILayout.LabelField("Handles and Gizmos", EditorStyles.boldLabel);
|
||||||
EditorGUI.BeginChangeCheck();
|
EditorGUI.BeginChangeCheck();
|
||||||
@ -1246,6 +1255,7 @@ namespace Spine.Unity.Editor {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newSkeletonAnimation.loop = SpineEditorUtilities.Preferences.defaultInstantiateLoop;
|
||||||
newSkeletonAnimation.skeleton.Update(0);
|
newSkeletonAnimation.skeleton.Update(0);
|
||||||
newSkeletonAnimation.state.Update(0);
|
newSkeletonAnimation.state.Update(0);
|
||||||
newSkeletonAnimation.state.Apply(newSkeletonAnimation.skeleton);
|
newSkeletonAnimation.state.Apply(newSkeletonAnimation.skeleton);
|
||||||
|
|||||||
@ -195,6 +195,7 @@ namespace Spine.Unity.Editor {
|
|||||||
skin = skin ?? data.DefaultSkin ?? data.Skins.Items[0];
|
skin = skin ?? data.DefaultSkin ?? data.Skins.Items[0];
|
||||||
graphic.MeshGenerator.settings.zSpacing = SpineEditorUtilities.Preferences.defaultZSpacing;
|
graphic.MeshGenerator.settings.zSpacing = SpineEditorUtilities.Preferences.defaultZSpacing;
|
||||||
|
|
||||||
|
graphic.startingLoop = SpineEditorUtilities.Preferences.defaultInstantiateLoop;
|
||||||
graphic.Initialize(false);
|
graphic.Initialize(false);
|
||||||
if (skin != null) graphic.Skeleton.SetSkin(skin);
|
if (skin != null) graphic.Skeleton.SetSkin(skin);
|
||||||
graphic.initialSkinName = skin.Name;
|
graphic.initialSkinName = skin.Name;
|
||||||
|
|||||||
@ -258,12 +258,12 @@ namespace Spine.Unity.Editor {
|
|||||||
GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||||
SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren<SkeletonUtilityBone>();
|
SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren<SkeletonUtilityBone>();
|
||||||
foreach (SkeletonUtilityBone utilBone in newUtilityBones)
|
foreach (SkeletonUtilityBone utilBone in newUtilityBones)
|
||||||
SkeletonGameObjectsInspector.AttachIcon(utilBone);
|
SkeletonUtilityInspector.AttachIcon(utilBone);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var bone = (Bone)obj;
|
var bone = (Bone)obj;
|
||||||
GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||||
SkeletonGameObjectsInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
|
SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
|
||||||
Selection.activeGameObject = go;
|
Selection.activeGameObject = go;
|
||||||
EditorGUIUtility.PingObject(go);
|
EditorGUIUtility.PingObject(go);
|
||||||
}
|
}
|
||||||
@ -272,7 +272,7 @@ namespace Spine.Unity.Editor {
|
|||||||
void SpawnOverride () {
|
void SpawnOverride () {
|
||||||
GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale);
|
||||||
go.name = go.name + " [Override]";
|
go.name = go.name + " [Override]";
|
||||||
SkeletonGameObjectsInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
|
SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
|
||||||
Selection.activeGameObject = go;
|
Selection.activeGameObject = go;
|
||||||
EditorGUIUtility.PingObject(go);
|
EditorGUIUtility.PingObject(go);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,17 +41,17 @@ namespace Spine.Unity.Editor {
|
|||||||
using Icons = SpineEditorUtilities.Icons;
|
using Icons = SpineEditorUtilities.Icons;
|
||||||
|
|
||||||
[CustomEditor(typeof(SkeletonUtility))]
|
[CustomEditor(typeof(SkeletonUtility))]
|
||||||
public class SkeletonGameObjectsInspector : UnityEditor.Editor {
|
public class SkeletonUtilityInspector : UnityEditor.Editor {
|
||||||
|
|
||||||
SkeletonUtility skeletonGameObjects;
|
SkeletonUtility skeletonUtility;
|
||||||
Skeleton skeleton;
|
Skeleton skeleton;
|
||||||
SkeletonRenderer skeletonRenderer;
|
SkeletonRenderer skeletonRenderer;
|
||||||
bool isPrefab;
|
bool isPrefab;
|
||||||
readonly GUIContent SpawnHierarchyButtonLabel = new GUIContent("Spawn Hierarchy", Icons.skeleton);
|
readonly GUIContent SpawnHierarchyButtonLabel = new GUIContent("Spawn Hierarchy", Icons.skeleton);
|
||||||
|
|
||||||
void OnEnable () {
|
void OnEnable () {
|
||||||
skeletonGameObjects = (SkeletonUtility)target;
|
skeletonUtility = (SkeletonUtility)target;
|
||||||
skeletonRenderer = skeletonGameObjects.GetComponent<SkeletonRenderer>();
|
skeletonRenderer = skeletonUtility.GetComponent<SkeletonRenderer>();
|
||||||
skeleton = skeletonRenderer.Skeleton;
|
skeleton = skeletonRenderer.Skeleton;
|
||||||
|
|
||||||
if (skeleton == null) {
|
if (skeleton == null) {
|
||||||
@ -78,7 +78,7 @@ namespace Spine.Unity.Editor {
|
|||||||
|
|
||||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("boneRoot"), SpineInspectorUtility.TempContent("Skeleton Root"));
|
EditorGUILayout.PropertyField(serializedObject.FindProperty("boneRoot"), SpineInspectorUtility.TempContent("Skeleton Root"));
|
||||||
|
|
||||||
bool hasRootBone = skeletonGameObjects.boneRoot != null;
|
bool hasRootBone = skeletonUtility.boneRoot != null;
|
||||||
|
|
||||||
if (!hasRootBone)
|
if (!hasRootBone)
|
||||||
EditorGUILayout.HelpBox("No hierarchy found. Use Spawn Hierarchy to generate GameObjects for bones.", MessageType.Info);
|
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 (hasRootBone) {
|
||||||
if (SpineInspectorUtility.CenteredButton(new GUIContent("Remove Hierarchy"))) {
|
if (SpineInspectorUtility.CenteredButton(new GUIContent("Remove Hierarchy"))) {
|
||||||
Undo.RegisterCompleteObjectUndo(skeletonGameObjects, "Remove Hierarchy");
|
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Remove Hierarchy");
|
||||||
Undo.DestroyObjectImmediate(skeletonGameObjects.boneRoot.gameObject);
|
Undo.DestroyObjectImmediate(skeletonUtility.boneRoot.gameObject);
|
||||||
skeletonGameObjects.boneRoot = null;
|
skeletonUtility.boneRoot = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,23 +134,23 @@ namespace Spine.Unity.Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SpawnFollowHierarchy () {
|
void SpawnFollowHierarchy () {
|
||||||
Selection.activeGameObject = skeletonGameObjects.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
||||||
AttachIconsToChildren(skeletonGameObjects.boneRoot);
|
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpawnFollowHierarchyRootOnly () {
|
void SpawnFollowHierarchyRootOnly () {
|
||||||
Selection.activeGameObject = skeletonGameObjects.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true);
|
||||||
AttachIconsToChildren(skeletonGameObjects.boneRoot);
|
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpawnOverrideHierarchy () {
|
void SpawnOverrideHierarchy () {
|
||||||
Selection.activeGameObject = skeletonGameObjects.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true);
|
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true);
|
||||||
AttachIconsToChildren(skeletonGameObjects.boneRoot);
|
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpawnOverrideHierarchyRootOnly () {
|
void SpawnOverrideHierarchyRootOnly () {
|
||||||
Selection.activeGameObject = skeletonGameObjects.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true);
|
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true);
|
||||||
AttachIconsToChildren(skeletonGameObjects.boneRoot);
|
AttachIconsToChildren(skeletonUtility.boneRoot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -172,6 +172,13 @@ namespace Spine.Unity {
|
|||||||
if (skeleton != null) skeleton.SetToSetupPose();
|
if (skeleton != null) skeleton.SetToSetupPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a minimum buffer size for the internal MeshGenerator to prevent excess allocations during animation.
|
||||||
|
/// </summary>
|
||||||
|
public void EnsureMeshGeneratorCapacity (int minimumVertexCount) {
|
||||||
|
meshGenerator.EnsureVertexCapacity(minimumVertexCount);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize this component. Attempts to load the SkeletonData and creates the internal Skeleton object and buffers.</summary>
|
/// Initialize this component. Attempts to load the SkeletonData and creates the internal Skeleton object and buffers.</summary>
|
||||||
/// <param name="overwrite">If set to <c>true</c>, it will overwrite internal objects if they were already generated. Otherwise, the initialized component will ignore subsequent calls to initialize.</param>
|
/// <param name="overwrite">If set to <c>true</c>, it will overwrite internal objects if they were already generated. Otherwise, the initialized component will ignore subsequent calls to initialize.</param>
|
||||||
|
|||||||
@ -455,12 +455,13 @@ namespace Spine.Unity {
|
|||||||
public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = true) {
|
public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = true) {
|
||||||
var settings = this.settings;
|
var settings = this.settings;
|
||||||
|
|
||||||
if (submeshes.Count - 1 < submeshIndex) {
|
int newSubmeshCount = submeshIndex + 1;
|
||||||
submeshes.Resize(submeshIndex + 1);
|
if (submeshes.Items.Length < newSubmeshCount)
|
||||||
if (submeshes.Items[submeshIndex] == null)
|
submeshes.Resize(newSubmeshCount);
|
||||||
submeshes.Items[submeshIndex] = new ExposedList<int>();
|
submeshes.Count = newSubmeshCount;
|
||||||
}
|
|
||||||
var submesh = submeshes.Items[submeshIndex];
|
var submesh = submeshes.Items[submeshIndex];
|
||||||
|
if (submesh == null)
|
||||||
|
submeshes.Items[submeshIndex] = submesh = new ExposedList<int>();
|
||||||
submesh.Clear(false);
|
submesh.Clear(false);
|
||||||
|
|
||||||
var skeleton = instruction.skeleton;
|
var skeleton = instruction.skeleton;
|
||||||
@ -570,10 +571,13 @@ namespace Spine.Unity {
|
|||||||
// Add data to vertex buffers
|
// Add data to vertex buffers
|
||||||
{
|
{
|
||||||
int newVertexCount = ovc + attachmentVertexCount;
|
int newVertexCount = ovc + attachmentVertexCount;
|
||||||
if (newVertexCount > vertexBuffer.Items.Length) { // Manual ExposedList.Resize()
|
int oldArraySize = vertexBuffer.Items.Length;
|
||||||
Array.Resize(ref vertexBuffer.Items, newVertexCount);
|
if (newVertexCount > oldArraySize) {
|
||||||
Array.Resize(ref uvBuffer.Items, newVertexCount);
|
int newArraySize = (int)(oldArraySize * 1.3f);
|
||||||
Array.Resize(ref colorBuffer.Items, newVertexCount);
|
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;
|
vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = newVertexCount;
|
||||||
}
|
}
|
||||||
@ -1057,6 +1061,38 @@ namespace Spine.Unity {
|
|||||||
}
|
}
|
||||||
#endregion
|
#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<Vector2>(minimumVertexCount);
|
||||||
|
uv3 = new ExposedList<Vector2>(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 () {
|
public void TrimExcess () {
|
||||||
vertexBuffer.TrimExcess();
|
vertexBuffer.TrimExcess();
|
||||||
uvBuffer.TrimExcess();
|
uvBuffer.TrimExcess();
|
||||||
@ -1437,13 +1473,13 @@ namespace Spine.Unity {
|
|||||||
this.hasActiveClipping = other.hasActiveClipping;
|
this.hasActiveClipping = other.hasActiveClipping;
|
||||||
this.rawVertexCount = other.rawVertexCount;
|
this.rawVertexCount = other.rawVertexCount;
|
||||||
this.attachments.Clear(false);
|
this.attachments.Clear(false);
|
||||||
this.attachments.GrowIfNeeded(other.attachments.Capacity);
|
this.attachments.EnsureCapacity(other.attachments.Capacity);
|
||||||
this.attachments.Count = other.attachments.Count;
|
this.attachments.Count = other.attachments.Count;
|
||||||
other.attachments.CopyTo(this.attachments.Items);
|
other.attachments.CopyTo(this.attachments.Items);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this.submeshInstructions.Clear(false);
|
this.submeshInstructions.Clear(false);
|
||||||
this.submeshInstructions.GrowIfNeeded(other.submeshInstructions.Capacity);
|
this.submeshInstructions.EnsureCapacity(other.submeshInstructions.Capacity);
|
||||||
this.submeshInstructions.Count = other.submeshInstructions.Count;
|
this.submeshInstructions.Count = other.submeshInstructions.Count;
|
||||||
other.submeshInstructions.CopyTo(this.submeshInstructions.Items);
|
other.submeshInstructions.CopyTo(this.submeshInstructions.Items);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ea7e7c05f36541b4bb280f98ebda8ba1
|
||||||
|
timeCreated: 1492385797
|
||||||
|
licenseType: Free
|
||||||
|
ShaderImporter:
|
||||||
|
defaultTextures: []
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -557,7 +557,8 @@ namespace Spine {
|
|||||||
|
|
||||||
var drawOrder = skeleton.drawOrder;
|
var drawOrder = skeleton.drawOrder;
|
||||||
drawOrder.Clear(false);
|
drawOrder.Clear(false);
|
||||||
drawOrder.GrowIfNeeded(n);
|
drawOrder.EnsureCapacity(n);
|
||||||
|
drawOrder.Count = n;
|
||||||
System.Array.Copy(slotsItems, drawOrder.Items, n);
|
System.Array.Copy(slotsItems, drawOrder.Items, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user