From f7e1b31ac95285cb4c72c5b1a5733047933b2bdb Mon Sep 17 00:00:00 2001 From: Arnold <40414978+PatriceJiang@users.noreply.github.com> Date: Tue, 3 Dec 2019 10:25:45 +0800 Subject: [PATCH 1/5] adaptor new renderer --- cocos/CMakeLists.txt | 2 +- cocos/base/ccConfig.h | 4 + cocos/editor-support/spine/Animation.cpp | 140 + cocos/editor-support/spine/Animation.h | 620 +-- cocos/editor-support/spine/AnimationState.cpp | 1030 +++++ cocos/editor-support/spine/AnimationState.h | 618 ++- .../spine/AnimationStateData.cpp | 85 + .../editor-support/spine/AnimationStateData.h | 138 +- cocos/editor-support/spine/Atlas.cpp | 356 ++ cocos/editor-support/spine/Atlas.h | 277 +- .../spine/AtlasAttachmentLoader.cpp | 128 + .../spine/AtlasAttachmentLoader.h | 105 +- cocos/editor-support/spine/Attachment.cpp | 51 + cocos/editor-support/spine/Attachment.h | 108 +- .../editor-support/spine/AttachmentLoader.cpp | 52 + cocos/editor-support/spine/AttachmentLoader.h | 132 +- .../spine/AttachmentTimeline.cpp | 124 + .../editor-support/spine/AttachmentTimeline.h | 73 + cocos/editor-support/spine/AttachmentType.h | 45 + .../spine/AttachmentVertices.cpp | 47 +- .../editor-support/spine/AttachmentVertices.h | 47 +- cocos/editor-support/spine/BlendMode.h | 42 + cocos/editor-support/spine/Bone.cpp | 540 +++ cocos/editor-support/spine/Bone.h | 357 +- cocos/editor-support/spine/BoneData.cpp | 139 + cocos/editor-support/spine/BoneData.h | 182 +- .../spine/BoundingBoxAttachment.cpp | 41 + .../spine/BoundingBoxAttachment.h | 84 +- cocos/editor-support/spine/CMakeLists.txt | 218 +- .../spine/ClippingAttachment.cpp | 51 + .../editor-support/spine/ClippingAttachment.h | 96 +- cocos/editor-support/spine/Color.h | 136 +- cocos/editor-support/spine/ColorTimeline.cpp | 143 + cocos/editor-support/spine/ColorTimeline.h | 80 + cocos/editor-support/spine/Constraint.cpp | 44 + cocos/editor-support/spine/Constraint.h | 52 + cocos/editor-support/spine/ContainerUtil.h | 127 + cocos/editor-support/spine/CurveTimeline.cpp | 128 + cocos/editor-support/spine/CurveTimeline.h | 76 + cocos/editor-support/spine/Debug.h | 116 + cocos/editor-support/spine/DeformTimeline.cpp | 296 ++ cocos/editor-support/spine/DeformTimeline.h | 69 + .../spine/DrawOrderTimeline.cpp | 128 + .../editor-support/spine/DrawOrderTimeline.h | 63 + cocos/editor-support/spine/Event.cpp | 95 + cocos/editor-support/spine/Event.h | 142 +- cocos/editor-support/spine/EventData.cpp | 101 + cocos/editor-support/spine/EventData.h | 139 +- cocos/editor-support/spine/EventTimeline.cpp | 115 + cocos/editor-support/spine/EventTimeline.h | 64 + cocos/editor-support/spine/Extension.cpp | 126 + cocos/editor-support/spine/Extension.h | 117 + .../editor-support/spine/HasRendererObject.h | 58 + cocos/editor-support/spine/HashMap.h | 184 + cocos/editor-support/spine/IkConstraint.cpp | 296 ++ cocos/editor-support/spine/IkConstraint.h | 152 +- .../editor-support/spine/IkConstraintData.cpp | 114 + cocos/editor-support/spine/IkConstraintData.h | 138 +- .../spine/IkConstraintTimeline.cpp | 156 + .../spine/IkConstraintTimeline.h | 71 + cocos/editor-support/spine/Json.cpp | 543 +++ cocos/editor-support/spine/Json.h | 160 +- cocos/editor-support/spine/LinkedMesh.cpp | 45 + cocos/editor-support/spine/LinkedMesh.h | 55 + cocos/editor-support/spine/MathUtil.cpp | 127 + cocos/editor-support/spine/MathUtil.h | 129 + cocos/editor-support/spine/MeshAttachment.cpp | 263 ++ cocos/editor-support/spine/MeshAttachment.h | 212 +- cocos/editor-support/spine/MixBlend.h | 45 + cocos/editor-support/spine/MixDirection.h | 43 + cocos/editor-support/spine/PathAttachment.cpp | 60 + cocos/editor-support/spine/PathAttachment.h | 100 +- cocos/editor-support/spine/PathConstraint.cpp | 570 +++ cocos/editor-support/spine/PathConstraint.h | 206 +- .../spine/PathConstraintData.cpp | 144 + .../editor-support/spine/PathConstraintData.h | 177 +- .../spine/PathConstraintMixTimeline.cpp | 123 + .../spine/PathConstraintMixTimeline.h | 68 + .../spine/PathConstraintPositionTimeline.cpp | 113 + .../spine/PathConstraintPositionTimeline.h | 67 + .../spine/PathConstraintSpacingTimeline.cpp | 99 + .../spine/PathConstraintSpacingTimeline.h | 51 + .../editor-support/spine/PointAttachment.cpp | 82 + cocos/editor-support/spine/PointAttachment.h | 115 +- cocos/editor-support/spine/Pool.h | 74 + cocos/editor-support/spine/PositionMode.h | 40 + cocos/editor-support/spine/RTTI.cpp | 65 + cocos/editor-support/spine/RTTI.h | 74 + .../editor-support/spine/RegionAttachment.cpp | 283 ++ cocos/editor-support/spine/RegionAttachment.h | 173 +- cocos/editor-support/spine/RotateMode.h | 41 + cocos/editor-support/spine/RotateTimeline.cpp | 138 + cocos/editor-support/spine/RotateTimeline.h | 70 + cocos/editor-support/spine/ScaleTimeline.cpp | 149 + cocos/editor-support/spine/ScaleTimeline.h | 51 + cocos/editor-support/spine/ShearTimeline.cpp | 110 + cocos/editor-support/spine/ShearTimeline.h | 51 + cocos/editor-support/spine/Skeleton.cpp | 652 +++ cocos/editor-support/spine/Skeleton.h | 404 +- .../spine/SkeletonAnimation.cpp | 203 +- .../editor-support/spine/SkeletonAnimation.h | 103 +- cocos/editor-support/spine/SkeletonBatch.cpp | 130 +- cocos/editor-support/spine/SkeletonBatch.h | 62 +- cocos/editor-support/spine/SkeletonBinary.cpp | 1052 +++++ cocos/editor-support/spine/SkeletonBinary.h | 173 +- cocos/editor-support/spine/SkeletonBounds.cpp | 237 + cocos/editor-support/spine/SkeletonBounds.h | 204 +- .../editor-support/spine/SkeletonClipping.cpp | 335 ++ cocos/editor-support/spine/SkeletonClipping.h | 123 +- cocos/editor-support/spine/SkeletonData.cpp | 221 + cocos/editor-support/spine/SkeletonData.h | 240 +- cocos/editor-support/spine/SkeletonJson.cpp | 1250 ++++++ cocos/editor-support/spine/SkeletonJson.h | 148 +- .../editor-support/spine/SkeletonRenderer.cpp | 1538 ++++--- cocos/editor-support/spine/SkeletonRenderer.h | 275 +- .../spine/SkeletonTwoColorBatch.cpp | 393 +- .../spine/SkeletonTwoColorBatch.h | 120 +- cocos/editor-support/spine/Skin.cpp | 145 + cocos/editor-support/spine/Skin.h | 201 +- cocos/editor-support/spine/Slot.cpp | 114 + cocos/editor-support/spine/Slot.h | 203 +- cocos/editor-support/spine/SlotData.cpp | 95 + cocos/editor-support/spine/SlotData.h | 181 +- cocos/editor-support/spine/SpacingMode.h | 41 + cocos/editor-support/spine/SpineObject.cpp | 65 + cocos/editor-support/spine/SpineObject.h | 59 + cocos/editor-support/spine/SpineString.h | 212 + cocos/editor-support/spine/TextureLoader.cpp | 42 + cocos/editor-support/spine/TextureLoader.h | 51 + cocos/editor-support/spine/Timeline.cpp | 48 + cocos/editor-support/spine/Timeline.h | 71 + cocos/editor-support/spine/TimelineType.h | 53 + .../spine/TransformConstraint.cpp | 382 ++ .../spine/TransformConstraint.h | 151 +- .../spine/TransformConstraintData.cpp | 122 + .../spine/TransformConstraintData.h | 146 +- .../spine/TransformConstraintTimeline.cpp | 146 + .../spine/TransformConstraintTimeline.h | 70 + cocos/editor-support/spine/TransformMode.h | 43 + .../spine/TranslateTimeline.cpp | 129 + .../editor-support/spine/TranslateTimeline.h | 72 + cocos/editor-support/spine/Triangulator.cpp | 300 ++ cocos/editor-support/spine/Triangulator.h | 95 +- .../editor-support/spine/TwoColorTimeline.cpp | 180 + cocos/editor-support/spine/TwoColorTimeline.h | 80 + cocos/editor-support/spine/Updatable.cpp | 44 + cocos/editor-support/spine/Updatable.h | 49 + cocos/editor-support/spine/Vector.h | 222 + .../editor-support/spine/VertexAttachment.cpp | 162 + cocos/editor-support/spine/VertexAttachment.h | 132 +- cocos/editor-support/spine/VertexEffect.cpp | 159 + cocos/editor-support/spine/VertexEffect.h | 146 +- cocos/editor-support/spine/Vertices.h | 43 + cocos/editor-support/spine/dll.h | 65 +- cocos/editor-support/spine/extension.h | 363 +- cocos/editor-support/spine/spine-cocos2dx.cpp | 215 +- cocos/editor-support/spine/spine-cocos2dx.h | 79 +- cocos/editor-support/spine/spine.h | 116 +- cocos/renderer/backend/ProgramState.cpp | 1 - cocos/scripting/lua-bindings/CMakeLists.txt | 4 +- .../lua-bindings/auto/lua_cocos2dx_auto.cpp | 222 +- .../lua-bindings/auto/lua_cocos2dx_auto.hpp | 15 +- .../auto/lua_cocos2dx_extension_auto.cpp | 268 +- .../auto/lua_cocos2dx_spine_auto.cpp | 520 ++- .../auto/lua_cocos2dx_spine_auto.hpp | 4 + .../auto/lua_cocos2dx_studio_auto.cpp | 96 +- .../manual/lua_module_register.cpp | 3 +- .../manual/spine/LuaSkeletonAnimation.cpp | 3 +- .../spine/lua_cocos2dx_spine_manual.cpp | 68 +- tests/cpp-tests/CMakeLists.txt | 4 +- .../cpp-tests/Classes/SpineTest/SpineTest.cpp | 166 +- tests/cpp-tests/Classes/SpineTest/SpineTest.h | 9 +- tests/cpp-tests/Classes/controller.cpp | 2 +- tests/cpp-tests/Resources/spine/coin-pro.skel | Bin 0 -> 2569 bytes tests/cpp-tests/Resources/spine/coin.atlas | 48 +- tests/cpp-tests/Resources/spine/coin.png | Bin 56635 -> 402363 bytes .../Resources/spine/goblins-pro.json | 1101 +---- tests/cpp-tests/Resources/spine/goblins.png | Bin 210574 -> 212961 bytes .../cpp-tests/Resources/spine/raptor-pro.json | 3834 +---------------- tests/cpp-tests/Resources/spine/raptor.atlas | 158 +- tests/cpp-tests/Resources/spine/raptor.png | Bin 522372 -> 480392 bytes .../Resources/spine/spineboy-pro.json | 1 + .../cpp-tests/Resources/spine/spineboy.atlas | 169 +- tests/cpp-tests/Resources/spine/spineboy.png | Bin 585797 -> 260801 bytes tests/cpp-tests/Resources/spine/tank-pro.skel | Bin 0 -> 47634 bytes tests/cpp-tests/Resources/spine/tank.png | Bin 594397 -> 611484 bytes 186 files changed, 23425 insertions(+), 10800 deletions(-) create mode 100644 cocos/editor-support/spine/Animation.cpp create mode 100644 cocos/editor-support/spine/AnimationState.cpp create mode 100644 cocos/editor-support/spine/AnimationStateData.cpp create mode 100644 cocos/editor-support/spine/Atlas.cpp create mode 100644 cocos/editor-support/spine/AtlasAttachmentLoader.cpp create mode 100644 cocos/editor-support/spine/Attachment.cpp create mode 100644 cocos/editor-support/spine/AttachmentLoader.cpp create mode 100644 cocos/editor-support/spine/AttachmentTimeline.cpp create mode 100644 cocos/editor-support/spine/AttachmentTimeline.h create mode 100644 cocos/editor-support/spine/AttachmentType.h create mode 100644 cocos/editor-support/spine/BlendMode.h create mode 100644 cocos/editor-support/spine/Bone.cpp create mode 100644 cocos/editor-support/spine/BoneData.cpp create mode 100644 cocos/editor-support/spine/BoundingBoxAttachment.cpp create mode 100644 cocos/editor-support/spine/ClippingAttachment.cpp create mode 100644 cocos/editor-support/spine/ColorTimeline.cpp create mode 100644 cocos/editor-support/spine/ColorTimeline.h create mode 100644 cocos/editor-support/spine/Constraint.cpp create mode 100644 cocos/editor-support/spine/Constraint.h create mode 100644 cocos/editor-support/spine/ContainerUtil.h create mode 100644 cocos/editor-support/spine/CurveTimeline.cpp create mode 100644 cocos/editor-support/spine/CurveTimeline.h create mode 100644 cocos/editor-support/spine/Debug.h create mode 100644 cocos/editor-support/spine/DeformTimeline.cpp create mode 100644 cocos/editor-support/spine/DeformTimeline.h create mode 100644 cocos/editor-support/spine/DrawOrderTimeline.cpp create mode 100644 cocos/editor-support/spine/DrawOrderTimeline.h create mode 100644 cocos/editor-support/spine/Event.cpp create mode 100644 cocos/editor-support/spine/EventData.cpp create mode 100644 cocos/editor-support/spine/EventTimeline.cpp create mode 100644 cocos/editor-support/spine/EventTimeline.h create mode 100644 cocos/editor-support/spine/Extension.cpp create mode 100644 cocos/editor-support/spine/Extension.h create mode 100644 cocos/editor-support/spine/HasRendererObject.h create mode 100644 cocos/editor-support/spine/HashMap.h create mode 100644 cocos/editor-support/spine/IkConstraint.cpp create mode 100644 cocos/editor-support/spine/IkConstraintData.cpp create mode 100644 cocos/editor-support/spine/IkConstraintTimeline.cpp create mode 100644 cocos/editor-support/spine/IkConstraintTimeline.h create mode 100644 cocos/editor-support/spine/Json.cpp create mode 100644 cocos/editor-support/spine/LinkedMesh.cpp create mode 100644 cocos/editor-support/spine/LinkedMesh.h create mode 100644 cocos/editor-support/spine/MathUtil.cpp create mode 100644 cocos/editor-support/spine/MathUtil.h create mode 100644 cocos/editor-support/spine/MeshAttachment.cpp create mode 100644 cocos/editor-support/spine/MixBlend.h create mode 100644 cocos/editor-support/spine/MixDirection.h create mode 100644 cocos/editor-support/spine/PathAttachment.cpp create mode 100644 cocos/editor-support/spine/PathConstraint.cpp create mode 100644 cocos/editor-support/spine/PathConstraintData.cpp create mode 100644 cocos/editor-support/spine/PathConstraintMixTimeline.cpp create mode 100644 cocos/editor-support/spine/PathConstraintMixTimeline.h create mode 100644 cocos/editor-support/spine/PathConstraintPositionTimeline.cpp create mode 100644 cocos/editor-support/spine/PathConstraintPositionTimeline.h create mode 100644 cocos/editor-support/spine/PathConstraintSpacingTimeline.cpp create mode 100644 cocos/editor-support/spine/PathConstraintSpacingTimeline.h create mode 100644 cocos/editor-support/spine/PointAttachment.cpp create mode 100644 cocos/editor-support/spine/Pool.h create mode 100644 cocos/editor-support/spine/PositionMode.h create mode 100644 cocos/editor-support/spine/RTTI.cpp create mode 100644 cocos/editor-support/spine/RTTI.h create mode 100644 cocos/editor-support/spine/RegionAttachment.cpp create mode 100644 cocos/editor-support/spine/RotateMode.h create mode 100644 cocos/editor-support/spine/RotateTimeline.cpp create mode 100644 cocos/editor-support/spine/RotateTimeline.h create mode 100644 cocos/editor-support/spine/ScaleTimeline.cpp create mode 100644 cocos/editor-support/spine/ScaleTimeline.h create mode 100644 cocos/editor-support/spine/ShearTimeline.cpp create mode 100644 cocos/editor-support/spine/ShearTimeline.h create mode 100644 cocos/editor-support/spine/Skeleton.cpp create mode 100644 cocos/editor-support/spine/SkeletonBinary.cpp create mode 100644 cocos/editor-support/spine/SkeletonBounds.cpp create mode 100644 cocos/editor-support/spine/SkeletonClipping.cpp create mode 100644 cocos/editor-support/spine/SkeletonData.cpp create mode 100644 cocos/editor-support/spine/SkeletonJson.cpp create mode 100644 cocos/editor-support/spine/Skin.cpp create mode 100644 cocos/editor-support/spine/Slot.cpp create mode 100644 cocos/editor-support/spine/SlotData.cpp create mode 100644 cocos/editor-support/spine/SpacingMode.h create mode 100644 cocos/editor-support/spine/SpineObject.cpp create mode 100644 cocos/editor-support/spine/SpineObject.h create mode 100644 cocos/editor-support/spine/SpineString.h create mode 100644 cocos/editor-support/spine/TextureLoader.cpp create mode 100644 cocos/editor-support/spine/TextureLoader.h create mode 100644 cocos/editor-support/spine/Timeline.cpp create mode 100644 cocos/editor-support/spine/Timeline.h create mode 100644 cocos/editor-support/spine/TimelineType.h create mode 100644 cocos/editor-support/spine/TransformConstraint.cpp create mode 100644 cocos/editor-support/spine/TransformConstraintData.cpp create mode 100644 cocos/editor-support/spine/TransformConstraintTimeline.cpp create mode 100644 cocos/editor-support/spine/TransformConstraintTimeline.h create mode 100644 cocos/editor-support/spine/TransformMode.h create mode 100644 cocos/editor-support/spine/TranslateTimeline.cpp create mode 100644 cocos/editor-support/spine/TranslateTimeline.h create mode 100644 cocos/editor-support/spine/Triangulator.cpp create mode 100644 cocos/editor-support/spine/TwoColorTimeline.cpp create mode 100644 cocos/editor-support/spine/TwoColorTimeline.h create mode 100644 cocos/editor-support/spine/Updatable.cpp create mode 100644 cocos/editor-support/spine/Updatable.h create mode 100644 cocos/editor-support/spine/Vector.h create mode 100644 cocos/editor-support/spine/VertexAttachment.cpp create mode 100644 cocos/editor-support/spine/VertexEffect.cpp create mode 100644 cocos/editor-support/spine/Vertices.h create mode 100644 tests/cpp-tests/Resources/spine/coin-pro.skel create mode 100644 tests/cpp-tests/Resources/spine/spineboy-pro.json create mode 100644 tests/cpp-tests/Resources/spine/tank-pro.skel diff --git a/cocos/CMakeLists.txt b/cocos/CMakeLists.txt index 50349bfbd0f2..3e82380d3a14 100644 --- a/cocos/CMakeLists.txt +++ b/cocos/CMakeLists.txt @@ -49,7 +49,7 @@ include(audio/CMakeLists.txt) # default value for cocos2dx extensions modules build option(BUILD_EDITOR_COCOSTUDIO "Build editor support for cocostudio" ON) -option(BUILD_EDITOR_SPINE "Build editor support for spine" OFF) +option(BUILD_EDITOR_SPINE "Build editor support for spine" ON) option(BUILD_EXTENSIONS "Build extension library" ON) if(BUILD_EDITOR_COCOSTUDIO) diff --git a/cocos/base/ccConfig.h b/cocos/base/ccConfig.h index 8c1df270c323..2e5298a2a572 100644 --- a/cocos/base/ccConfig.h +++ b/cocos/base/ccConfig.h @@ -357,3 +357,7 @@ THE SOFTWARE. #ifndef CC_STRIP_FPS #define CC_STRIP_FPS 0 #endif + +#ifndef CC_USE_NEW_RENDERER +#define CC_USE_NEW_RENDERER 1 +#endif \ No newline at end of file diff --git a/cocos/editor-support/spine/Animation.cpp b/cocos/editor-support/spine/Animation.cpp new file mode 100644 index 000000000000..359dda81efa1 --- /dev/null +++ b/cocos/editor-support/spine/Animation.cpp @@ -0,0 +1,140 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include +#include +#include + +#include + +using namespace spine; + +Animation::Animation(const String &name, Vector &timelines, float duration) : + _timelines(timelines), + _duration(duration), + _name(name) { + assert(_name.length() > 0); +} + +Animation::~Animation() { + ContainerUtil::cleanUpVectorOfPointers(_timelines); +} + +void Animation::apply(Skeleton &skeleton, float lastTime, float time, bool loop, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + if (loop && _duration != 0) { + time = MathUtil::fmod(time, _duration); + if (lastTime > 0) { + lastTime = MathUtil::fmod(lastTime, _duration); + } + } + + for (size_t i = 0, n = _timelines.size(); i < n; ++i) { + _timelines[i]->apply(skeleton, lastTime, time, pEvents, alpha, blend, direction); + } +} + +const String &Animation::getName() { + return _name; +} + +Vector &Animation::getTimelines() { + return _timelines; +} + +float Animation::getDuration() { + return _duration; +} + +void Animation::setDuration(float inValue) { + _duration = inValue; +} + +int Animation::binarySearch(Vector &values, float target, int step) { + int low = 0; + int size = (int)values.size(); + int high = size / step - 2; + if (high == 0) { + return step; + } + + int current = (int) (static_cast(high) >> 1); + while (true) { + if (values[(current + 1) * step] <= target) { + low = current + 1; + } else { + high = current; + } + + if (low == high) { + return (low + 1) * step; + } + + current = (int) (static_cast(low + high) >> 1); + } +} + +int Animation::binarySearch(Vector &values, float target) { + int low = 0; + int size = (int)values.size(); + int high = size - 2; + if (high == 0) { + return 1; + } + + int current = (int) (static_cast(high) >> 1); + while (true) { + if (values[(current + 1)] <= target) { + low = current + 1; + } else { + high = current; + } + + if (low == high) { + return (low + 1); + } + + current = (int) (static_cast(low + high) >> 1); + } +} + +int Animation::linearSearch(Vector &values, float target, int step) { + for (int i = 0, last = (int)values.size() - step; i <= last; i += step) { + if (values[i] > target) { + return i; + } + } + + return -1; +} diff --git a/cocos/editor-support/spine/Animation.h b/cocos/editor-support/spine/Animation.h index 296f8e26ea90..684a324cdf3b 100644 --- a/cocos/editor-support/spine/Animation.h +++ b/cocos/editor-support/spine/Animation.h @@ -1,588 +1,118 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_ANIMATION_H_ -#define SPINE_ANIMATION_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spTimeline spTimeline; -struct spSkeleton; - -typedef struct spAnimation { - const char* const name; - float duration; - - int timelinesCount; - spTimeline** timelines; - -#ifdef __cplusplus - spAnimation() : - name(0), - duration(0), - timelinesCount(0), - timelines(0) { - } -#endif -} spAnimation; - -typedef enum { - SP_MIX_POSE_SETUP, - SP_MIX_POSE_CURRENT, - SP_MIX_POSE_CURRENT_LAYERED -} spMixPose; - -typedef enum { - SP_MIX_DIRECTION_IN, - SP_MIX_DIRECTION_OUT -} spMixDirection; - -SP_API spAnimation* spAnimation_create (const char* name, int timelinesCount); -SP_API void spAnimation_dispose (spAnimation* self); - -/** Poses the skeleton at the specified time for this animation. - * @param lastTime The last time the animation was applied. - * @param events Any triggered events are added. May be null.*/ -SP_API void spAnimation_apply (const spAnimation* self, struct spSkeleton* skeleton, float lastTime, float time, int loop, - spEvent** events, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction); - -#ifdef SPINE_SHORT_NAMES -typedef spAnimation Animation; -#define Animation_create(...) spAnimation_create(__VA_ARGS__) -#define Animation_dispose(...) spAnimation_dispose(__VA_ARGS__) -#define Animation_apply(...) spAnimation_apply(__VA_ARGS__) -#endif - -/**/ - -typedef enum { - SP_TIMELINE_ROTATE, - SP_TIMELINE_TRANSLATE, - SP_TIMELINE_SCALE, - SP_TIMELINE_SHEAR, - SP_TIMELINE_ATTACHMENT, - SP_TIMELINE_COLOR, - SP_TIMELINE_DEFORM, - SP_TIMELINE_EVENT, - SP_TIMELINE_DRAWORDER, - SP_TIMELINE_IKCONSTRAINT, - SP_TIMELINE_TRANSFORMCONSTRAINT, - SP_TIMELINE_PATHCONSTRAINTPOSITION, - SP_TIMELINE_PATHCONSTRAINTSPACING, - SP_TIMELINE_PATHCONSTRAINTMIX, - SP_TIMELINE_TWOCOLOR -} spTimelineType; - -struct spTimeline { - const spTimelineType type; - const void* const vtable; - -#ifdef __cplusplus - spTimeline() : - type(SP_TIMELINE_SCALE), - vtable(0) { - } -#endif -}; - -SP_API void spTimeline_dispose (spTimeline* self); -SP_API void spTimeline_apply (const spTimeline* self, struct spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents, - int* eventsCount, float alpha, spMixPose pose, spMixDirection direction); -SP_API int spTimeline_getPropertyId (const spTimeline* self); - -#ifdef SPINE_SHORT_NAMES -typedef spTimeline Timeline; -#define TIMELINE_SCALE SP_TIMELINE_SCALE -#define TIMELINE_ROTATE SP_TIMELINE_ROTATE -#define TIMELINE_TRANSLATE SP_TIMELINE_TRANSLATE -#define TIMELINE_COLOR SP_TIMELINE_COLOR -#define TIMELINE_ATTACHMENT SP_TIMELINE_ATTACHMENT -#define TIMELINE_EVENT SP_TIMELINE_EVENT -#define TIMELINE_DRAWORDER SP_TIMELINE_DRAWORDER -#define Timeline_dispose(...) spTimeline_dispose(__VA_ARGS__) -#define Timeline_apply(...) spTimeline_apply(__VA_ARGS__) -#endif - -/**/ - -typedef struct spCurveTimeline { - spTimeline super; - float* curves; /* type, x, y, ... */ - -#ifdef __cplusplus - spCurveTimeline() : - super(), - curves(0) { - } -#endif -} spCurveTimeline; +#ifndef Spine_Animation_h +#define Spine_Animation_h -SP_API void spCurveTimeline_setLinear (spCurveTimeline* self, int frameIndex); -SP_API void spCurveTimeline_setStepped (spCurveTimeline* self, int frameIndex); +#include +#include +#include +#include +#include -/* Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. - * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of - * the difference between the keyframe's values. */ -SP_API void spCurveTimeline_setCurve (spCurveTimeline* self, int frameIndex, float cx1, float cy1, float cx2, float cy2); -SP_API float spCurveTimeline_getCurvePercent (const spCurveTimeline* self, int frameIndex, float percent); +namespace spine { +class Timeline; -#ifdef SPINE_SHORT_NAMES -typedef spCurveTimeline CurveTimeline; -#define CurveTimeline_setLinear(...) spCurveTimeline_setLinear(__VA_ARGS__) -#define CurveTimeline_setStepped(...) spCurveTimeline_setStepped(__VA_ARGS__) -#define CurveTimeline_setCurve(...) spCurveTimeline_setCurve(__VA_ARGS__) -#define CurveTimeline_getCurvePercent(...) spCurveTimeline_getCurvePercent(__VA_ARGS__) -#endif +class Skeleton; -/**/ +class Event; -typedef struct spBaseTimeline { - spCurveTimeline super; - int const framesCount; - float* const frames; /* time, angle, ... for rotate. time, x, y, ... for translate and scale. */ - int boneIndex; +class SP_API Animation : public SpineObject { + friend class AnimationState; -#ifdef __cplusplus - spBaseTimeline() : - super(), - framesCount(0), - frames(0), - boneIndex(0) { - } -#endif -} spBaseTimeline; + friend class TrackEntry; -/**/ + friend class AnimationStateData; -static const int ROTATE_PREV_TIME = -2, ROTATE_PREV_ROTATION = -1; -static const int ROTATE_ROTATION = 1; -static const int ROTATE_ENTRIES = 2; + friend class AttachmentTimeline; -typedef struct spBaseTimeline spRotateTimeline; + friend class ColorTimeline; -SP_API spRotateTimeline* spRotateTimeline_create (int framesCount); + friend class DeformTimeline; -SP_API void spRotateTimeline_setFrame (spRotateTimeline* self, int frameIndex, float time, float angle); + friend class DrawOrderTimeline; -#ifdef SPINE_SHORT_NAMES -typedef spRotateTimeline RotateTimeline; -#define RotateTimeline_create(...) spRotateTimeline_create(__VA_ARGS__) -#define RotateTimeline_setFrame(...) spRotateTimeline_setFrame(__VA_ARGS__) -#endif + friend class EventTimeline; -/**/ + friend class IkConstraintTimeline; -static const int TRANSLATE_ENTRIES = 3; + friend class PathConstraintMixTimeline; -typedef struct spBaseTimeline spTranslateTimeline; + friend class PathConstraintPositionTimeline; -SP_API spTranslateTimeline* spTranslateTimeline_create (int framesCount); + friend class PathConstraintSpacingTimeline; -SP_API void spTranslateTimeline_setFrame (spTranslateTimeline* self, int frameIndex, float time, float x, float y); + friend class RotateTimeline; -#ifdef SPINE_SHORT_NAMES -typedef spTranslateTimeline TranslateTimeline; -#define TranslateTimeline_create(...) spTranslateTimeline_create(__VA_ARGS__) -#define TranslateTimeline_setFrame(...) spTranslateTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -typedef struct spBaseTimeline spScaleTimeline; - -SP_API spScaleTimeline* spScaleTimeline_create (int framesCount); - -SP_API void spScaleTimeline_setFrame (spScaleTimeline* self, int frameIndex, float time, float x, float y); - -#ifdef SPINE_SHORT_NAMES -typedef spScaleTimeline ScaleTimeline; -#define ScaleTimeline_create(...) spScaleTimeline_create(__VA_ARGS__) -#define ScaleTimeline_setFrame(...) spScaleTimeline_setFrame(__VA_ARGS__) -#endif + friend class ScaleTimeline; -/**/ + friend class ShearTimeline; -typedef struct spBaseTimeline spShearTimeline; + friend class TransformConstraintTimeline; -SP_API spShearTimeline* spShearTimeline_create (int framesCount); + friend class TranslateTimeline; -SP_API void spShearTimeline_setFrame (spShearTimeline* self, int frameIndex, float time, float x, float y); + friend class TwoColorTimeline; -#ifdef SPINE_SHORT_NAMES -typedef spShearTimeline ShearTimeline; -#define ShearTimeline_create(...) spShearTimeline_create(__VA_ARGS__) -#define ShearTimeline_setFrame(...) spShearTimeline_setFrame(__VA_ARGS__) -#endif +public: + Animation(const String &name, Vector &timelines, float duration); -/**/ + ~Animation(); -static const int COLOR_ENTRIES = 5; + /// Applies all the animation's timelines to the specified skeleton. + /// See also Timeline::apply(Skeleton&, float, float, Vector, float, MixPose, MixDirection) + void apply(Skeleton &skeleton, float lastTime, float time, bool loop, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction); -typedef struct spColorTimeline { - spCurveTimeline super; - int const framesCount; - float* const frames; /* time, r, g, b, a, ... */ - int slotIndex; + const String &getName(); -#ifdef __cplusplus - spColorTimeline() : - super(), - framesCount(0), - frames(0), - slotIndex(0) { - } -#endif -} spColorTimeline; + Vector &getTimelines(); -SP_API spColorTimeline* spColorTimeline_create (int framesCount); + float getDuration(); -SP_API void spColorTimeline_setFrame (spColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a); + void setDuration(float inValue); -#ifdef SPINE_SHORT_NAMES -typedef spColorTimeline ColorTimeline; -#define ColorTimeline_create(...) spColorTimeline_create(__VA_ARGS__) -#define ColorTimeline_setFrame(...) spColorTimeline_setFrame(__VA_ARGS__) -#endif -/**/ -static const int TWOCOLOR_ENTRIES = 8; +private: + Vector _timelines; + float _duration; + String _name; -typedef struct spTwoColorTimeline { - spCurveTimeline super; - int const framesCount; - float* const frames; /* time, r, g, b, a, ... */ - int slotIndex; + /// @param target After the first and before the last entry. + static int binarySearch(Vector &values, float target, int step); -#ifdef __cplusplus - spTwoColorTimeline() : - super(), - framesCount(0), - frames(0), - slotIndex(0) { - } -#endif -} spTwoColorTimeline; + /// @param target After the first and before the last entry. + static int binarySearch(Vector &values, float target); -SP_API spTwoColorTimeline* spTwoColorTimeline_create (int framesCount); - -SP_API void spTwoColorTimeline_setFrame (spTwoColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2); - -#ifdef SPINE_SHORT_NAMES -typedef spTwoColorTimeline TwoColorTimeline; -#define TwoColorTimeline_create(...) spTwoColorTimeline_create(__VA_ARGS__) -#define TwoColorTimeline_setFrame(...) spTwoColorTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -typedef struct spAttachmentTimeline { - spTimeline super; - int const framesCount; - float* const frames; /* time, ... */ - int slotIndex; - const char** const attachmentNames; - -#ifdef __cplusplus - spAttachmentTimeline() : - super(), - framesCount(0), - frames(0), - slotIndex(0), - attachmentNames(0) { - } -#endif -} spAttachmentTimeline; - -SP_API spAttachmentTimeline* spAttachmentTimeline_create (int framesCount); - -/* @param attachmentName May be 0. */ -SP_API void spAttachmentTimeline_setFrame (spAttachmentTimeline* self, int frameIndex, float time, const char* attachmentName); - -#ifdef SPINE_SHORT_NAMES -typedef spAttachmentTimeline AttachmentTimeline; -#define AttachmentTimeline_create(...) spAttachmentTimeline_create(__VA_ARGS__) -#define AttachmentTimeline_setFrame(...) spAttachmentTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -typedef struct spEventTimeline { - spTimeline super; - int const framesCount; - float* const frames; /* time, ... */ - spEvent** const events; - -#ifdef __cplusplus - spEventTimeline() : - super(), - framesCount(0), - frames(0), - events(0) { - } -#endif -} spEventTimeline; - -SP_API spEventTimeline* spEventTimeline_create (int framesCount); - -SP_API void spEventTimeline_setFrame (spEventTimeline* self, int frameIndex, spEvent* event); - -#ifdef SPINE_SHORT_NAMES -typedef spEventTimeline EventTimeline; -#define EventTimeline_create(...) spEventTimeline_create(__VA_ARGS__) -#define EventTimeline_setFrame(...) spEventTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -typedef struct spDrawOrderTimeline { - spTimeline super; - int const framesCount; - float* const frames; /* time, ... */ - const int** const drawOrders; - int const slotsCount; - -#ifdef __cplusplus - spDrawOrderTimeline() : - super(), - framesCount(0), - frames(0), - drawOrders(0), - slotsCount(0) { - } -#endif -} spDrawOrderTimeline; - -SP_API spDrawOrderTimeline* spDrawOrderTimeline_create (int framesCount, int slotsCount); - -SP_API void spDrawOrderTimeline_setFrame (spDrawOrderTimeline* self, int frameIndex, float time, const int* drawOrder); - -#ifdef SPINE_SHORT_NAMES -typedef spDrawOrderTimeline DrawOrderTimeline; -#define DrawOrderTimeline_create(...) spDrawOrderTimeline_create(__VA_ARGS__) -#define DrawOrderTimeline_setFrame(...) spDrawOrderTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -typedef struct spDeformTimeline { - spCurveTimeline super; - int const framesCount; - float* const frames; /* time, ... */ - int const frameVerticesCount; - const float** const frameVertices; - int slotIndex; - spAttachment* attachment; - -#ifdef __cplusplus - spDeformTimeline() : - super(), - framesCount(0), - frames(0), - frameVerticesCount(0), - frameVertices(0), - slotIndex(0) { - } -#endif -} spDeformTimeline; - -SP_API spDeformTimeline* spDeformTimeline_create (int framesCount, int frameVerticesCount); - -SP_API void spDeformTimeline_setFrame (spDeformTimeline* self, int frameIndex, float time, float* vertices); - -#ifdef SPINE_SHORT_NAMES -typedef spDeformTimeline DeformTimeline; -#define DeformTimeline_create(...) spDeformTimeline_create(__VA_ARGS__) -#define DeformTimeline_setFrame(...) spDeformTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -static const int IKCONSTRAINT_ENTRIES = 3; - -typedef struct spIkConstraintTimeline { - spCurveTimeline super; - int const framesCount; - float* const frames; /* time, mix, bendDirection, ... */ - int ikConstraintIndex; - -#ifdef __cplusplus - spIkConstraintTimeline() : - super(), - framesCount(0), - frames(0), - ikConstraintIndex(0) { - } -#endif -} spIkConstraintTimeline; - -SP_API spIkConstraintTimeline* spIkConstraintTimeline_create (int framesCount); - -SP_API void spIkConstraintTimeline_setFrame (spIkConstraintTimeline* self, int frameIndex, float time, float mix, int bendDirection); - -#ifdef SPINE_SHORT_NAMES -typedef spIkConstraintTimeline IkConstraintTimeline; -#define IkConstraintTimeline_create(...) spIkConstraintTimeline_create(__VA_ARGS__) -#define IkConstraintTimeline_setFrame(...) spIkConstraintTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -static const int TRANSFORMCONSTRAINT_ENTRIES = 5; - -typedef struct spTransformConstraintTimeline { - spCurveTimeline super; - int const framesCount; - float* const frames; /* time, rotate mix, translate mix, scale mix, shear mix, ... */ - int transformConstraintIndex; - -#ifdef __cplusplus - spTransformConstraintTimeline() : - super(), - framesCount(0), - frames(0), - transformConstraintIndex(0) { - } -#endif -} spTransformConstraintTimeline; - -SP_API spTransformConstraintTimeline* spTransformConstraintTimeline_create (int framesCount); - -SP_API void spTransformConstraintTimeline_setFrame (spTransformConstraintTimeline* self, int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix); - -#ifdef SPINE_SHORT_NAMES -typedef spTransformConstraintTimeline TransformConstraintTimeline; -#define TransformConstraintTimeline_create(...) spTransformConstraintTimeline_create(__VA_ARGS__) -#define TransformConstraintTimeline_setFrame(...) spTransformConstraintTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -static const int PATHCONSTRAINTPOSITION_ENTRIES = 2; - -typedef struct spPathConstraintPositionTimeline { - spCurveTimeline super; - int const framesCount; - float* const frames; /* time, rotate mix, translate mix, scale mix, shear mix, ... */ - int pathConstraintIndex; - -#ifdef __cplusplus - spPathConstraintPositionTimeline() : - super(), - framesCount(0), - frames(0), - pathConstraintIndex(0) { - } -#endif -} spPathConstraintPositionTimeline; - -SP_API spPathConstraintPositionTimeline* spPathConstraintPositionTimeline_create (int framesCount); - -SP_API void spPathConstraintPositionTimeline_setFrame (spPathConstraintPositionTimeline* self, int frameIndex, float time, float value); - -#ifdef SPINE_SHORT_NAMES -typedef spPathConstraintPositionTimeline PathConstraintPositionTimeline; -#define PathConstraintPositionTimeline_create(...) spPathConstraintPositionTimeline_create(__VA_ARGS__) -#define PathConstraintPositionTimeline_setFrame(...) spPathConstraintPositionTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -static const int PATHCONSTRAINTSPACING_ENTRIES = 2; - -typedef struct spPathConstraintSpacingTimeline { - spCurveTimeline super; - int const framesCount; - float* const frames; /* time, rotate mix, translate mix, scale mix, shear mix, ... */ - int pathConstraintIndex; - -#ifdef __cplusplus - spPathConstraintSpacingTimeline() : - super(), - framesCount(0), - frames(0), - pathConstraintIndex(0) { - } -#endif -} spPathConstraintSpacingTimeline; - -SP_API spPathConstraintSpacingTimeline* spPathConstraintSpacingTimeline_create (int framesCount); - -SP_API void spPathConstraintSpacingTimeline_setFrame (spPathConstraintSpacingTimeline* self, int frameIndex, float time, float value); - -#ifdef SPINE_SHORT_NAMES -typedef spPathConstraintSpacingTimeline PathConstraintSpacingTimeline; -#define PathConstraintSpacingTimeline_create(...) spPathConstraintSpacingTimeline_create(__VA_ARGS__) -#define PathConstraintSpacingTimeline_setFrame(...) spPathConstraintSpacingTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -static const int PATHCONSTRAINTMIX_ENTRIES = 3; - -typedef struct spPathConstraintMixTimeline { - spCurveTimeline super; - int const framesCount; - float* const frames; /* time, rotate mix, translate mix, scale mix, shear mix, ... */ - int pathConstraintIndex; - -#ifdef __cplusplus - spPathConstraintMixTimeline() : - super(), - framesCount(0), - frames(0), - pathConstraintIndex(0) { - } -#endif -} spPathConstraintMixTimeline; - -SP_API spPathConstraintMixTimeline* spPathConstraintMixTimeline_create (int framesCount); - -SP_API void spPathConstraintMixTimeline_setFrame (spPathConstraintMixTimeline* self, int frameIndex, float time, float rotateMix, float translateMix); - -#ifdef SPINE_SHORT_NAMES -typedef spPathConstraintMixTimeline PathConstraintMixTimeline; -#define PathConstraintMixTimeline_create(...) spPathConstraintMixTimeline_create(__VA_ARGS__) -#define PathConstraintMixTimeline_setFrame(...) spPathConstraintMixTimeline_setFrame(__VA_ARGS__) -#endif - -/**/ - -#ifdef __cplusplus + static int linearSearch(Vector &values, float target, int step); +}; } -#endif -#endif /* SPINE_ANIMATION_H_ */ +#endif /* Spine_Animation_h */ diff --git a/cocos/editor-support/spine/AnimationState.cpp b/cocos/editor-support/spine/AnimationState.cpp new file mode 100644 index 000000000000..6597dfd52f61 --- /dev/null +++ b/cocos/editor-support/spine/AnimationState.cpp @@ -0,0 +1,1030 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace spine; + +void dummyOnAnimationEventFunc(AnimationState *state, spine::EventType type, TrackEntry *entry, Event *event = NULL) { + SP_UNUSED(state); + SP_UNUSED(type); + SP_UNUSED(entry); + SP_UNUSED(event); +} + +TrackEntry::TrackEntry() : _animation(NULL), _next(NULL), _mixingFrom(NULL), _mixingTo(0), _trackIndex(0), _loop(false), _holdPrevious(false), + _eventThreshold(0), _attachmentThreshold(0), _drawOrderThreshold(0), _animationStart(0), + _animationEnd(0), _animationLast(0), _nextAnimationLast(0), _delay(0), _trackTime(0), + _trackLast(0), _nextTrackLast(0), _trackEnd(0), _timeScale(1.0f), _alpha(0), _mixTime(0), + _mixDuration(0), _interruptAlpha(0), _totalAlpha(0), _mixBlend(MixBlend_Replace), + _listener(dummyOnAnimationEventFunc) { +} + +TrackEntry::~TrackEntry() { } + +int TrackEntry::getTrackIndex() { return _trackIndex; } + +Animation *TrackEntry::getAnimation() { return _animation; } + +bool TrackEntry::getLoop() { return _loop; } + +void TrackEntry::setLoop(bool inValue) { _loop = inValue; } + +bool TrackEntry::getHoldPrevious() { return _holdPrevious; } + +void TrackEntry::setHoldPrevious(bool inValue) { _holdPrevious = inValue; } + +float TrackEntry::getDelay() { return _delay; } + +void TrackEntry::setDelay(float inValue) { _delay = inValue; } + +float TrackEntry::getTrackTime() { return _trackTime; } + +void TrackEntry::setTrackTime(float inValue) { _trackTime = inValue; } + +float TrackEntry::getTrackEnd() { return _trackEnd; } + +void TrackEntry::setTrackEnd(float inValue) { _trackEnd = inValue; } + +float TrackEntry::getAnimationStart() { return _animationStart; } + +void TrackEntry::setAnimationStart(float inValue) { _animationStart = inValue; } + +float TrackEntry::getAnimationEnd() { return _animationEnd; } + +void TrackEntry::setAnimationEnd(float inValue) { _animationEnd = inValue; } + +float TrackEntry::getAnimationLast() { return _animationLast; } + +void TrackEntry::setAnimationLast(float inValue) { + _animationLast = inValue; + _nextAnimationLast = inValue; +} + +float TrackEntry::getAnimationTime() { + if (_loop) { + float duration = _animationEnd - _animationStart; + if (duration == 0) { + return _animationStart; + } + + return MathUtil::fmod(_trackTime, duration) + _animationStart; + } + + return MathUtil::min(_trackTime + _animationStart, _animationEnd); +} + +float TrackEntry::getTimeScale() { return _timeScale; } + +void TrackEntry::setTimeScale(float inValue) { _timeScale = inValue; } + +float TrackEntry::getAlpha() { return _alpha; } + +void TrackEntry::setAlpha(float inValue) { _alpha = inValue; } + +float TrackEntry::getEventThreshold() { return _eventThreshold; } + +void TrackEntry::setEventThreshold(float inValue) { _eventThreshold = inValue; } + +float TrackEntry::getAttachmentThreshold() { return _attachmentThreshold; } + +void TrackEntry::setAttachmentThreshold(float inValue) { _attachmentThreshold = inValue; } + +float TrackEntry::getDrawOrderThreshold() { return _drawOrderThreshold; } + +void TrackEntry::setDrawOrderThreshold(float inValue) { _drawOrderThreshold = inValue; } + +TrackEntry *TrackEntry::getNext() { return _next; } + +bool TrackEntry::isComplete() { + return _trackTime >= _animationEnd - _animationStart; +} + +float TrackEntry::getMixTime() { return _mixTime; } + +void TrackEntry::setMixTime(float inValue) { _mixTime = inValue; } + +float TrackEntry::getMixDuration() { return _mixDuration; } + +void TrackEntry::setMixDuration(float inValue) { _mixDuration = inValue; } + +TrackEntry *TrackEntry::getMixingFrom() { return _mixingFrom; } + +TrackEntry *TrackEntry::getMixingTo() { return _mixingTo; } + +void TrackEntry::setMixBlend(MixBlend blend) { _mixBlend = blend; } + +MixBlend TrackEntry::getMixBlend() { return _mixBlend; } + +void TrackEntry::resetRotationDirections() { + _timelinesRotation.clear(); +} + +void TrackEntry::setListener(AnimationStateListener inValue) { + _listener = inValue; +} + +void TrackEntry::reset() { + _animation = NULL; + _next = NULL; + _mixingFrom = NULL; + _mixingTo = NULL; + + setRendererObject(NULL); + + _timelineMode.clear(); + _timelineHoldMix.clear(); + _timelinesRotation.clear(); + + _listener = dummyOnAnimationEventFunc; +} + +EventQueueEntry::EventQueueEntry(EventType eventType, TrackEntry *trackEntry, Event *event) : + _type(eventType), + _entry(trackEntry), + _event(event) { +} + +EventQueue *EventQueue::newEventQueue(AnimationState &state, Pool &trackEntryPool) { + return new(__FILE__, __LINE__) EventQueue(state, trackEntryPool); +} + +EventQueueEntry EventQueue::newEventQueueEntry(EventType eventType, TrackEntry *entry, Event *event) { + return EventQueueEntry(eventType, entry, event); +} + +EventQueue::EventQueue(AnimationState &state, Pool &trackEntryPool) : _state(state), + _trackEntryPool(trackEntryPool), + _drainDisabled(false) { +} + +EventQueue::~EventQueue() { +} + +void EventQueue::start(TrackEntry *entry) { + _eventQueueEntries.add(newEventQueueEntry(EventType_Start, entry)); + _state._animationsChanged = true; +} + +void EventQueue::interrupt(TrackEntry *entry) { + _eventQueueEntries.add(newEventQueueEntry(EventType_Interrupt, entry)); +} + +void EventQueue::end(TrackEntry *entry) { + _eventQueueEntries.add(newEventQueueEntry(EventType_End, entry)); + _state._animationsChanged = true; +} + +void EventQueue::dispose(TrackEntry *entry) { + _eventQueueEntries.add(newEventQueueEntry(EventType_Dispose, entry)); +} + +void EventQueue::complete(TrackEntry *entry) { + _eventQueueEntries.add(newEventQueueEntry(EventType_Complete, entry)); +} + +void EventQueue::event(TrackEntry *entry, Event *event) { + _eventQueueEntries.add(newEventQueueEntry(EventType_Event, entry, event)); +} + +/// Raises all events in the queue and drains the queue. +void EventQueue::drain() { + if (_drainDisabled) { + return; + } + + _drainDisabled = true; + + AnimationState &state = _state; + + // Don't cache _eventQueueEntries.size() so callbacks can queue their own events (eg, call setAnimation in AnimationState_Complete). + for (size_t i = 0; i < _eventQueueEntries.size(); ++i) { + EventQueueEntry *queueEntry = &_eventQueueEntries[i]; + TrackEntry *trackEntry = queueEntry->_entry; + + switch (queueEntry->_type) { + case EventType_Start: + case EventType_Interrupt: + case EventType_Complete: + trackEntry->_listener(&state, queueEntry->_type, trackEntry, NULL); + state._listener(&state, queueEntry->_type, trackEntry, NULL); + break; + case EventType_End: + trackEntry->_listener(&state, queueEntry->_type, trackEntry, NULL); + state._listener(&state, queueEntry->_type, trackEntry, NULL); + /* Yes, we want to fall through here */ + case EventType_Dispose: + trackEntry->_listener(&state, EventType_Dispose, trackEntry, NULL); + state._listener(&state, EventType_Dispose, trackEntry, NULL); + trackEntry->reset(); + _trackEntryPool.free(trackEntry); + break; + case EventType_Event: + trackEntry->_listener(&state, queueEntry->_type, trackEntry, queueEntry->_event); + state._listener(&state, queueEntry->_type, trackEntry, queueEntry->_event); + break; + } + } + _eventQueueEntries.clear(); + + _drainDisabled = false; +} + +const int Subsequent = 0; +const int First = 1; +const int Hold = 2; +const int HoldMix = 3; + +AnimationState::AnimationState(AnimationStateData *data) : + _data(data), + _queue(EventQueue::newEventQueue(*this, _trackEntryPool)), + _animationsChanged(false), + _listener(dummyOnAnimationEventFunc), + _timeScale(1) { +} + +AnimationState::~AnimationState() { + for (size_t i = 0; i < _tracks.size(); i++) { + TrackEntry* entry = _tracks[i]; + if (entry) { + TrackEntry* from = entry->_mixingFrom; + while (from) { + TrackEntry* curr = from; + from = curr->_mixingFrom; + delete curr; + } + TrackEntry* next = entry->_next; + while (next) { + TrackEntry* curr = next; + next = curr->_next; + delete curr; + } + delete entry; + } + } + delete _queue; +} + +void AnimationState::update(float delta) { + delta *= _timeScale; + for (size_t i = 0, n = _tracks.size(); i < n; ++i) { + TrackEntry *currentP = _tracks[i]; + if (currentP == NULL) { + continue; + } + + TrackEntry ¤t = *currentP; + + current._animationLast = current._nextAnimationLast; + current._trackLast = current._nextTrackLast; + + float currentDelta = delta * current._timeScale; + + if (current._delay > 0) { + current._delay -= currentDelta; + if (current._delay > 0) { + continue; + } + currentDelta = -current._delay; + current._delay = 0; + } + + TrackEntry *next = current._next; + if (next != NULL) { + // When the next entry's delay is passed, change to the next entry, preserving leftover time. + float nextTime = current._trackLast - next->_delay; + if (nextTime >= 0) { + next->_delay = 0; + next->_trackTime = current._timeScale == 0 ? 0 : (nextTime / current._timeScale + delta) * next->_timeScale; + current._trackTime += currentDelta; + setCurrent(i, next, true); + while (next->_mixingFrom != NULL) { + next->_mixTime += delta; + next = next->_mixingFrom; + } + continue; + } + } else if (current._trackLast >= current._trackEnd && current._mixingFrom == NULL) { + // clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom. + _tracks[i] = NULL; + + _queue->end(currentP); + disposeNext(currentP); + continue; + } + + if (current._mixingFrom != NULL && updateMixingFrom(currentP, delta)) { + // End mixing from entries once all have completed. + TrackEntry *from = current._mixingFrom; + current._mixingFrom = NULL; + if (from != NULL) from->_mixingTo = NULL; + while (from != NULL) { + _queue->end(from); + from = from->_mixingFrom; + } + } + + current._trackTime += currentDelta; + } + + _queue->drain(); +} + +bool AnimationState::apply(Skeleton &skeleton) { + if (_animationsChanged) { + animationsChanged(); + } + + bool applied = false; + for (size_t i = 0, n = _tracks.size(); i < n; ++i) { + TrackEntry *currentP = _tracks[i]; + if (currentP == NULL || currentP->_delay > 0) { + continue; + } + + TrackEntry ¤t = *currentP; + + applied = true; + MixBlend blend = i == 0 ? MixBlend_First : current._mixBlend; + + // apply mixing from entries first. + float mix = current._alpha; + if (current._mixingFrom != NULL) { + mix *= applyMixingFrom(currentP, skeleton, blend); + } else if (current._trackTime >= current._trackEnd && current._next == NULL) { + mix = 0; // Set to setup pose the last time the entry will be applied. + } + + // apply current entry. + float animationLast = current._animationLast, animationTime = current.getAnimationTime(); + size_t timelineCount = current._animation->_timelines.size(); + Vector &timelines = current._animation->_timelines; + if ((i == 0 && mix == 1) || blend == MixBlend_Add) { + for (size_t ii = 0; ii < timelineCount; ++ii) { + timelines[ii]->apply(skeleton, animationLast, animationTime, &_events, mix, blend, + MixDirection_In); + } + } else { + Vector &timelineMode = current._timelineMode; + + bool firstFrame = current._timelinesRotation.size() == 0; + if (firstFrame) { + current._timelinesRotation.setSize(timelines.size() << 1, 0); + } + Vector &timelinesRotation = current._timelinesRotation; + + for (size_t ii = 0; ii < timelineCount; ++ii) { + Timeline *timeline = timelines[ii]; + assert(timeline); + + MixBlend timelineBlend = timelineMode[ii] == Subsequent ? blend : MixBlend_Setup; + + RotateTimeline *rotateTimeline = NULL; + if (timeline->getRTTI().isExactly(RotateTimeline::rtti)) { + rotateTimeline = static_cast(timeline); + } + + if (rotateTimeline != NULL) { + applyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, + firstFrame); + } else { + timeline->apply(skeleton, animationLast, animationTime, &_events, mix, timelineBlend, MixDirection_In); + } + } + } + + queueEvents(currentP, animationTime); + _events.clear(); + current._nextAnimationLast = animationTime; + current._nextTrackLast = current._trackTime; + } + + _queue->drain(); + return applied; +} + +void AnimationState::clearTracks() { + bool oldDrainDisabled = _queue->_drainDisabled; + _queue->_drainDisabled = true; + for (size_t i = 0, n = _tracks.size(); i < n; ++i) { + clearTrack(i); + } + _tracks.clear(); + _queue->_drainDisabled = oldDrainDisabled; + _queue->drain(); +} + +void AnimationState::clearTrack(size_t trackIndex) { + if (trackIndex >= _tracks.size()) { + return; + } + + TrackEntry *current = _tracks[trackIndex]; + if (current == NULL) { + return; + } + + _queue->end(current); + + disposeNext(current); + + TrackEntry *entry = current; + while (true) { + TrackEntry *from = entry->_mixingFrom; + if (from == NULL) { + break; + } + + _queue->end(from); + entry->_mixingFrom = NULL; + entry->_mixingTo = NULL; + entry = from; + } + + _tracks[current->_trackIndex] = NULL; + + _queue->drain(); +} + +TrackEntry *AnimationState::setAnimation(size_t trackIndex, const String &animationName, bool loop) { + Animation *animation = _data->_skeletonData->findAnimation(animationName); + assert(animation != NULL); + + return setAnimation(trackIndex, animation, loop); +} + +TrackEntry *AnimationState::setAnimation(size_t trackIndex, Animation *animation, bool loop) { + assert(animation != NULL); + + bool interrupt = true; + TrackEntry *current = expandToIndex(trackIndex); + if (current != NULL) { + if (current->_nextTrackLast == -1) { + // Don't mix from an entry that was never applied. + _tracks[trackIndex] = current->_mixingFrom; + _queue->interrupt(current); + _queue->end(current); + disposeNext(current); + current = current->_mixingFrom; + interrupt = false; + } else { + disposeNext(current); + } + } + + TrackEntry *entry = newTrackEntry(trackIndex, animation, loop, current); + setCurrent(trackIndex, entry, interrupt); + _queue->drain(); + + return entry; +} + +TrackEntry *AnimationState::addAnimation(size_t trackIndex, const String &animationName, bool loop, float delay) { + Animation *animation = _data->_skeletonData->findAnimation(animationName); + assert(animation != NULL); + + return addAnimation(trackIndex, animation, loop, delay); +} + +TrackEntry *AnimationState::addAnimation(size_t trackIndex, Animation *animation, bool loop, float delay) { + assert(animation != NULL); + + TrackEntry *last = expandToIndex(trackIndex); + if (last != NULL) { + while (last->_next != NULL) { + last = last->_next; + } + } + + TrackEntry *entry = newTrackEntry(trackIndex, animation, loop, last); + + if (last == NULL) { + setCurrent(trackIndex, entry, true); + _queue->drain(); + } else { + last->_next = entry; + if (delay <= 0) { + float duration = last->_animationEnd - last->_animationStart; + if (duration != 0) { + if (last->_loop) { + delay += duration * (1 + (int) (last->_trackTime / duration)); + } else { + delay += MathUtil::max(duration, last->_trackTime); + } + delay -= _data->getMix(last->_animation, animation); + } else { + delay = last->_trackTime; + } + } + } + + entry->_delay = delay; + return entry; +} + +TrackEntry *AnimationState::setEmptyAnimation(size_t trackIndex, float mixDuration) { + TrackEntry *entry = setAnimation(trackIndex, AnimationState::getEmptyAnimation(), false); + entry->_mixDuration = mixDuration; + entry->_trackEnd = mixDuration; + return entry; +} + +TrackEntry *AnimationState::addEmptyAnimation(size_t trackIndex, float mixDuration, float delay) { + if (delay <= 0) { + delay -= mixDuration; + } + + TrackEntry *entry = addAnimation(trackIndex, AnimationState::getEmptyAnimation(), false, delay); + entry->_mixDuration = mixDuration; + entry->_trackEnd = mixDuration; + return entry; +} + +void AnimationState::setEmptyAnimations(float mixDuration) { + bool oldDrainDisabled = _queue->_drainDisabled; + _queue->_drainDisabled = true; + for (size_t i = 0, n = _tracks.size(); i < n; ++i) { + TrackEntry *current = _tracks[i]; + if (current != NULL) { + setEmptyAnimation(i, mixDuration); + } + } + _queue->_drainDisabled = oldDrainDisabled; + _queue->drain(); +} + +TrackEntry *AnimationState::getCurrent(size_t trackIndex) { + return trackIndex >= _tracks.size() ? NULL : _tracks[trackIndex]; +} + +AnimationStateData *AnimationState::getData() { + return _data; +} + +Vector &AnimationState::getTracks() { + return _tracks; +} + +float AnimationState::getTimeScale() { + return _timeScale; +} + +void AnimationState::setTimeScale(float inValue) { + _timeScale = inValue; +} + +void AnimationState::setListener(AnimationStateListener inValue) { + _listener = inValue; +} + +void AnimationState::disableQueue() { + _queue->_drainDisabled = true; +} +void AnimationState::enableQueue() { + _queue->_drainDisabled = false; +} + +Animation *AnimationState::getEmptyAnimation() { + static Vector timelines; + static Animation ret(String(""), timelines, 0); + return &ret; +} + +void AnimationState::applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleton &skeleton, float time, float alpha, + MixBlend blend, Vector &timelinesRotation, size_t i, bool firstFrame) { + if (firstFrame) { + timelinesRotation[i] = 0; + } + + if (alpha == 1) { + rotateTimeline->apply(skeleton, 0, time, NULL, 1, blend, MixDirection_In); + return; + } + + Bone *bone = skeleton._bones[rotateTimeline->_boneIndex]; + Vector& frames = rotateTimeline->_frames; + float r1, r2; + if (time < frames[0]) { + switch (blend) { + case MixBlend_Setup: + bone->_rotation = bone->_data._rotation; + default: + return; + case MixBlend_First: + r1 = bone->_rotation; + r2 = bone->_data._rotation; + } + } else { + r1 = blend == MixBlend_Setup ? bone->_data._rotation : bone->_rotation; + if (time >= frames[frames.size() - RotateTimeline::ENTRIES]) { + // Time is after last frame. + r2 = bone->_data._rotation + frames[frames.size() + RotateTimeline::PREV_ROTATION]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(frames, time, RotateTimeline::ENTRIES); + float prevRotation = frames[frame + RotateTimeline::PREV_ROTATION]; + float frameTime = frames[frame]; + float percent = rotateTimeline->getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + + RotateTimeline::PREV_TIME] - + frameTime)); + + r2 = frames[frame + RotateTimeline::ROTATION] - prevRotation; + r2 -= (16384 - (int) (16384.499999999996 - r2 / 360)) * 360; + r2 = prevRotation + r2 * percent + bone->_data._rotation; + r2 -= (16384 - (int) (16384.499999999996 - r2 / 360)) * 360; + } + } + + // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. + float total, diff = r2 - r1; + diff -= (16384 - (int) (16384.499999999996 - diff / 360)) * 360; + if (diff == 0) { + total = timelinesRotation[i]; + } else { + float lastTotal, lastDiff; + if (firstFrame) { + lastTotal = 0; + lastDiff = diff; + } else { + lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops. + lastDiff = timelinesRotation[i + 1]; // Difference between bones. + } + + bool current = diff > 0, dir = lastTotal >= 0; + // Detect cross at 0 (not 180). + if (MathUtil::sign(lastDiff) != MathUtil::sign(diff) && MathUtil::abs(lastDiff) <= 90) { + // A cross after a 360 rotation is a loop. + if (MathUtil::abs(lastTotal) > 180) { + lastTotal += 360 * MathUtil::sign(lastTotal); + } + dir = current; + } + + total = diff + lastTotal - MathUtil::fmod(lastTotal, 360); // Store loops as part of lastTotal. + if (dir != current) { + total += 360 * MathUtil::sign(lastTotal); + } + timelinesRotation[i] = total; + } + timelinesRotation[i + 1] = diff; + r1 += total * alpha; + bone->_rotation = r1 - (16384 - (int) (16384.499999999996 - r1 / 360)) * 360; +} + +bool AnimationState::updateMixingFrom(TrackEntry *to, float delta) { + TrackEntry *from = to->_mixingFrom; + if (from == NULL) { + return true; + } + + bool finished = updateMixingFrom(from, delta); + + from->_animationLast = from->_nextAnimationLast; + from->_trackLast = from->_nextTrackLast; + + // Require mixTime > 0 to ensure the mixing from entry was applied at least once. + if (to->_mixTime > 0 && to->_mixTime >= to->_mixDuration) { + // Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame). + if (from->_totalAlpha == 0 || to->_mixDuration == 0) { + to->_mixingFrom = from->_mixingFrom; + if (from->_mixingFrom != NULL) from->_mixingFrom->_mixingTo = to; + to->_interruptAlpha = from->_interruptAlpha; + _queue->end(from); + } + return finished; + } + + from->_trackTime += delta * from->_timeScale; + to->_mixTime += delta; + + return false; +} + +float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBlend blend) { + TrackEntry *from = to->_mixingFrom; + if (from->_mixingFrom != NULL) { + applyMixingFrom(from, skeleton, blend); + } + + float mix; + if (to->_mixDuration == 0) { + // Single frame mix to undo mixingFrom changes. + mix = 1; + if (blend == MixBlend_First) blend = MixBlend_Setup; + } else { + mix = to->_mixTime / to->_mixDuration; + if (mix > 1) { + mix = 1; + } + if (blend != MixBlend_First) blend = from->_mixBlend; + } + + Vector *eventBuffer = mix < from->_eventThreshold ? &_events : NULL; + bool attachments = mix < from->_attachmentThreshold, drawOrder = mix < from->_drawOrderThreshold; + float animationLast = from->_animationLast, animationTime = from->getAnimationTime(); + Vector &timelines = from->_animation->_timelines; + size_t timelineCount = timelines.size(); + float alphaHold = from->_alpha * to->_interruptAlpha, alphaMix = alphaHold * (1 - mix); + + if (blend == MixBlend_Add) { + for (size_t i = 0; i < timelineCount; i++) + timelines[i]->apply(skeleton, animationLast, animationTime, eventBuffer, alphaMix, blend, MixDirection_Out); + } else { + Vector &timelineMode = from->_timelineMode; + Vector &timelineHoldMix = from->_timelineHoldMix; + + bool firstFrame = from->_timelinesRotation.size() == 0; + if (firstFrame) { + from->_timelinesRotation.setSize(timelines.size() << 1, 0); + } + + Vector &timelinesRotation = from->_timelinesRotation; + + from->_totalAlpha = 0; + for (size_t i = 0; i < timelineCount; i++) { + Timeline *timeline = timelines[i]; + MixDirection direction = MixDirection_Out; + MixBlend timelineBlend; + float alpha; + switch (timelineMode[i]) { + case Subsequent: + if (!attachments && (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))) continue; + if (!drawOrder && (timeline->getRTTI().isExactly(DrawOrderTimeline::rtti))) continue; + timelineBlend = blend; + alpha = alphaMix; + break; + case First: + timelineBlend = MixBlend_Setup; + alpha = alphaMix; + break; + case Hold: + timelineBlend = MixBlend_Setup; + alpha = alphaHold; + break; + default: + timelineBlend = MixBlend_Setup; + TrackEntry *holdMix = timelineHoldMix[i]; + alpha = alphaHold * MathUtil::max(0.0f, 1.0f - holdMix->_mixTime / holdMix->_mixDuration); + break; + } + from->_totalAlpha += alpha; + if ((timeline->getRTTI().isExactly(RotateTimeline::rtti))) { + applyRotateTimeline((RotateTimeline*)timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i << 1, + firstFrame); + } else { + if (timelineBlend == MixBlend_Setup) { + if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) { + if (attachments) direction = MixDirection_In; + } else if (timeline->getRTTI().isExactly(DrawOrderTimeline::rtti)) { + if (drawOrder) direction = MixDirection_In; + } + } + timeline->apply(skeleton, animationLast, animationTime, eventBuffer, alpha, timelineBlend, + direction); + } + } + } + + if (to->_mixDuration > 0) { + queueEvents(from, animationTime); + } + + _events.clear(); + from->_nextAnimationLast = animationTime; + from->_nextTrackLast = from->_trackTime; + + return mix; +} + +void AnimationState::queueEvents(TrackEntry *entry, float animationTime) { + float animationStart = entry->_animationStart, animationEnd = entry->_animationEnd; + float duration = animationEnd - animationStart; + float trackLastWrapped = MathUtil::fmod(entry->_trackLast, duration); + + // Queue events before complete. + size_t i = 0, n = _events.size(); + for (; i < n; ++i) { + Event *e = _events[i]; + if (e->_time < trackLastWrapped) { + break; + } + if (e->_time > animationEnd) { + // Discard events outside animation start/end. + continue; + } + _queue->event(entry, e); + } + + // Queue complete if completed a loop iteration or the animation. + bool complete = false; + if (entry->_loop) + complete = duration == 0 || (trackLastWrapped > MathUtil::fmod(entry->_trackTime, duration)); + else + complete = animationTime >= animationEnd && entry->_animationLast < animationEnd; + if (complete) _queue->complete(entry); + + // Queue events after complete. + for (; i < n; ++i) { + Event *e = _events[i]; + if (e->_time < animationStart) { + // Discard events outside animation start/end. + continue; + } + _queue->event(entry, _events[i]); + } +} + +void AnimationState::setCurrent(size_t index, TrackEntry *current, bool interrupt) { + TrackEntry *from = expandToIndex(index); + _tracks[index] = current; + + if (from != NULL) { + if (interrupt) { + _queue->interrupt(from); + } + + current->_mixingFrom = from; + from->_mixingTo = current; + current->_mixTime = 0; + + // Store interrupted mix percentage. + if (from->_mixingFrom != NULL && from->_mixDuration > 0) { + current->_interruptAlpha *= MathUtil::min(1.0f, from->_mixTime / from->_mixDuration); + } + + from->_timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in. + } + + _queue->start(current); // triggers animationsChanged +} + +TrackEntry *AnimationState::expandToIndex(size_t index) { + if (index < _tracks.size()) { + return _tracks[index]; + } + + while (index >= _tracks.size()) { + _tracks.add(NULL); + } + + return NULL; +} + +TrackEntry *AnimationState::newTrackEntry(size_t trackIndex, Animation *animation, bool loop, TrackEntry *last) { + TrackEntry *entryP = _trackEntryPool.obtain(); // Pooling + TrackEntry &entry = *entryP; + + entry._trackIndex = trackIndex; + entry._animation = animation; + entry._loop = loop; + entry._holdPrevious = 0; + + entry._eventThreshold = 0; + entry._attachmentThreshold = 0; + entry._drawOrderThreshold = 0; + + entry._animationStart = 0; + entry._animationEnd = animation->getDuration(); + entry._animationLast = -1; + entry._nextAnimationLast = -1; + + entry._delay = 0; + entry._trackTime = 0; + entry._trackLast = -1; + entry._nextTrackLast = -1; // nextTrackLast == -1 signifies a TrackEntry that wasn't applied yet. + entry._trackEnd = std::numeric_limits::max(); // loop ? float.MaxValue : animation.Duration; + entry._timeScale = 1; + + entry._alpha = 1; + entry._interruptAlpha = 1; + entry._mixTime = 0; + entry._mixDuration = (last == NULL) ? 0 : _data->getMix(last->_animation, animation); + + return entryP; +} + +void AnimationState::disposeNext(TrackEntry *entry) { + TrackEntry *next = entry->_next; + while (next != NULL) { + _queue->dispose(next); + next = next->_next; + } + entry->_next = NULL; +} + +void AnimationState::animationsChanged() { + _animationsChanged = false; + + _propertyIDs.clear(); + + for (size_t i = 0, n = _tracks.size(); i < n; ++i) { + TrackEntry *entry = _tracks[i]; + + if (!entry) continue; + + while (entry->_mixingFrom != NULL) + entry = entry->_mixingFrom; + + do { + if (entry->_mixingTo == NULL || entry->_mixBlend != MixBlend_Add) setTimelineModes(entry); + entry = entry->_mixingTo; + } while (entry != NULL); + } +} + +void AnimationState::setTimelineModes(TrackEntry *entry) { + TrackEntry* to = entry->_mixingTo; + Vector &timelines = entry->_animation->_timelines; + size_t timelinesCount = timelines.size(); + Vector &timelineMode = entry->_timelineMode; + timelineMode.setSize(timelinesCount, 0); + Vector &timelineHoldMix = entry->_timelineHoldMix; + timelineHoldMix.setSize(timelinesCount, 0); + + if (to != NULL && to->_holdPrevious) { + for (size_t i = 0; i < timelinesCount; i++) { + int id = timelines[i]->getPropertyId(); + if (!_propertyIDs.contains(id)) _propertyIDs.add(id); + timelineMode[i] = Hold; + } + return; + } + + // outer: + size_t i = 0; + continue_outer: + for (; i < timelinesCount; ++i) { + int id = timelines[i]->getPropertyId(); + if (_propertyIDs.contains(id)) { + timelineMode[i] = Subsequent; + } else { + _propertyIDs.add(id); + + if (to == NULL || !hasTimeline(to, id)) { + timelineMode[i] = First; + } else { + for (TrackEntry *next = to->_mixingTo; next != NULL; next = next->_mixingTo) { + if (hasTimeline(next, id)) continue; + if (entry->_mixDuration > 0) { + timelineMode[i] = HoldMix; + timelineHoldMix[i] = entry; + i++; + goto continue_outer; // continue outer; + } + break; + } + timelineMode[i] = Hold; + } + } + } +} + +bool AnimationState::hasTimeline(TrackEntry* entry, int inId) { + Vector &timelines = entry->_animation->_timelines; + for (size_t i = 0, n = timelines.size(); i < n; ++i) { + if (timelines[i]->getPropertyId() == inId) { + return true; + } + } + return false; +} diff --git a/cocos/editor-support/spine/AnimationState.h b/cocos/editor-support/spine/AnimationState.h index a8b54e73253f..66dae29e9c0a 100644 --- a/cocos/editor-support/spine/AnimationState.h +++ b/cocos/editor-support/spine/AnimationState.h @@ -1,185 +1,449 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_ANIMATIONSTATE_H_ -#define SPINE_ANIMATIONSTATE_H_ - -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - SP_ANIMATION_START, SP_ANIMATION_INTERRUPT, SP_ANIMATION_END, SP_ANIMATION_COMPLETE, SP_ANIMATION_DISPOSE, SP_ANIMATION_EVENT -} spEventType; - -typedef struct spAnimationState spAnimationState; -typedef struct spTrackEntry spTrackEntry; - -typedef void (*spAnimationStateListener) (spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event); - -_SP_ARRAY_DECLARE_TYPE(spTrackEntryArray, spTrackEntry*) - -struct spTrackEntry { - spAnimation* animation; - spTrackEntry* next; - spTrackEntry* mixingFrom; - spAnimationStateListener listener; - int trackIndex; - int /*boolean*/ loop; - float eventThreshold, attachmentThreshold, drawOrderThreshold; - float animationStart, animationEnd, animationLast, nextAnimationLast; - float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale; - float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha; - spIntArray* timelineData; - spTrackEntryArray* timelineDipMix; - float* timelinesRotation; - int timelinesRotationCount; - void* rendererObject; - void* userData; - -#ifdef __cplusplus - spTrackEntry() : - animation(0), - next(0), mixingFrom(0), - listener(0), - trackIndex(0), - loop(0), - eventThreshold(0), attachmentThreshold(0), drawOrderThreshold(0), - animationStart(0), animationEnd(0), animationLast(0), nextAnimationLast(0), - delay(0), trackTime(0), trackLast(0), nextTrackLast(0), trackEnd(0), timeScale(0), - alpha(0), mixTime(0), mixDuration(0), interruptAlpha(0), totalAlpha(0), - timelineData(0), - timelineDipMix(0), - timelinesRotation(0), - timelinesRotationCount(0) { - } -#endif -}; - -struct spAnimationState { - spAnimationStateData* const data; - - int tracksCount; - spTrackEntry** tracks; - - spAnimationStateListener listener; - - float timeScale; - - spTrackEntryArray* mixingTo; - - void* rendererObject; - -#ifdef __cplusplus - spAnimationState() : - data(0), - tracksCount(0), - tracks(0), - listener(0), - timeScale(0), - mixingTo(0), - rendererObject(0) { - } -#endif -}; - -/* @param data May be 0 for no mixing. */ -SP_API spAnimationState* spAnimationState_create (spAnimationStateData* data); -SP_API void spAnimationState_dispose (spAnimationState* self); - -SP_API void spAnimationState_update (spAnimationState* self, float delta); -SP_API int /**bool**/ spAnimationState_apply (spAnimationState* self, struct spSkeleton* skeleton); - -SP_API void spAnimationState_clearTracks (spAnimationState* self); -SP_API void spAnimationState_clearTrack (spAnimationState* self, int trackIndex); - -/** Set the current animation. Any queued animations are cleared. */ -SP_API spTrackEntry* spAnimationState_setAnimationByName (spAnimationState* self, int trackIndex, const char* animationName, - int/*bool*/loop); -SP_API spTrackEntry* spAnimationState_setAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop); - -/** Adds an animation to be played delay seconds after the current or last queued animation, taking into account any mix - * duration. */ -SP_API spTrackEntry* spAnimationState_addAnimationByName (spAnimationState* self, int trackIndex, const char* animationName, - int/*bool*/loop, float delay); -SP_API spTrackEntry* spAnimationState_addAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop, - float delay); -SP_API spTrackEntry* spAnimationState_setEmptyAnimation(spAnimationState* self, int trackIndex, float mixDuration); -SP_API spTrackEntry* spAnimationState_addEmptyAnimation(spAnimationState* self, int trackIndex, float mixDuration, float delay); -SP_API void spAnimationState_setEmptyAnimations(spAnimationState* self, float mixDuration); - -SP_API spTrackEntry* spAnimationState_getCurrent (spAnimationState* self, int trackIndex); - -SP_API void spAnimationState_clearListenerNotifications(spAnimationState* self); - -SP_API float spTrackEntry_getAnimationTime (spTrackEntry* entry); - -/** Use this to dispose static memory before your app exits to appease your memory leak detector*/ -SP_API void spAnimationState_disposeStatics (); - -#ifdef SPINE_SHORT_NAMES -typedef spEventType EventType; -#define ANIMATION_START SP_ANIMATION_START -#define ANIMATION_INTERRUPT SP_ANIMATION_INTERRUPT -#define ANIMATION_END SP_ANIMATION_END -#define ANIMATION_COMPLETE SP_ANIMATION_COMPLETE -#define ANIMATION_DISPOSE SP_ANIMATION_DISPOSE -#define ANIMATION_EVENT SP_ANIMATION_EVENT -typedef spAnimationStateListener AnimationStateListener; -typedef spTrackEntry TrackEntry; -typedef spAnimationState AnimationState; -#define AnimationState_create(...) spAnimationState_create(__VA_ARGS__) -#define AnimationState_dispose(...) spAnimationState_dispose(__VA_ARGS__) -#define AnimationState_update(...) spAnimationState_update(__VA_ARGS__) -#define AnimationState_apply(...) spAnimationState_apply(__VA_ARGS__) -#define AnimationState_clearTracks(...) spAnimationState_clearTracks(__VA_ARGS__) -#define AnimationState_clearTrack(...) spAnimationState_clearTrack(__VA_ARGS__) -#define AnimationState_setAnimationByName(...) spAnimationState_setAnimationByName(__VA_ARGS__) -#define AnimationState_setAnimation(...) spAnimationState_setAnimation(__VA_ARGS__) -#define AnimationState_addAnimationByName(...) spAnimationState_addAnimationByName(__VA_ARGS__) -#define AnimationState_addAnimation(...) spAnimationState_addAnimation(__VA_ARGS__) -#define AnimationState_setEmptyAnimation(...) spAnimatinState_setEmptyAnimation(__VA_ARGS__) -#define AnimationState_addEmptyAnimation(...) spAnimatinState_addEmptyAnimation(__VA_ARGS__) -#define AnimationState_setEmptyAnimations(...) spAnimatinState_setEmptyAnimations(__VA_ARGS__) -#define AnimationState_getCurrent(...) spAnimationState_getCurrent(__VA_ARGS__) -#define AnimationState_clearListenerNotifications(...) spAnimatinState_clearListenerNotifications(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#ifndef Spine_AnimationState_h +#define Spine_AnimationState_h + +#include +#include +#include +#include +#include +#include + +namespace spine { + enum EventType { + EventType_Start, + EventType_Interrupt, + EventType_End, + EventType_Complete, + EventType_Dispose, + EventType_Event + }; + + class AnimationState; + class TrackEntry; + + class Animation; + class Event; + class AnimationStateData; + class Skeleton; + class RotateTimeline; + + typedef void (*AnimationStateListener) (AnimationState* state, EventType type, TrackEntry* entry, Event* event); + + /// State for the playback of an animation + class SP_API TrackEntry : public SpineObject, public HasRendererObject { + friend class EventQueue; + friend class AnimationState; + + public: + TrackEntry(); + + virtual ~TrackEntry(); + + /// The index of the track where this entry is either current or queued. + int getTrackIndex(); + + /// The animation to apply for this track entry. + Animation* getAnimation(); + + /// + /// If true, the animation will repeat. If false, it will not, instead its last frame is applied if played beyond its duration. + bool getLoop(); + void setLoop(bool inValue); + + /// + /// If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead + /// of being mixed out. + /// + /// When mixing between animations that key the same property, if a lower track also keys that property then the value will + /// briefly dip toward the lower track value during the mix. This happens because the first animation mixes from 100% to 0% + /// while the second animation mixes from 0% to 100%. Setting holdPrevious to true applies the first animation + /// at 100% during the mix so the lower track value is overwritten. Such dipping does not occur on the lowest track which + /// keys the property, only when a higher track also keys the property. + /// + /// Snapping will occur if holdPrevious is true and this animation does not key all the same properties as the + /// previous animation. + bool getHoldPrevious(); + void setHoldPrevious(bool inValue); + + /// + /// Seconds to postpone playing the animation. When a track entry is the current track entry, delay postpones incrementing + /// the track time. 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. + float getDelay(); + void setDelay(float inValue); + + /// + /// Current time in seconds this track entry has been the current track entry. The track time determines + /// TrackEntry.AnimationTime. The track time can be set to start the animation at a time other than 0, without affecting looping. + float getTrackTime(); + void setTrackTime(float inValue); + + /// + /// The track time in seconds when this animation will be removed from the track. Defaults to the animation duration for + /// non-looping animations and to int.MaxValue for looping animations. If the track end time is reached and no + /// other animations are queued for playback, and mixing from any previous animations is complete, properties keyed by the animation, + /// are set to the setup pose and the track is cleared. + /// + /// It may be desired to use AnimationState.addEmptyAnimation(int, float, float) to mix the properties back to the + /// setup pose over time, rather than have it happen instantly. + /// + float getTrackEnd(); + void setTrackEnd(float inValue); + + /// + /// Seconds when this animation starts, both initially and after looping. Defaults to 0. + /// + /// When changing the animation start time, it often makes sense to set TrackEntry.AnimationLast to the same value to + /// prevent timeline keys before the start time from triggering. + /// + float getAnimationStart(); + void setAnimationStart(float inValue); + + /// + /// Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animations will + /// loop back to TrackEntry.AnimationStart at this time. Defaults to the animation duration. + float getAnimationEnd(); + void setAnimationEnd(float inValue); + + /// + /// The time in seconds this animation was last applied. Some timelines use this for one-time triggers. Eg, when this + /// animation is applied, event timelines will fire all events between the animation last time (exclusive) and animation time + /// (inclusive). Defaults to -1 to ensure triggers on frame 0 happen the first time this animation is applied. + float getAnimationLast(); + void setAnimationLast(float inValue); + + /// + /// Uses TrackEntry.TrackTime to compute the animation time between TrackEntry.AnimationStart. and + /// TrackEntry.AnimationEnd. When the track time is 0, the animation time is equal to the animation start time. + /// + float getAnimationTime(); + + /// + /// Multiplier for the delta time when the animation state is updated, causing time for this animation to play slower or + /// faster. Defaults to 1. + /// + float getTimeScale(); + void setTimeScale(float inValue); + + /// + /// Values less than 1 mix this animation with the last skeleton pose. Defaults to 1, which overwrites the last skeleton 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. + /// + float getAlpha(); + void setAlpha(float inValue); + + /// + /// When the mix percentage (mix time / mix duration) is less than the event threshold, event timelines for the animation + /// being mixed out will be applied. Defaults to 0, so event timelines are not applied for an animation being mixed out. + float getEventThreshold(); + void setEventThreshold(float inValue); + + /// + /// When the mix percentage (mix time / mix duration) is less than the attachment threshold, 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. + float getAttachmentThreshold(); + void setAttachmentThreshold(float inValue); + + /// + /// When the mix percentage (mix time / mix duration) is less than the draw order threshold, draw order timelines for the + /// animation being mixed out will be applied. Defaults to 0, so draw order timelines are not applied for an animation being + /// mixed out. + /// + float getDrawOrderThreshold(); + void setDrawOrderThreshold(float inValue); + + /// + /// The animation queued to start after this animation, or NULL. + TrackEntry* getNext(); + + /// + /// Returns true if at least one loop has been completed. + bool isComplete(); + + /// + /// Seconds from 0 to the mix duration when mixing from the previous animation to this animation. May be slightly more than + /// TrackEntry.MixDuration when the mix is complete. + float getMixTime(); + void setMixTime(float inValue); + + /// + /// Seconds for mixing from the previous animation to this animation. Defaults to the value provided by + /// AnimationStateData based on the animation before this animation (if any). + /// + /// The mix duration can be set manually rather than use the value from AnimationStateData.GetMix. + /// In that case, the mixDuration must be set before AnimationState.update(float) is next called. + /// + /// When using AnimationState::addAnimation(int, Animation, bool, float) with a delay + /// less than or equal to 0, note the Delay is set using the mix duration from the AnimationStateData + /// + /// + /// + float getMixDuration(); + void setMixDuration(float inValue); + + + MixBlend getMixBlend(); + void setMixBlend(MixBlend blend); + + /// + /// The track entry for the previous animation when mixing from the previous animation to this animation, or NULL if no + /// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a double linked list with MixingTo. + TrackEntry* getMixingFrom(); + + /// + /// The track entry for the next animation when mixing from this animation, or NULL if no mixing is currently occuring. + /// When mixing from multiple animations, MixingTo makes up a double linked list with MixingFrom. + TrackEntry* getMixingTo(); + + /// + /// Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the + /// long way around when using alpha and starting animations on other tracks. + /// + /// Mixing involves finding a rotation between two others, which has two possible solutions: the short way or the long way around. + /// The two rotations likely change over time, so which direction is the short or long way also changes. + /// If the short way was always chosen, bones would flip to the other side when that direction became the long way. + /// TrackEntry chooses the short way the first time it is applied and remembers that direction. + void resetRotationDirections(); + + void setListener(AnimationStateListener listener); + + private: + Animation* _animation; + + TrackEntry* _next; + TrackEntry* _mixingFrom; + TrackEntry* _mixingTo; + int _trackIndex; + + bool _loop, _holdPrevious; + float _eventThreshold, _attachmentThreshold, _drawOrderThreshold; + float _animationStart, _animationEnd, _animationLast, _nextAnimationLast; + float _delay, _trackTime, _trackLast, _nextTrackLast, _trackEnd, _timeScale; + float _alpha, _mixTime, _mixDuration, _interruptAlpha, _totalAlpha; + MixBlend _mixBlend; + Vector _timelineMode; + Vector _timelineHoldMix; + Vector _timelinesRotation; + AnimationStateListener _listener; + + void reset(); + }; + + class SP_API EventQueueEntry : public SpineObject { + friend class EventQueue; + + public: + EventType _type; + TrackEntry* _entry; + Event* _event; + + EventQueueEntry(EventType eventType, TrackEntry* trackEntry, Event* event = NULL); + }; + + class SP_API EventQueue : public SpineObject { + friend class AnimationState; + + private: + Vector _eventQueueEntries; + AnimationState& _state; + Pool& _trackEntryPool; + bool _drainDisabled; + + static EventQueue* newEventQueue(AnimationState& state, Pool& trackEntryPool); + + static EventQueueEntry newEventQueueEntry(EventType eventType, TrackEntry* entry, Event* event = NULL); + + EventQueue(AnimationState& state, Pool& trackEntryPool); + + ~EventQueue(); + + void start(TrackEntry* entry); + + void interrupt(TrackEntry* entry); + + void end(TrackEntry* entry); + + void dispose(TrackEntry* entry); + + void complete(TrackEntry* entry); + + void event(TrackEntry* entry, Event* event); + + /// Raises all events in the queue and drains the queue. + void drain(); + }; + + class SP_API AnimationState : public SpineObject, public HasRendererObject { + friend class TrackEntry; + friend class EventQueue; + + public: + explicit AnimationState(AnimationStateData* data); + + ~AnimationState(); + + /// + /// Increments the track entry times, setting queued animations as current if needed + /// @param delta delta time + void update(float delta); + + /// + /// Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the + /// animation state can be applied to multiple skeletons to pose them identically. + bool apply(Skeleton& skeleton); + + /// + /// Removes all animations from all tracks, leaving skeletons in their previous pose. + /// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose, + /// rather than leaving them in their previous pose. + void clearTracks(); + + /// + /// Removes all animations from the tracks, leaving skeletons in their previous pose. + /// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose, + /// rather than leaving them in their previous pose. + void clearTrack(size_t trackIndex); + + /// Sets an animation by name. setAnimation(int, Animation, bool) + TrackEntry* setAnimation(size_t trackIndex, const String& animationName, bool loop); + + /// Sets the current animation for a track, discarding any queued animations. + /// @param loop If true, the animation will repeat. + /// If false, it will not, instead its last frame is applied if played beyond its duration. + /// In either case TrackEntry.TrackEnd determines when the track is cleared. + /// @return + /// A track entry to allow further customization of animation playback. References to the track entry must not be kept + /// after AnimationState.Dispose. + TrackEntry* setAnimation(size_t trackIndex, Animation* animation, bool loop); + + /// Queues an animation by name. + /// addAnimation(int, Animation, bool, float) + TrackEntry* addAnimation(size_t trackIndex, const String& animationName, bool loop, float delay); + + /// Adds an animation to be played delay seconds after the current or last queued animation + /// for a track. If the track is empty, it is equivalent to calling setAnimation. + /// @param delay + /// Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation + /// duration of the previous track minus any mix duration plus the negative delay. + /// + /// @return A track entry to allow further customization of animation playback. References to the track entry must not be kept + /// after AnimationState.Dispose + TrackEntry* addAnimation(size_t trackIndex, Animation* animation, bool loop, float delay); + + /// + /// Sets an empty animation for a track, discarding any queued animations, and mixes to it over the specified mix duration. + TrackEntry* setEmptyAnimation(size_t trackIndex, float mixDuration); + + /// + /// Adds an empty animation to be played after the current or last queued animation for a track, and mixes to it over the + /// specified mix duration. + /// @return + /// A track entry to allow further customization of animation playback. References to the track entry must not be kept after AnimationState.Dispose. + /// + /// @param trackIndex Track number. + /// @param mixDuration Mix duration. + /// @param delay Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation + /// duration of the previous track minus any mix duration plus the negative delay. + TrackEntry* addEmptyAnimation(size_t trackIndex, float mixDuration, float delay); + + /// + /// Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration. + void setEmptyAnimations(float mixDuration); + + /// @return The track entry for the animation currently playing on the track, or NULL if no animation is currently playing. + TrackEntry* getCurrent(size_t trackIndex); + + AnimationStateData* getData(); + + /// A list of tracks that have animations, which may contain NULLs. + Vector &getTracks(); + + float getTimeScale(); + void setTimeScale(float inValue); + + void setListener(AnimationStateListener listener); + + void disableQueue(); + void enableQueue(); + + private: + + AnimationStateData* _data; + + Pool _trackEntryPool; + Vector _tracks; + Vector _events; + EventQueue* _queue; + + Vector _propertyIDs; + bool _animationsChanged; + + AnimationStateListener _listener; + + float _timeScale; + + static Animation* getEmptyAnimation(); + + static void applyRotateTimeline(RotateTimeline* rotateTimeline, Skeleton& skeleton, float time, float alpha, MixBlend pose, Vector& timelinesRotation, size_t i, bool firstFrame); + + /// Returns true when all mixing from entries are complete. + bool updateMixingFrom(TrackEntry* to, float delta); + + float applyMixingFrom(TrackEntry* to, Skeleton& skeleton, MixBlend currentPose); + + void queueEvents(TrackEntry* entry, float animationTime); + + /// Sets the active TrackEntry for a given track number. + void setCurrent(size_t index, TrackEntry* current, bool interrupt); + + TrackEntry* expandToIndex(size_t index); + + /// Object-pooling version of new TrackEntry. Obtain an unused TrackEntry from the pool and clear/initialize its values. + /// @param last May be NULL. + TrackEntry* newTrackEntry(size_t trackIndex, Animation* animation, bool loop, TrackEntry* last); + + /// Dispose all track entries queued after the given TrackEntry. + void disposeNext(TrackEntry* entry); + + void animationsChanged(); + + void setTimelineModes(TrackEntry* entry); + + bool hasTimeline(TrackEntry* entry, int inId); + }; } -#endif -#endif /* SPINE_ANIMATIONSTATE_H_ */ +#endif /* Spine_AnimationState_h */ diff --git a/cocos/editor-support/spine/AnimationStateData.cpp b/cocos/editor-support/spine/AnimationStateData.cpp new file mode 100644 index 000000000000..bb1d29e2274e --- /dev/null +++ b/cocos/editor-support/spine/AnimationStateData.cpp @@ -0,0 +1,85 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include +#include + +using namespace spine; + +AnimationStateData::AnimationStateData(SkeletonData *skeletonData) : _skeletonData(skeletonData), _defaultMix(0) { +} + +void AnimationStateData::setMix(const String &fromName, const String &toName, float duration) { + Animation *from = _skeletonData->findAnimation(fromName); + Animation *to = _skeletonData->findAnimation(toName); + + setMix(from, to, duration); +} + +void AnimationStateData::setMix(Animation *from, Animation *to, float duration) { + assert(from != NULL); + assert(to != NULL); + + AnimationPair key(from, to); + _animationToMixTime.put(key, duration); +} + +float AnimationStateData::getMix(Animation *from, Animation *to) { + assert(from != NULL); + assert(to != NULL); + + AnimationPair key(from, to); + + if (_animationToMixTime.containsKey(key)) return _animationToMixTime[key]; + return _defaultMix; +} + +SkeletonData *AnimationStateData::getSkeletonData() { + return _skeletonData; +} + +float AnimationStateData::getDefaultMix() { + return _defaultMix; +} + +void AnimationStateData::setDefaultMix(float inValue) { + _defaultMix = inValue; +} + +AnimationStateData::AnimationPair::AnimationPair(Animation *a1, Animation *a2) : _a1(a1), _a2(a2) { +} + +bool AnimationStateData::AnimationPair::operator==(const AnimationPair &other) const { + return _a1->_name == other._a1->_name && _a2->_name == other._a2->_name; +} diff --git a/cocos/editor-support/spine/AnimationStateData.h b/cocos/editor-support/spine/AnimationStateData.h index 87bdba5c1892..d80a23ea8d22 100644 --- a/cocos/editor-support/spine/AnimationStateData.h +++ b/cocos/editor-support/spine/AnimationStateData.h @@ -1,77 +1,87 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_ANIMATIONSTATEDATA_H_ -#define SPINE_ANIMATIONSTATEDATA_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spAnimationStateData { - spSkeletonData* const skeletonData; - float defaultMix; - const void* const entries; +#ifndef Spine_AnimationStateData_h +#define Spine_AnimationStateData_h -#ifdef __cplusplus - spAnimationStateData() : - skeletonData(0), - defaultMix(0), - entries(0) { - } -#endif -} spAnimationStateData; +#include +#include +#include -SP_API spAnimationStateData* spAnimationStateData_create (spSkeletonData* skeletonData); -SP_API void spAnimationStateData_dispose (spAnimationStateData* self); +#include -SP_API void spAnimationStateData_setMixByName (spAnimationStateData* self, const char* fromName, const char* toName, float duration); -SP_API void spAnimationStateData_setMix (spAnimationStateData* self, spAnimation* from, spAnimation* to, float duration); -/* Returns 0 if there is no mixing between the animations. */ -SP_API float spAnimationStateData_getMix (spAnimationStateData* self, spAnimation* from, spAnimation* to); +namespace spine { + class SkeletonData; + class Animation; + + /// Stores mix (crossfade) durations to be applied when AnimationState animations are changed. + class SP_API AnimationStateData : public SpineObject { + friend class AnimationState; + + public: + explicit AnimationStateData(SkeletonData* skeletonData); + + /// The SkeletonData to look up animations when they are specified by name. + SkeletonData* getSkeletonData(); + + /// The mix duration to use when no mix duration has been specifically defined between two animations. + float getDefaultMix(); + void setDefaultMix(float inValue); + + /// Sets a mix duration by animation names. + void setMix(const String& fromName, const String& toName, float duration); + + /// Sets a mix duration when changing from the specified animation to the other. + /// See TrackEntry.MixDuration. + void setMix(Animation* from, Animation* to, float duration); + + /// + /// The mix duration to use when changing from the specified animation to the other, + /// or the DefaultMix if no mix duration has been set. + /// + float getMix(Animation* from, Animation* to); -#ifdef SPINE_SHORT_NAMES -typedef spAnimationStateData AnimationStateData; -#define AnimationStateData_create(...) spAnimationStateData_create(__VA_ARGS__) -#define AnimationStateData_dispose(...) spAnimationStateData_dispose(__VA_ARGS__) -#define AnimationStateData_setMixByName(...) spAnimationStateData_setMixByName(__VA_ARGS__) -#define AnimationStateData_setMix(...) spAnimationStateData_setMix(__VA_ARGS__) -#define AnimationStateData_getMix(...) spAnimationStateData_getMix(__VA_ARGS__) -#endif + private: + class AnimationPair : public SpineObject { + public: + Animation* _a1; + Animation* _a2; -#ifdef __cplusplus + explicit AnimationPair(Animation* a1 = NULL, Animation* a2 = NULL); + + bool operator==(const AnimationPair &other) const; + }; + + SkeletonData* _skeletonData; + float _defaultMix; + HashMap _animationToMixTime; + }; } -#endif -#endif /* SPINE_ANIMATIONSTATEDATA_H_ */ +#endif /* Spine_AnimationStateData_h */ diff --git a/cocos/editor-support/spine/Atlas.cpp b/cocos/editor-support/spine/Atlas.cpp new file mode 100644 index 000000000000..99de2dc8f313 --- /dev/null +++ b/cocos/editor-support/spine/Atlas.cpp @@ -0,0 +1,356 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include +#include + +#include + +using namespace spine; + +Atlas::Atlas(const String &path, TextureLoader *textureLoader) : _textureLoader(textureLoader) { + int dirLength; + char *dir; + int length; + const char *data; + + /* Get directory from atlas path. */ + const char *lastForwardSlash = strrchr(path.buffer(), '/'); + const char *lastBackwardSlash = strrchr(path.buffer(), '\\'); + const char *lastSlash = lastForwardSlash > lastBackwardSlash ? lastForwardSlash : lastBackwardSlash; + if (lastSlash == path) lastSlash++; /* Never drop starting slash. */ + dirLength = (int) (lastSlash ? lastSlash - path.buffer() : 0); + dir = SpineExtension::calloc(dirLength + 1, __FILE__, __LINE__); + memcpy(dir, path.buffer(), dirLength); + dir[dirLength] = '\0'; + + data = SpineExtension::readFile(path, &length); + if (data) { + load(data, length, dir); + } + + SpineExtension::free(data, __FILE__, __LINE__); + SpineExtension::free(dir, __FILE__, __LINE__); +} + +Atlas::Atlas(const char *data, int length, const char *dir, TextureLoader *textureLoader) : _textureLoader( + textureLoader) { + load(data, length, dir); +} + +Atlas::~Atlas() { + if (_textureLoader) { + for (size_t i = 0, n = _pages.size(); i < n; ++i) { + _textureLoader->unload(_pages[i]->getRendererObject()); + } + } + ContainerUtil::cleanUpVectorOfPointers(_pages); + ContainerUtil::cleanUpVectorOfPointers(_regions); +} + +void Atlas::flipV() { + for (size_t i = 0, n = _regions.size(); i < n; ++i) { + AtlasRegion *regionP = _regions[i]; + AtlasRegion ®ion = *regionP; + region.v = 1 - region.v; + region.v2 = 1 - region.v2; + } +} + +AtlasRegion *Atlas::findRegion(const String &name) { + for (size_t i = 0, n = _regions.size(); i < n; ++i) { + if (_regions[i]->name == name) { + return _regions[i]; + } + } + + return NULL; +} + +Vector &Atlas::getPages() { + return _pages; +} + +void Atlas::load(const char *begin, int length, const char *dir) { + static const char *formatNames[] = {"", "Alpha", "Intensity", "LuminanceAlpha", "RGB565", "RGBA4444", "RGB888", + "RGBA8888"}; + static const char *textureFilterNames[] = {"", "Nearest", "Linear", "MipMap", "MipMapNearestNearest", + "MipMapLinearNearest", + "MipMapNearestLinear", "MipMapLinearLinear"}; + + int count; + const char *end = begin + length; + int dirLength = (int) strlen(dir); + int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\'; + + AtlasPage *page = NULL; + Str str; + Str tuple[4]; + + while (readLine(&begin, end, &str)) { + if (str.end - str.begin == 0) { + page = 0; + } else if (!page) { + char *name = mallocString(&str); + char *path = SpineExtension::calloc(dirLength + needsSlash + strlen(name) + 1, __FILE__, __LINE__); + memcpy(path, dir, dirLength); + if (needsSlash) { + path[dirLength] = '/'; + } + strcpy(path + dirLength + needsSlash, name); + + page = new(__FILE__, __LINE__) AtlasPage(String(name, true)); + + int tupleVal = readTuple(&begin, end, tuple); + assert(tupleVal == 2); + + /* size is only optional for an atlas packed with an old TexturePacker. */ + page->width = toInt(tuple); + page->height = toInt(tuple + 1); + readTuple(&begin, end, tuple); + + page->format = (Format) indexOf(formatNames, 8, tuple); + + readTuple(&begin, end, tuple); + page->minFilter = (TextureFilter) indexOf(textureFilterNames, 8, tuple); + page->magFilter = (TextureFilter) indexOf(textureFilterNames, 8, tuple + 1); + + readValue(&begin, end, &str); + + page->uWrap = TextureWrap_ClampToEdge; + page->vWrap = TextureWrap_ClampToEdge; + if (!equals(&str, "none")) { + if (str.end - str.begin == 1) { + if (*str.begin == 'x') { + page->uWrap = TextureWrap_Repeat; + } else if (*str.begin == 'y') { + page->vWrap = TextureWrap_Repeat; + } + } else if (equals(&str, "xy")) { + page->uWrap = TextureWrap_Repeat; + page->vWrap = TextureWrap_Repeat; + } + } + + if (_textureLoader) _textureLoader->load(*page, String(path)); + + SpineExtension::free(path, __FILE__, __LINE__); + + _pages.add(page); + } else { + AtlasRegion *region = new(__FILE__, __LINE__) AtlasRegion(); + + region->page = page; + region->name = String(mallocString(&str), true); + + readValue(&begin, end, &str); + region->rotate = equals(&str, "true") ? true : false; + + readTuple(&begin, end, tuple); + region->x = toInt(tuple); + region->y = toInt(tuple + 1); + + readTuple(&begin, end, tuple); + region->width = toInt(tuple); + region->height = toInt(tuple + 1); + + region->u = region->x / (float) page->width; + region->v = region->y / (float) page->height; + if (region->rotate) { + region->u2 = (region->x + region->height) / (float) page->width; + region->v2 = (region->y + region->width) / (float) page->height; + } else { + region->u2 = (region->x + region->width) / (float) page->width; + region->v2 = (region->y + region->height) / (float) page->height; + } + + count = readTuple(&begin, end, tuple); + assert(count); + + if (count == 4) { + /* split is optional */ + region->splits.setSize(4, 0); + region->splits[0] = toInt(tuple); + region->splits[1] = toInt(tuple + 1); + region->splits[2] = toInt(tuple + 2); + region->splits[3] = toInt(tuple + 3); + + count = readTuple(&begin, end, tuple); + assert(count); + + if (count == 4) { + /* pad is optional, but only present with splits */ + region->pads.setSize(4, 0); + region->pads[0] = toInt(tuple); + region->pads[1] = toInt(tuple + 1); + region->pads[2] = toInt(tuple + 2); + region->pads[3] = toInt(tuple + 3); + + readTuple(&begin, end, tuple); + } + } + + region->originalWidth = toInt(tuple); + region->originalHeight = toInt(tuple + 1); + + readTuple(&begin, end, tuple); + region->offsetX = (float)toInt(tuple); + region->offsetY = (float)toInt(tuple + 1); + + readValue(&begin, end, &str); + + region->index = toInt(&str); + + _regions.add(region); + } + } +} + +void Atlas::trim(Str *str) { + while (isspace((unsigned char) *str->begin) && str->begin < str->end) { + (str->begin)++; + } + + if (str->begin == str->end) { + return; + } + + str->end--; + + while (((unsigned char)*str->end == '\r') && str->end >= str->begin) { + str->end--; + } + + str->end++; +} + +int Atlas::readLine(const char **begin, const char *end, Str *str) { + if (*begin == end) { + return 0; + } + + str->begin = *begin; + + /* Find next delimiter. */ + while (*begin != end && **begin != '\n') { + (*begin)++; + } + + str->end = *begin; + trim(str); + + if (*begin != end) { + (*begin)++; + } + + return 1; +} + +int Atlas::beginPast(Str *str, char c) { + const char *begin = str->begin; + while (true) { + char lastSkippedChar = *begin; + if (begin == str->end) { + return 0; + } + begin++; + if (lastSkippedChar == c) { + break; + } + } + str->begin = begin; + return 1; +} + +int Atlas::readValue(const char **begin, const char *end, Str *str) { + readLine(begin, end, str); + if (!beginPast(str, ':')) { + return 0; + } + + trim(str); + return 1; +} + +int Atlas::readTuple(const char **begin, const char *end, Str tuple[]) { + int i; + Str str = {NULL, NULL}; + readLine(begin, end, &str); + if (!beginPast(&str, ':')) { + return 0; + } + + for (i = 0; i < 3; ++i) { + tuple[i].begin = str.begin; + if (!beginPast(&str, ',')) { + break; + } + + tuple[i].end = str.begin - 2; + trim(&tuple[i]); + } + + tuple[i].begin = str.begin; + tuple[i].end = str.end; + trim(&tuple[i]); + + return i + 1; +} + +char *Atlas::mallocString(Str *str) { + int length = (int) (str->end - str->begin); + char *string = SpineExtension::calloc(length + 1, __FILE__, __LINE__); + memcpy(string, str->begin, length); + string[length] = '\0'; + return string; +} + +int Atlas::indexOf(const char **array, int count, Str *str) { + int length = (int) (str->end - str->begin); + int i; + for (i = count - 1; i >= 0; i--) { + if (strncmp(array[i], str->begin, length) == 0) { + return i; + } + } + return 0; +} + +int Atlas::equals(Str *str, const char *other) { + return strncmp(other, str->begin, str->end - str->begin) == 0; +} + +int Atlas::toInt(Str *str) { + return (int) strtol(str->begin, (char **) &str->end, 10); +} diff --git a/cocos/editor-support/spine/Atlas.h b/cocos/editor-support/spine/Atlas.h index 61637f17909f..d73bcdded1ef 100644 --- a/cocos/editor-support/spine/Atlas.h +++ b/cocos/editor-support/spine/Atlas.h @@ -1,174 +1,157 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_ATLAS_H_ -#define SPINE_ATLAS_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spAtlas spAtlas; - -typedef enum { - SP_ATLAS_UNKNOWN_FORMAT, - SP_ATLAS_ALPHA, - SP_ATLAS_INTENSITY, - SP_ATLAS_LUMINANCE_ALPHA, - SP_ATLAS_RGB565, - SP_ATLAS_RGBA4444, - SP_ATLAS_RGB888, - SP_ATLAS_RGBA8888 -} spAtlasFormat; - -typedef enum { - SP_ATLAS_UNKNOWN_FILTER, - SP_ATLAS_NEAREST, - SP_ATLAS_LINEAR, - SP_ATLAS_MIPMAP, - SP_ATLAS_MIPMAP_NEAREST_NEAREST, - SP_ATLAS_MIPMAP_LINEAR_NEAREST, - SP_ATLAS_MIPMAP_NEAREST_LINEAR, - SP_ATLAS_MIPMAP_LINEAR_LINEAR -} spAtlasFilter; - -typedef enum { - SP_ATLAS_MIRROREDREPEAT, - SP_ATLAS_CLAMPTOEDGE, - SP_ATLAS_REPEAT -} spAtlasWrap; - -typedef struct spAtlasPage spAtlasPage; -struct spAtlasPage { - const spAtlas* atlas; - const char* name; - spAtlasFormat format; - spAtlasFilter minFilter, magFilter; - spAtlasWrap uWrap, vWrap; - - void* rendererObject; +#ifndef Spine_Atlas_h +#define Spine_Atlas_h + +#include +#include +#include +#include +#include + +namespace spine { +enum Format { + Format_Alpha, + Format_Intensity, + Format_LuminanceAlpha, + Format_RGB565, + Format_RGBA4444, + Format_RGB888, + Format_RGBA8888 +}; + +enum TextureFilter { + TextureFilter_Unknown, + TextureFilter_Nearest, + TextureFilter_Linear, + TextureFilter_MipMap, + TextureFilter_MipMapNearestNearest, + TextureFilter_MipMapLinearNearest, + TextureFilter_MipMapNearestLinear, + TextureFilter_MipMapLinearLinear +}; + +enum TextureWrap { + TextureWrap_MirroredRepeat, + TextureWrap_ClampToEdge, + TextureWrap_Repeat +}; + +class SP_API AtlasPage : public SpineObject, public HasRendererObject { +public: + String name; + Format format; + TextureFilter minFilter; + TextureFilter magFilter; + TextureWrap uWrap; + TextureWrap vWrap; int width, height; - spAtlasPage* next; + explicit AtlasPage(const String &inName) : name(inName), format(Format_RGBA8888), minFilter(TextureFilter_Nearest), + magFilter(TextureFilter_Nearest), uWrap(TextureWrap_ClampToEdge), + vWrap(TextureWrap_ClampToEdge) { + } + + virtual ~AtlasPage() { } }; -SP_API spAtlasPage* spAtlasPage_create (spAtlas* atlas, const char* name); -SP_API void spAtlasPage_dispose (spAtlasPage* self); - -#ifdef SPINE_SHORT_NAMES -typedef spAtlasFormat AtlasFormat; -#define ATLAS_UNKNOWN_FORMAT SP_ATLAS_UNKNOWN_FORMAT -#define ATLAS_ALPHA SP_ATLAS_ALPHA -#define ATLAS_INTENSITY SP_ATLAS_INTENSITY -#define ATLAS_LUMINANCE_ALPHA SP_ATLAS_LUMINANCE_ALPHA -#define ATLAS_RGB565 SP_ATLAS_RGB565 -#define ATLAS_RGBA4444 SP_ATLAS_RGBA4444 -#define ATLAS_RGB888 SP_ATLAS_RGB888 -#define ATLAS_RGBA8888 SP_ATLAS_RGBA8888 -typedef spAtlasFilter AtlasFilter; -#define ATLAS_UNKNOWN_FILTER SP_ATLAS_UNKNOWN_FILTER -#define ATLAS_NEAREST SP_ATLAS_NEAREST -#define ATLAS_LINEAR SP_ATLAS_LINEAR -#define ATLAS_MIPMAP SP_ATLAS_MIPMAP -#define ATLAS_MIPMAP_NEAREST_NEAREST SP_ATLAS_MIPMAP_NEAREST_NEAREST -#define ATLAS_MIPMAP_LINEAR_NEAREST SP_ATLAS_MIPMAP_LINEAR_NEAREST -#define ATLAS_MIPMAP_NEAREST_LINEAR SP_ATLAS_MIPMAP_NEAREST_LINEAR -#define ATLAS_MIPMAP_LINEAR_LINEAR SP_ATLAS_MIPMAP_LINEAR_LINEAR -typedef spAtlasWrap AtlasWrap; -#define ATLAS_MIRROREDREPEAT SP_ATLAS_MIRROREDREPEAT -#define ATLAS_CLAMPTOEDGE SP_ATLAS_CLAMPTOEDGE -#define ATLAS_REPEAT SP_ATLAS_REPEAT -typedef spAtlasPage AtlasPage; -#define AtlasPage_create(...) spAtlasPage_create(__VA_ARGS__) -#define AtlasPage_dispose(...) spAtlasPage_dispose(__VA_ARGS__) -#endif - -/**/ - -typedef struct spAtlasRegion spAtlasRegion; -struct spAtlasRegion { - const char* name; +class SP_API AtlasRegion : public SpineObject { +public: + AtlasPage *page; + String name; int x, y, width, height; float u, v, u2, v2; - int offsetX, offsetY; + float offsetX, offsetY; int originalWidth, originalHeight; int index; - int/*bool*/rotate; - int/*bool*/flip; - int* splits; - int* pads; + bool rotate; + Vector splits; + Vector pads; +}; - spAtlasPage* page; +class TextureLoader; - spAtlasRegion* next; -}; +class SP_API Atlas : public SpineObject { +public: + Atlas(const String &path, TextureLoader *textureLoader); -SP_API spAtlasRegion* spAtlasRegion_create (); -SP_API void spAtlasRegion_dispose (spAtlasRegion* self); + Atlas(const char *data, int length, const char *dir, TextureLoader *textureLoader); -#ifdef SPINE_SHORT_NAMES -typedef spAtlasRegion AtlasRegion; -#define AtlasRegion_create(...) spAtlasRegion_create(__VA_ARGS__) -#define AtlasRegion_dispose(...) spAtlasRegion_dispose(__VA_ARGS__) -#endif + ~Atlas(); -/**/ + void flipV(); -struct spAtlas { - spAtlasPage* pages; - spAtlasRegion* regions; + /// Returns the first region found with the specified name. This method uses String comparison to find the region, so the result + /// should be cached rather than calling this method multiple times. + /// @return The region, or NULL. + AtlasRegion *findRegion(const String &name); - void* rendererObject; -}; + Vector &getPages(); -/* Image files referenced in the atlas file will be prefixed with dir. */ -SP_API spAtlas* spAtlas_create (const char* data, int length, const char* dir, void* rendererObject); -/* Image files referenced in the atlas file will be prefixed with the directory containing the atlas file. */ -SP_API spAtlas* spAtlas_createFromFile (const char* path, void* rendererObject); -SP_API void spAtlas_dispose (spAtlas* atlas); +private: + Vector _pages; + Vector _regions; + TextureLoader *_textureLoader; -/* Returns 0 if the region was not found. */ -SP_API spAtlasRegion* spAtlas_findRegion (const spAtlas* self, const char* name); + void load(const char *begin, int length, const char *dir); -#ifdef SPINE_SHORT_NAMES -typedef spAtlas Atlas; -#define Atlas_create(...) spAtlas_create(__VA_ARGS__) -#define Atlas_createFromFile(...) spAtlas_createFromFile(__VA_ARGS__) -#define Atlas_dispose(...) spAtlas_dispose(__VA_ARGS__) -#define Atlas_findRegion(...) spAtlas_findRegion(__VA_ARGS__) -#endif + class Str { + public: + const char *begin; + const char *end; + }; -#ifdef __cplusplus + static void trim(Str *str); + + /// Tokenize string without modification. Returns 0 on failure + static int readLine(const char **begin, const char *end, Str *str); + + /// Moves str->begin past the first occurence of c. Returns 0 on failure + static int beginPast(Str *str, char c); + + /// Returns 0 on failure + static int readValue(const char **begin, const char *end, Str *str); + + /// Returns the number of tuple values read (1, 2, 4, or 0 for failure) + static int readTuple(const char **begin, const char *end, Str tuple[]); + + static char *mallocString(Str *str); + + static int indexOf(const char **array, int count, Str *str); + + static int equals(Str *str, const char *other); + + static int toInt(Str *str); + + static Atlas *abortAtlas(Atlas *atlas); +}; } -#endif -#endif /* SPINE_ATLAS_H_ */ +#endif /* Spine_Atlas_h */ diff --git a/cocos/editor-support/spine/AtlasAttachmentLoader.cpp b/cocos/editor-support/spine/AtlasAttachmentLoader.cpp new file mode 100644 index 000000000000..aa45e5971e4c --- /dev/null +++ b/cocos/editor-support/spine/AtlasAttachmentLoader.cpp @@ -0,0 +1,128 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace spine { +RTTI_IMPL(AtlasAttachmentLoader, AttachmentLoader) + +AtlasAttachmentLoader::AtlasAttachmentLoader(Atlas *atlas) : AttachmentLoader(), _atlas(atlas) { +} + +RegionAttachment *AtlasAttachmentLoader::newRegionAttachment(Skin &skin, const String &name, const String &path) { + SP_UNUSED(skin); + + AtlasRegion *regionP = findRegion(path); + if (!regionP) return NULL; + + AtlasRegion ®ion = *regionP; + + RegionAttachment *attachmentP = new(__FILE__, __LINE__) RegionAttachment(name); + + RegionAttachment &attachment = *attachmentP; + attachment.setRendererObject(regionP); + attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment._regionOffsetX = region.offsetX; + attachment._regionOffsetY = region.offsetY; + attachment._regionWidth = (float)region.width; + attachment._regionHeight = (float)region.height; + attachment._regionOriginalWidth = (float)region.originalWidth; + attachment._regionOriginalHeight = (float)region.originalHeight; + return attachmentP; +} + +MeshAttachment *AtlasAttachmentLoader::newMeshAttachment(Skin &skin, const String &name, const String &path) { + SP_UNUSED(skin); + + AtlasRegion *regionP = findRegion(path); + if (!regionP) return NULL; + + AtlasRegion ®ion = *regionP; + + MeshAttachment *attachmentP = new(__FILE__, __LINE__) MeshAttachment(name); + + MeshAttachment &attachment = *attachmentP; + attachment.setRendererObject(regionP); + attachment._regionU = region.u; + attachment._regionV = region.v; + attachment._regionU2 = region.u2; + attachment._regionV2 = region.v2; + attachment._regionRotate = region.rotate; + attachment._regionOffsetX = region.offsetX; + attachment._regionOffsetY = region.offsetY; + attachment._regionWidth = (float)region.width; + attachment._regionHeight = (float)region.height; + attachment._regionOriginalWidth = (float)region.originalWidth; + attachment._regionOriginalHeight = (float)region.originalHeight; + + return attachmentP; +} + +BoundingBoxAttachment *AtlasAttachmentLoader::newBoundingBoxAttachment(Skin &skin, const String &name) { + SP_UNUSED(skin); + return new(__FILE__, __LINE__) BoundingBoxAttachment(name); +} + +PathAttachment *AtlasAttachmentLoader::newPathAttachment(Skin &skin, const String &name) { + SP_UNUSED(skin); + return new(__FILE__, __LINE__) PathAttachment(name); +} + +PointAttachment *AtlasAttachmentLoader::newPointAttachment(Skin &skin, const String &name) { + SP_UNUSED(skin); + return new(__FILE__, __LINE__) PointAttachment(name); +} + +ClippingAttachment *AtlasAttachmentLoader::newClippingAttachment(Skin &skin, const String &name) { + SP_UNUSED(skin); + return new(__FILE__, __LINE__) ClippingAttachment(name); +} + +void AtlasAttachmentLoader::configureAttachment(Attachment* attachment) { + SP_UNUSED(attachment); +} + +AtlasRegion *AtlasAttachmentLoader::findRegion(const String &name) { + return _atlas->findRegion(name); +} + +} diff --git a/cocos/editor-support/spine/AtlasAttachmentLoader.h b/cocos/editor-support/spine/AtlasAttachmentLoader.h index a62b14e5b6e3..ff1c1208ffcf 100644 --- a/cocos/editor-support/spine/AtlasAttachmentLoader.h +++ b/cocos/editor-support/spine/AtlasAttachmentLoader.h @@ -1,58 +1,73 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_ATLASATTACHMENTLOADER_H_ -#define SPINE_ATLASATTACHMENTLOADER_H_ +#ifndef Spine_AtlasAttachmentLoader_h +#define Spine_AtlasAttachmentLoader_h -#include #include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spAtlasAttachmentLoader { - spAttachmentLoader super; - spAtlas* atlas; -} spAtlasAttachmentLoader; +#include +#include -SP_API spAtlasAttachmentLoader* spAtlasAttachmentLoader_create (spAtlas* atlas); -#ifdef SPINE_SHORT_NAMES -typedef spAtlasAttachmentLoader AtlasAttachmentLoader; -#define AtlasAttachmentLoader_create(...) spAtlasAttachmentLoader_create(__VA_ARGS__) -#endif +namespace spine { + class Atlas; + class AtlasRegion; + + /// + /// An AttachmentLoader that configures attachments using texture regions from an Atlas. + /// See http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data about Loading Skeleton Data in the Spine Runtimes Guide. + /// + class SP_API AtlasAttachmentLoader : public AttachmentLoader { + public: + RTTI_DECL + + explicit AtlasAttachmentLoader(Atlas* atlas); + + virtual RegionAttachment* newRegionAttachment(Skin& skin, const String& name, const String& path); + + virtual MeshAttachment* newMeshAttachment(Skin& skin, const String& name, const String& path); + + virtual BoundingBoxAttachment* newBoundingBoxAttachment(Skin& skin, const String& name); + + virtual PathAttachment* newPathAttachment(Skin& skin, const String& name); + + virtual PointAttachment* newPointAttachment(Skin& skin, const String& name); + + virtual ClippingAttachment* newClippingAttachment(Skin& skin, const String& name); -#ifdef __cplusplus + virtual void configureAttachment(Attachment* attachment); + + AtlasRegion* findRegion(const String& name); + + private: + Atlas* _atlas; + }; } -#endif -#endif /* SPINE_ATLASATTACHMENTLOADER_H_ */ +#endif /* Spine_AtlasAttachmentLoader_h */ diff --git a/cocos/editor-support/spine/Attachment.cpp b/cocos/editor-support/spine/Attachment.cpp new file mode 100644 index 000000000000..8a014786176a --- /dev/null +++ b/cocos/editor-support/spine/Attachment.cpp @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +using namespace spine; + +RTTI_IMPL_NOPARENT(Attachment) + +Attachment::Attachment(const String &name) : _name(name) { + assert(_name.length() > 0); +} + +Attachment::~Attachment() { +} + +const String &Attachment::getName() const { + return _name; +} diff --git a/cocos/editor-support/spine/Attachment.h b/cocos/editor-support/spine/Attachment.h index 39d2cf3a0872..65945d25430b 100644 --- a/cocos/editor-support/spine/Attachment.h +++ b/cocos/editor-support/spine/Attachment.h @@ -1,83 +1,53 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_ATTACHMENT_H_ -#define SPINE_ATTACHMENT_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct spAttachmentLoader; +#ifndef Spine_Attachment_h +#define Spine_Attachment_h -typedef enum { - SP_ATTACHMENT_REGION, - SP_ATTACHMENT_BOUNDING_BOX, - SP_ATTACHMENT_MESH, - SP_ATTACHMENT_LINKED_MESH, - SP_ATTACHMENT_PATH, - SP_ATTACHMENT_POINT, - SP_ATTACHMENT_CLIPPING -} spAttachmentType; +#include +#include +#include -typedef struct spAttachment { - const char* const name; - const spAttachmentType type; - const void* const vtable; - struct spAttachmentLoader* attachmentLoader; +namespace spine { +class SP_API Attachment : public SpineObject { +RTTI_DECL -#ifdef __cplusplus - spAttachment() : - name(0), - type(SP_ATTACHMENT_REGION), - vtable(0) { - } -#endif -} spAttachment; +public: + explicit Attachment(const String &name); -void spAttachment_dispose (spAttachment* self); + virtual ~Attachment(); -#ifdef SPINE_SHORT_NAMES -typedef spAttachmentType AttachmentType; -#define ATTACHMENT_REGION SP_ATTACHMENT_REGION -#define ATTACHMENT_BOUNDING_BOX SP_ATTACHMENT_BOUNDING_BOX -#define ATTACHMENT_MESH SP_ATTACHMENT_MESH -#define ATTACHMENT_LINKED_MESH SP_ATTACHMENT_LINKED_MESH -typedef spAttachment Attachment; -#define Attachment_dispose(...) spAttachment_dispose(__VA_ARGS__) -#endif + const String &getName() const; -#ifdef __cplusplus +private: + const String _name; +}; } -#endif -#endif /* SPINE_ATTACHMENT_H_ */ +#endif /* Spine_Attachment_h */ diff --git a/cocos/editor-support/spine/AttachmentLoader.cpp b/cocos/editor-support/spine/AttachmentLoader.cpp new file mode 100644 index 000000000000..ae5c4a79f751 --- /dev/null +++ b/cocos/editor-support/spine/AttachmentLoader.cpp @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL_NOPARENT(AttachmentLoader) + +AttachmentLoader::AttachmentLoader() { +} + +AttachmentLoader::~AttachmentLoader() { +} diff --git a/cocos/editor-support/spine/AttachmentLoader.h b/cocos/editor-support/spine/AttachmentLoader.h index 774ff8ecdfce..6efe0c63ac0d 100644 --- a/cocos/editor-support/spine/AttachmentLoader.h +++ b/cocos/editor-support/spine/AttachmentLoader.h @@ -1,79 +1,75 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_ATTACHMENTLOADER_H_ -#define SPINE_ATTACHMENTLOADER_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spAttachmentLoader { - const char* error1; - const char* error2; - - const void* const vtable; -#ifdef __cplusplus - spAttachmentLoader () : - error1(0), - error2(0), - vtable(0) { - } -#endif -} spAttachmentLoader; - -SP_API void spAttachmentLoader_dispose (spAttachmentLoader* self); +#ifndef Spine_AttachmentLoader_h +#define Spine_AttachmentLoader_h -/* Called to create each attachment. Returns 0 to not load an attachment. If 0 is returned and _spAttachmentLoader_setError was - * called, an error occurred. */ -SP_API spAttachment* spAttachmentLoader_createAttachment (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name, - const char* path); -/* Called after the attachment has been fully configured. */ -SP_API void spAttachmentLoader_configureAttachment (spAttachmentLoader* self, spAttachment* attachment); -/* Called just before the attachment is disposed. This can release allocations made in spAttachmentLoader_configureAttachment. */ -SP_API void spAttachmentLoader_disposeAttachment (spAttachmentLoader* self, spAttachment* attachment); +#include +#include +#include -#ifdef SPINE_SHORT_NAMES -typedef spAttachmentLoader AttachmentLoader; -#define AttachmentLoader_dispose(...) spAttachmentLoader_dispose(__VA_ARGS__) -#define AttachmentLoader_createAttachment(...) spAttachmentLoader_createAttachment(__VA_ARGS__) -#define AttachmentLoader_configureAttachment(...) spAttachmentLoader_configureAttachment(__VA_ARGS__) -#define AttachmentLoader_disposeAttachment(...) spAttachmentLoader_disposeAttachment(__VA_ARGS__) -#endif +namespace spine { + class Skin; + class Attachment; + class RegionAttachment; + class MeshAttachment; + class BoundingBoxAttachment; + class PathAttachment; + class PointAttachment; + class ClippingAttachment; + + class SP_API AttachmentLoader : public SpineObject { + public: + RTTI_DECL + + AttachmentLoader(); + + virtual ~AttachmentLoader(); + + /// @return May be NULL to not load any attachment. + virtual RegionAttachment* newRegionAttachment(Skin& skin, const String& name, const String& path) = 0; + + /// @return May be NULL to not load any attachment. + virtual MeshAttachment* newMeshAttachment(Skin& skin, const String& name, const String& path) = 0; + + /// @return May be NULL to not load any attachment. + virtual BoundingBoxAttachment* newBoundingBoxAttachment(Skin& skin, const String& name) = 0; + + /// @return May be NULL to not load any attachment + virtual PathAttachment* newPathAttachment(Skin& skin, const String& name) = 0; + + virtual PointAttachment* newPointAttachment(Skin& skin, const String& name) = 0; + + virtual ClippingAttachment* newClippingAttachment(Skin& skin, const String& name) = 0; -#ifdef __cplusplus + virtual void configureAttachment(Attachment* attachment) = 0; + }; } -#endif -#endif /* SPINE_ATTACHMENTLOADER_H_ */ +#endif /* Spine_AttachmentLoader_h */ diff --git a/cocos/editor-support/spine/AttachmentTimeline.cpp b/cocos/editor-support/spine/AttachmentTimeline.cpp new file mode 100644 index 000000000000..78d84cccc892 --- /dev/null +++ b/cocos/editor-support/spine/AttachmentTimeline.cpp @@ -0,0 +1,124 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(AttachmentTimeline, Timeline) + +AttachmentTimeline::AttachmentTimeline(int frameCount) : Timeline(), _slotIndex(0) { + _frames.ensureCapacity(frameCount); + _attachmentNames.ensureCapacity(frameCount); + + _frames.setSize(frameCount, 0); + + for (int i = 0; i < frameCount; ++i) { + _attachmentNames.add(String()); + } +} + +void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(alpha); + + assert(_slotIndex < skeleton._slots.size()); + + String *attachmentName; + Slot *slotP = skeleton._slots[_slotIndex]; + Slot &slot = *slotP; + if (direction == MixDirection_Out && blend == MixBlend_Setup) { + attachmentName = &slot._data._attachmentName; + slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName)); + return; + } + + if (time < _frames[0]) { + // Time is before first frame. + if (blend == MixBlend_Setup || blend == MixBlend_First) { + attachmentName = &slot._data._attachmentName; + slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName)); + } + return; + } + + size_t frameIndex; + if (time >= _frames[_frames.size() - 1]) { + // Time is after last frame. + frameIndex = _frames.size() - 1; + } else { + frameIndex = Animation::binarySearch(_frames, time, 1) - 1; + } + + attachmentName = &_attachmentNames[frameIndex]; + slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName)); +} + +int AttachmentTimeline::getPropertyId() { + return ((int) TimelineType_Attachment << 24) + _slotIndex; +} + +void AttachmentTimeline::setFrame(int frameIndex, float time, const String &attachmentName) { + _frames[frameIndex] = time; + _attachmentNames[frameIndex] = attachmentName; +} + +size_t AttachmentTimeline::getSlotIndex() { + return _slotIndex; +} + +void AttachmentTimeline::setSlotIndex(size_t inValue) { + _slotIndex = inValue; +} + +const Vector &AttachmentTimeline::getFrames() { + return _frames; +} + +const Vector &AttachmentTimeline::getAttachmentNames() { + return _attachmentNames; +} + +size_t AttachmentTimeline::getFrameCount() { + return _frames.size(); +} diff --git a/cocos/editor-support/spine/AttachmentTimeline.h b/cocos/editor-support/spine/AttachmentTimeline.h new file mode 100644 index 000000000000..4fb110be0e1c --- /dev/null +++ b/cocos/editor-support/spine/AttachmentTimeline.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_AttachmentTimeline_h +#define Spine_AttachmentTimeline_h + +#include +#include +#include +#include +#include +#include + +namespace spine { + + class Skeleton; + class Event; + + class SP_API AttachmentTimeline : public Timeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + explicit AttachmentTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, const String& attachmentName); + + size_t getSlotIndex(); + void setSlotIndex(size_t inValue); + const Vector& getFrames(); + const Vector& getAttachmentNames(); + size_t getFrameCount(); + private: + size_t _slotIndex; + Vector _frames; + Vector _attachmentNames; + }; +} + +#endif /* Spine_AttachmentTimeline_h */ diff --git a/cocos/editor-support/spine/AttachmentType.h b/cocos/editor-support/spine/AttachmentType.h new file mode 100644 index 000000000000..25681e334501 --- /dev/null +++ b/cocos/editor-support/spine/AttachmentType.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_AttachmentType_h +#define Spine_AttachmentType_h + +namespace spine { + enum AttachmentType { + AttachmentType_Region, + AttachmentType_Boundingbox, + AttachmentType_Mesh, + AttachmentType_Linkedmesh, + AttachmentType_Path, + AttachmentType_Point, + AttachmentType_Clipping + }; +} + +#endif /* Spine_AttachmentType_h */ diff --git a/cocos/editor-support/spine/AttachmentVertices.cpp b/cocos/editor-support/spine/AttachmentVertices.cpp index ce614ec74688..54c681abdb9f 100644 --- a/cocos/editor-support/spine/AttachmentVertices.cpp +++ b/cocos/editor-support/spine/AttachmentVertices.cpp @@ -1,31 +1,30 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include diff --git a/cocos/editor-support/spine/AttachmentVertices.h b/cocos/editor-support/spine/AttachmentVertices.h index facf4d64f14d..c77450f5e649 100644 --- a/cocos/editor-support/spine/AttachmentVertices.h +++ b/cocos/editor-support/spine/AttachmentVertices.h @@ -1,31 +1,30 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef SPINE_ATTACHMENTVERTICES_H_ diff --git a/cocos/editor-support/spine/BlendMode.h b/cocos/editor-support/spine/BlendMode.h new file mode 100644 index 000000000000..30540f0abc2a --- /dev/null +++ b/cocos/editor-support/spine/BlendMode.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_BlendMode_h +#define Spine_BlendMode_h + +namespace spine { +enum BlendMode { + BlendMode_Normal = 0, + BlendMode_Additive, + BlendMode_Multiply, + BlendMode_Screen +}; +} + +#endif /* Spine_BlendMode_h */ diff --git a/cocos/editor-support/spine/Bone.cpp b/cocos/editor-support/spine/Bone.cpp new file mode 100644 index 000000000000..6e0bdcbc08df --- /dev/null +++ b/cocos/editor-support/spine/Bone.cpp @@ -0,0 +1,540 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +using namespace spine; + +RTTI_IMPL(Bone, Updatable) + +bool Bone::yDown = false; + +void Bone::setYDown(bool inValue) { + yDown = inValue; +} + +bool Bone::isYDown() { + return yDown; +} + +Bone::Bone(BoneData &data, Skeleton &skeleton, Bone *parent) : Updatable(), + _data(data), + _skeleton(skeleton), + _parent(parent), + _x(0), + _y(0), + _rotation(0), + _scaleX(0), + _scaleY(0), + _shearX(0), + _shearY(0), + _ax(0), + _ay(0), + _arotation(0), + _ascaleX(0), + _ascaleY(0), + _ashearX(0), + _ashearY(0), + _appliedValid(false), + _a(1), + _b(0), + _worldX(0), + _c(0), + _d(1), + _worldY(0), + _sorted(false) { + setToSetupPose(); +} + +void Bone::update() { + updateWorldTransform(_x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY); +} + +void Bone::updateWorldTransform() { + updateWorldTransform(_x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY); +} + +void +Bone::updateWorldTransform(float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) { + float cosine, sine; + float pa, pb, pc, pd; + Bone *parent = _parent; + + _ax = x; + _ay = y; + _arotation = rotation; + _ascaleX = scaleX; + _ascaleY = scaleY; + _ashearX = shearX; + _ashearY = shearY; + _appliedValid = true; + + if (!parent) { /* Root bone. */ + float rotationY = rotation + 90 + shearY; + float sx = _skeleton.getScaleX(); + float sy = _skeleton.getScaleY(); + _a = MathUtil::cosDeg(rotation + shearX) * scaleX * sx; + _b = MathUtil::cosDeg(rotationY) * scaleY * sx; + _c = MathUtil::sinDeg(rotation + shearX) * scaleX * sy; + _d = MathUtil::sinDeg(rotationY) * scaleY * sy; + _worldX = x * sx + _skeleton.getX(); + _worldY = y * sy + _skeleton.getY(); + return; + } + + pa = parent->_a; + pb = parent->_b; + pc = parent->_c; + pd = parent->_d; + + _worldX = pa * x + pb * y + parent->_worldX; + _worldY = pc * x + pd * y + parent->_worldY; + + switch (_data.getTransformMode()) { + case TransformMode_Normal: { + float rotationY = rotation + 90 + shearY; + float la = MathUtil::cosDeg(rotation + shearX) * scaleX; + float lb = MathUtil::cosDeg(rotationY) * scaleY; + float lc = MathUtil::sinDeg(rotation + shearX) * scaleX; + float ld = MathUtil::sinDeg(rotationY) * scaleY; + _a = pa * la + pb * lc; + _b = pa * lb + pb * ld; + _c = pc * la + pd * lc; + _d = pc * lb + pd * ld; + return; + } + case TransformMode_OnlyTranslation: { + float rotationY = rotation + 90 + shearY; + _a = MathUtil::cosDeg(rotation + shearX) * scaleX; + _b = MathUtil::cosDeg(rotationY) * scaleY; + _c = MathUtil::sinDeg(rotation + shearX) * scaleX; + _d = MathUtil::sinDeg(rotationY) * scaleY; + break; + } + case TransformMode_NoRotationOrReflection: { + float s = pa * pa + pc * pc; + float prx, rx, ry, la, lb, lc, ld; + if (s > 0.0001f) { + s = MathUtil::abs(pa * pd - pb * pc) / s; + pb = pc * s; + pd = pa * s; + prx = MathUtil::atan2(pc, pa) * MathUtil::Rad_Deg; + } else { + pa = 0; + pc = 0; + prx = 90 - MathUtil::atan2(pd, pb) * MathUtil::Rad_Deg; + } + rx = rotation + shearX - prx; + ry = rotation + shearY - prx + 90; + la = MathUtil::cosDeg(rx) * scaleX; + lb = MathUtil::cosDeg(ry) * scaleY; + lc = MathUtil::sinDeg(rx) * scaleX; + ld = MathUtil::sinDeg(ry) * scaleY; + _a = pa * la - pb * lc; + _b = pa * lb - pb * ld; + _c = pc * la + pd * lc; + _d = pc * lb + pd * ld; + break; + } + case TransformMode_NoScale: + case TransformMode_NoScaleOrReflection: { + float za, zc, s; + float r, zb, zd, la, lb, lc, ld; + cosine = MathUtil::cosDeg(rotation); + sine = MathUtil::sinDeg(rotation); + za = (pa * cosine + pb * sine) / _skeleton.getScaleX(); + zc = (pc * cosine + pd * sine) / _skeleton.getScaleY(); + s = MathUtil::sqrt(za * za + zc * zc); + if (s > 0.00001f) s = 1 / s; + za *= s; + zc *= s; + s = MathUtil::sqrt(za * za + zc * zc); + if (_data.getTransformMode() == TransformMode_NoScale + && (pa * pd - pb * pc < 0) != (_skeleton.getScaleX() < 0 != _skeleton.getScaleY() < 0)) + s = -s; + r = MathUtil::Pi / 2 + MathUtil::atan2(zc, za); + zb = MathUtil::cos(r) * s; + zd = MathUtil::sin(r) * s; + la = MathUtil::cosDeg(shearX) * scaleX; + lb = MathUtil::cosDeg(90 + shearY) * scaleY; + lc = MathUtil::sinDeg(shearX) * scaleX; + ld = MathUtil::sinDeg(90 + shearY) * scaleY; + _a = za * la + zb * lc; + _b = za * lb + zb * ld; + _c = zc * la + zd * lc; + _d = zc * lb + zd * ld; + break; + } + } + _a *= _skeleton.getScaleX(); + _b *= _skeleton.getScaleX(); + _c *= _skeleton.getScaleY(); + _d *= _skeleton.getScaleY(); +} + +void Bone::setToSetupPose() { + BoneData &data = _data; + _x = data.getX(); + _y = data.getY(); + _rotation = data.getRotation(); + _scaleX = data.getScaleX(); + _scaleY = data.getScaleY(); + _shearX = data.getShearX(); + _shearY = data.getShearY(); +} + +void Bone::worldToLocal(float worldX, float worldY, float &outLocalX, float &outLocalY) { + float a = _a; + float b = _b; + float c = _c; + float d = _d; + + float invDet = 1 / (a * d - b * c); + float x = worldX - _worldX; + float y = worldY - _worldY; + + outLocalX = (x * d * invDet - y * b * invDet); + outLocalY = (y * a * invDet - x * c * invDet); +} + +void Bone::localToWorld(float localX, float localY, float &outWorldX, float &outWorldY) { + outWorldX = localX * _a + localY * _b + _worldX; + outWorldY = localX * _c + localY * _d + _worldY; +} + +float Bone::worldToLocalRotation(float worldRotation) { + float sin = MathUtil::sinDeg(worldRotation); + float cos = MathUtil::cosDeg(worldRotation); + + return MathUtil::atan2(_a * sin - _c * cos, _d * cos - _b * sin) * MathUtil::Rad_Deg + this->_rotation - this->_shearX; +} + +float Bone::localToWorldRotation(float localRotation) { + localRotation -= this->_rotation - this->_shearX; + float sin = MathUtil::sinDeg(localRotation); + float cos = MathUtil::cosDeg(localRotation); + + return MathUtil::atan2(cos * _c + sin * _d, cos * _a + sin * _b) * MathUtil::Rad_Deg; +} + +void Bone::rotateWorld(float degrees) { + float a = _a; + float b = _b; + float c = _c; + float d = _d; + + float cos = MathUtil::cosDeg(degrees); + float sin = MathUtil::sinDeg(degrees); + + _a = cos * a - sin * c; + _b = cos * b - sin * d; + _c = sin * a + cos * c; + _d = sin * b + cos * d; + + _appliedValid = false; +} + +float Bone::getWorldToLocalRotationX() { + Bone *parent = _parent; + if (!parent) { + return _arotation; + } + + float pa = parent->_a; + float pb = parent->_b; + float pc = parent->_c; + float pd = parent->_d; + float a = _a; + float c = _c; + + return MathUtil::atan2(pa * c - pc * a, pd * a - pb * c) * MathUtil::Rad_Deg; +} + +float Bone::getWorldToLocalRotationY() { + Bone *parent = _parent; + if (!parent) { + return _arotation; + } + + float pa = parent->_a; + float pb = parent->_b; + float pc = parent->_c; + float pd = parent->_d; + float b = _b; + float d = _d; + + return MathUtil::atan2(pa * d - pc * b, pd * b - pb * d) * MathUtil::Rad_Deg; +} + +BoneData &Bone::getData() { + return _data; +} + +Skeleton &Bone::getSkeleton() { + return _skeleton; +} + +Bone *Bone::getParent() { + return _parent; +} + +Vector &Bone::getChildren() { + return _children; +} + +float Bone::getX() { + return _x; +} + +void Bone::setX(float inValue) { + _x = inValue; +} + +float Bone::getY() { + return _y; +} + +void Bone::setY(float inValue) { + _y = inValue; +} + +float Bone::getRotation() { + return _rotation; +} + +void Bone::setRotation(float inValue) { + _rotation = inValue; +} + +float Bone::getScaleX() { + return _scaleX; +} + +void Bone::setScaleX(float inValue) { + _scaleX = inValue; +} + +float Bone::getScaleY() { + return _scaleY; +} + +void Bone::setScaleY(float inValue) { + _scaleY = inValue; +} + +float Bone::getShearX() { + return _shearX; +} + +void Bone::setShearX(float inValue) { + _shearX = inValue; +} + +float Bone::getShearY() { + return _shearY; +} + +void Bone::setShearY(float inValue) { + _shearY = inValue; +} + +float Bone::getAppliedRotation() { + return _arotation; +} + +void Bone::setAppliedRotation(float inValue) { + _arotation = inValue; +} + +float Bone::getAX() { + return _ax; +} + +void Bone::setAX(float inValue) { + _ax = inValue; +} + +float Bone::getAY() { + return _ay; +} + +void Bone::setAY(float inValue) { + _ay = inValue; +} + +float Bone::getAScaleX() { + return _ascaleX; +} + +void Bone::setAScaleX(float inValue) { + _ascaleX = inValue; +} + +float Bone::getAScaleY() { + return _ascaleY; +} + +void Bone::setAScaleY(float inValue) { + _ascaleY = inValue; +} + +float Bone::getAShearX() { + return _ashearX; +} + +void Bone::setAShearX(float inValue) { + _ashearX = inValue; +} + +float Bone::getAShearY() { + return _ashearY; +} + +void Bone::setAShearY(float inValue) { + _ashearY = inValue; +} + +float Bone::getA() { + return _a; +} + +void Bone::setA(float inValue) { + _a = inValue; +} + +float Bone::getB() { + return _b; +} + +void Bone::setB(float inValue) { + _b = inValue; +} + +float Bone::getC() { + return _c; +} + +void Bone::setC(float inValue) { + _c = inValue; +} + +float Bone::getD() { + return _d; +} + +void Bone::setD(float inValue) { + _d = inValue; +} + +float Bone::getWorldX() { + return _worldX; +} + +void Bone::setWorldX(float inValue) { + _worldX = inValue; +} + +float Bone::getWorldY() { + return _worldY; +} + +void Bone::setWorldY(float inValue) { + _worldY = inValue; +} + +float Bone::getWorldRotationX() { + return MathUtil::atan2(_c, _a) * MathUtil::MathUtil::Rad_Deg; +} + +float Bone::getWorldRotationY() { + return MathUtil::atan2(_d, _b) * MathUtil::Rad_Deg; +} + +float Bone::getWorldScaleX() { + return MathUtil::sqrt(_a * _a + _c * _c); +} + +float Bone::getWorldScaleY() { + return MathUtil::sqrt(_b * _b + _d * _d); +} + +bool Bone::isAppliedValid() { + return _appliedValid; +} +void Bone::setAppliedValid(bool valid) { + _appliedValid = valid; +} + +void Bone::updateAppliedTransform() { + Bone *parent = _parent; + _appliedValid = 1; + if (!parent) { + _ax = _worldX; + _ay = _worldY; + _arotation = MathUtil::atan2(_c, _a) * MathUtil::Rad_Deg; + _ascaleX = MathUtil::sqrt(_a * _a + _c * _c); + _ascaleY = MathUtil::sqrt(_b * _b + _d * _d); + _ashearX = 0; + _ashearY = MathUtil::atan2(_a * _b + _c * _d, _a * _d - _b * _c) * MathUtil::Rad_Deg; + } else { + float pa = parent->_a, pb = parent->_b, pc = parent->_c, pd = parent->_d; + float pid = 1 / (pa * pd - pb * pc); + float dx = _worldX - parent->_worldX, dy = _worldY - parent->_worldY; + float ia = pid * pd; + float id = pid * pa; + float ib = pid * pb; + float ic = pid * pc; + float ra = ia * _a - ib * _c; + float rb = ia * _b - ib * _d; + float rc = id * _c - ic * _a; + float rd = id * _d - ic * _b; + _ax = (dx * pd * pid - dy * pb * pid); + _ay = (dy * pa * pid - dx * pc * pid); + _ashearX = 0; + _ascaleX = MathUtil::sqrt(ra * ra + rc * rc); + if (_ascaleX > 0.0001f) { + float det = ra * rd - rb * rc; + _ascaleY = det / _ascaleX; + _ashearY = MathUtil::atan2(ra * rb + rc * rd, det) * MathUtil::Rad_Deg; + _arotation = MathUtil::atan2(rc, ra) * MathUtil::Rad_Deg; + } else { + _ascaleX = 0; + _ascaleY = MathUtil::sqrt(rb * rb + rd * rd); + _ashearY = 0; + _arotation = 90 - MathUtil::atan2(rd, rb) * MathUtil::Rad_Deg; + } + } +} diff --git a/cocos/editor-support/spine/Bone.h b/cocos/editor-support/spine/Bone.h index 725152a62748..f0026d233fb8 100644 --- a/cocos/editor-support/spine/Bone.h +++ b/cocos/editor-support/spine/Bone.h @@ -1,127 +1,248 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_BONE_H_ -#define SPINE_BONE_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct spSkeleton; - -typedef struct spBone spBone; -struct spBone { - spBoneData* const data; - struct spSkeleton* const skeleton; - spBone* const parent; - int childrenCount; - spBone** const children; - float x, y, rotation, scaleX, scaleY, shearX, shearY; - float ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY; - int /*bool*/ appliedValid; - - float const a, b, worldX; - float const c, d, worldY; - - int/*bool*/ sorted; - -#ifdef __cplusplus - spBone() : - data(0), - skeleton(0), - parent(0), - childrenCount(0), children(0), - x(0), y(0), rotation(0), scaleX(0), scaleY(0), - ax(0), ay(0), arotation(0), ascaleX(0), ascaleY(0), ashearX(0), ashearY(0), - appliedValid(0), - - a(0), b(0), worldX(0), - c(0), d(0), worldY(0), - - sorted(0) { - } -#endif -}; +#ifndef Spine_Bone_h +#define Spine_Bone_h + +#include +#include +#include + +namespace spine { +class BoneData; + +class Skeleton; + +/// Stores a bone's current pose. +/// +/// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a +/// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a +/// constraint or application code modifies the world transform after it was computed from the local transform. +class SP_API Bone : public Updatable { + friend class AnimationState; + + friend class RotateTimeline; + + friend class IkConstraint; + + friend class TransformConstraint; + + friend class VertexAttachment; + + friend class PathConstraint; + + friend class Skeleton; + + friend class RegionAttachment; + + friend class PointAttachment; + + friend class ScaleTimeline; + + friend class ShearTimeline; + + friend class TranslateTimeline; + +RTTI_DECL + +public: + static void setYDown(bool inValue); + + static bool isYDown(); + + /// @param parent May be NULL. + Bone(BoneData &data, Skeleton &skeleton, Bone *parent = NULL); + + /// Same as updateWorldTransform. This method exists for Bone to implement Spine::Updatable. + virtual void update(); + + /// Computes the world transform using the parent bone and this bone's local transform. + void updateWorldTransform(); + + /// Computes the world transform using the parent bone and the specified local transform. + void updateWorldTransform(float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY); + + void setToSetupPose(); + + void worldToLocal(float worldX, float worldY, float &outLocalX, float &outLocalY); + + void localToWorld(float localX, float localY, float &outWorldX, float &outWorldY); + + float worldToLocalRotation(float worldRotation); + + float localToWorldRotation(float localRotation); + + /// + /// Rotates the world transform the specified amount and sets isAppliedValid to false. + /// + /// @param degrees Degrees. + void rotateWorld(float degrees); + + float getWorldToLocalRotationX(); + + float getWorldToLocalRotationY(); + + BoneData &getData(); + + Skeleton &getSkeleton(); + + Bone *getParent(); + + Vector &getChildren(); + + /// The local X translation. + float getX(); + + void setX(float inValue); + + /// The local Y translation. + float getY(); + + void setY(float inValue); + + /// The local rotation. + float getRotation(); + + void setRotation(float inValue); -SP_API void spBone_setYDown (int/*bool*/yDown); -SP_API int/*bool*/spBone_isYDown (); - -/* @param parent May be 0. */ -SP_API spBone* spBone_create (spBoneData* data, struct spSkeleton* skeleton, spBone* parent); -SP_API void spBone_dispose (spBone* self); - -SP_API void spBone_setToSetupPose (spBone* self); - -SP_API void spBone_updateWorldTransform (spBone* self); -SP_API void spBone_updateWorldTransformWith (spBone* self, float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY); - -SP_API float spBone_getWorldRotationX (spBone* self); -SP_API float spBone_getWorldRotationY (spBone* self); -SP_API float spBone_getWorldScaleX (spBone* self); -SP_API float spBone_getWorldScaleY (spBone* self); - -SP_API void spBone_updateAppliedTransform (spBone* self); - -SP_API void spBone_worldToLocal (spBone* self, float worldX, float worldY, float* localX, float* localY); -SP_API void spBone_localToWorld (spBone* self, float localX, float localY, float* worldX, float* worldY); -SP_API float spBone_worldToLocalRotation (spBone* self, float worldRotation); -SP_API float spBone_localToWorldRotation (spBone* self, float localRotation); -SP_API void spBone_rotateWorld (spBone* self, float degrees); - -#ifdef SPINE_SHORT_NAMES -typedef spBone Bone; -#define Bone_setYDown(...) spBone_setYDown(__VA_ARGS__) -#define Bone_isYDown() spBone_isYDown() -#define Bone_create(...) spBone_create(__VA_ARGS__) -#define Bone_dispose(...) spBone_dispose(__VA_ARGS__) -#define Bone_setToSetupPose(...) spBone_setToSetupPose(__VA_ARGS__) -#define Bone_updateWorldTransform(...) spBone_updateWorldTransform(__VA_ARGS__) -#define Bone_updateWorldTransformWith(...) spBone_updateWorldTransformWith(__VA_ARGS__) -#define Bone_getWorldRotationX(...) spBone_getWorldRotationX(__VA_ARGS__) -#define Bone_getWorldRotationY(...) spBone_getWorldRotationY(__VA_ARGS__) -#define Bone_getWorldScaleX(...) spBone_getWorldScaleX(__VA_ARGS__) -#define Bone_getWorldScaleY(...) spBone_getWorldScaleY(__VA_ARGS__) -#define Bone_updateAppliedTransform(...) spBone_updateAppliedTransform(__VA_ARGS__) -#define Bone_worldToLocal(...) spBone_worldToLocal(__VA_ARGS__) -#define Bone_localToWorld(...) spBone_localToWorld(__VA_ARGS__) -#define Bone_worldToLocalRotation(...) spBone_worldToLocalRotation(__VA_ARGS__) -#define Bone_localToWorldRotation(...) spBone_localToWorldRotation(__VA_ARGS__) -#define Bone_rotateWorld(...) spBone_rotateWorld(__VA_ARGS__) -#endif - -#ifdef __cplusplus + /// The local scaleX. + float getScaleX(); + + void setScaleX(float inValue); + + /// The local scaleY. + float getScaleY(); + + void setScaleY(float inValue); + + /// The local shearX. + float getShearX(); + + void setShearX(float inValue); + + /// The local shearY. + float getShearY(); + + void setShearY(float inValue); + + /// The rotation, as calculated by any constraints. + float getAppliedRotation(); + + void setAppliedRotation(float inValue); + + /// The applied local x translation. + float getAX(); + + void setAX(float inValue); + + /// The applied local y translation. + float getAY(); + + void setAY(float inValue); + + /// The applied local scaleX. + float getAScaleX(); + + void setAScaleX(float inValue); + + /// The applied local scaleY. + float getAScaleY(); + + void setAScaleY(float inValue); + + /// The applied local shearX. + float getAShearX(); + + void setAShearX(float inValue); + + /// The applied local shearY. + float getAShearY(); + + void setAShearY(float inValue); + + float getA(); + + void setA(float inValue); + + float getB(); + + void setB(float inValue); + + float getC(); + + void setC(float inValue); + + float getD(); + + void setD(float inValue); + + float getWorldX(); + + void setWorldX(float inValue); + + float getWorldY(); + + void setWorldY(float inValue); + + float getWorldRotationX(); + + float getWorldRotationY(); + + /// Returns the magnitide (always positive) of the world scale X. + float getWorldScaleX(); + + /// Returns the magnitide (always positive) of the world scale Y. + float getWorldScaleY(); + + bool isAppliedValid(); + void setAppliedValid(bool valid); + +private: + static bool yDown; + + BoneData &_data; + Skeleton &_skeleton; + Bone *_parent; + Vector _children; + float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY; + float _ax, _ay, _arotation, _ascaleX, _ascaleY, _ashearX, _ashearY; + bool _appliedValid; + float _a, _b, _worldX; + float _c, _d, _worldY; + bool _sorted; + + /// Computes the individual applied transform values from the world transform. This can be useful to perform processing using + /// the applied transform after the world transform has been modified directly (eg, by a constraint).. + /// + /// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. + void updateAppliedTransform(); +}; } -#endif -#endif /* SPINE_BONE_H_ */ +#endif /* Spine_Bone_h */ diff --git a/cocos/editor-support/spine/BoneData.cpp b/cocos/editor-support/spine/BoneData.cpp new file mode 100644 index 000000000000..d70116c495a2 --- /dev/null +++ b/cocos/editor-support/spine/BoneData.cpp @@ -0,0 +1,139 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +using namespace spine; + +BoneData::BoneData(int index, const String &name, BoneData *parent) : + _index(index), + _name(name), + _parent(parent), + _length(0), + _x(0), + _y(0), + _rotation(0), + _scaleX(1), + _scaleY(1), + _shearX(0), + _shearY(0), + _transformMode(TransformMode_Normal) { + assert(index >= 0); + assert(_name.length() > 0); +} + +int BoneData::getIndex() { + return _index; +} + +const String &BoneData::getName() { + return _name; +} + +BoneData *BoneData::getParent() { + return _parent; +} + +float BoneData::getLength() { + return _length; +} + +void BoneData::setLength(float inValue) { + _length = inValue; +} + +float BoneData::getX() { + return _x; +} + +void BoneData::setX(float inValue) { + _x = inValue; +} + +float BoneData::getY() { + return _y; +} + +void BoneData::setY(float inValue) { + _y = inValue; +} + +float BoneData::getRotation() { + return _rotation; +} + +void BoneData::setRotation(float inValue) { + _rotation = inValue; +} + +float BoneData::getScaleX() { + return _scaleX; +} + +void BoneData::setScaleX(float inValue) { + _scaleX = inValue; +} + +float BoneData::getScaleY() { + return _scaleY; +} + +void BoneData::setScaleY(float inValue) { + _scaleY = inValue; +} + +float BoneData::getShearX() { + return _shearX; +} + +void BoneData::setShearX(float inValue) { + _shearX = inValue; +} + +float BoneData::getShearY() { + return _shearY; +} + +void BoneData::setShearY(float inValue) { + _shearY = inValue; +} + +TransformMode BoneData::getTransformMode() { + return _transformMode; +} + +void BoneData::setTransformMode(TransformMode inValue) { + _transformMode = inValue; +} diff --git a/cocos/editor-support/spine/BoneData.h b/cocos/editor-support/spine/BoneData.h index 1f4730b1b3b7..226ecf1bc423 100644 --- a/cocos/editor-support/spine/BoneData.h +++ b/cocos/editor-support/spine/BoneData.h @@ -1,85 +1,119 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_BONEDATA_H_ -#define SPINE_BONEDATA_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - SP_TRANSFORMMODE_NORMAL, - SP_TRANSFORMMODE_ONLYTRANSLATION, - SP_TRANSFORMMODE_NOROTATIONORREFLECTION, - SP_TRANSFORMMODE_NOSCALE, - SP_TRANSFORMMODE_NOSCALEORREFLECTION -} spTransformMode; - -typedef struct spBoneData spBoneData; -struct spBoneData { - const int index; - const char* const name; - spBoneData* const parent; - float length; - float x, y, rotation, scaleX, scaleY, shearX, shearY; - spTransformMode transformMode; - -#ifdef __cplusplus - spBoneData() : - index(0), - name(0), - parent(0), - length(0), - x(0), y(0), - rotation(0), - scaleX(0), scaleY(0), - shearX(0), shearY(0), - transformMode(SP_TRANSFORMMODE_NORMAL) { - } -#endif -}; +#ifndef Spine_BoneData_h +#define Spine_BoneData_h + +#include +#include +#include + +namespace spine { +class SP_API BoneData : public SpineObject { + friend class SkeletonBinary; + + friend class SkeletonJson; + + friend class AnimationState; + + friend class RotateTimeline; + + friend class ScaleTimeline; + + friend class ShearTimeline; + + friend class TranslateTimeline; + +public: + BoneData(int index, const String &name, BoneData *parent = NULL); + + /// The index of the bone in Skeleton.Bones + int getIndex(); + + /// The name of the bone, which is unique within the skeleton. + const String &getName(); + + /// May be NULL. + BoneData *getParent(); + + float getLength(); + + void setLength(float inValue); -SP_API spBoneData* spBoneData_create (int index, const char* name, spBoneData* parent); -SP_API void spBoneData_dispose (spBoneData* self); + /// Local X translation. + float getX(); -#ifdef SPINE_SHORT_NAMES -typedef spBoneData BoneData; -#define BoneData_create(...) spBoneData_create(__VA_ARGS__) -#define BoneData_dispose(...) spBoneData_dispose(__VA_ARGS__) -#endif + void setX(float inValue); -#ifdef __cplusplus + /// Local Y translation. + float getY(); + + void setY(float inValue); + + /// Local rotation. + float getRotation(); + + void setRotation(float inValue); + + /// Local scaleX. + float getScaleX(); + + void setScaleX(float inValue); + + /// Local scaleY. + float getScaleY(); + + void setScaleY(float inValue); + + /// Local shearX. + float getShearX(); + + void setShearX(float inValue); + + /// Local shearY. + float getShearY(); + + void setShearY(float inValue); + + /// The transform mode for how parent world transforms affect this bone. + TransformMode getTransformMode(); + + void setTransformMode(TransformMode inValue); + +private: + const int _index; + const String _name; + BoneData *_parent; + float _length; + float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY; + TransformMode _transformMode; +}; } -#endif -#endif /* SPINE_BONEDATA_H_ */ +#endif /* Spine_BoneData_h */ diff --git a/cocos/editor-support/spine/BoundingBoxAttachment.cpp b/cocos/editor-support/spine/BoundingBoxAttachment.cpp new file mode 100644 index 000000000000..3504be5f64c6 --- /dev/null +++ b/cocos/editor-support/spine/BoundingBoxAttachment.cpp @@ -0,0 +1,41 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +using namespace spine; + +RTTI_IMPL(BoundingBoxAttachment, VertexAttachment) + +BoundingBoxAttachment::BoundingBoxAttachment(const String &name) : VertexAttachment(name) { +} diff --git a/cocos/editor-support/spine/BoundingBoxAttachment.h b/cocos/editor-support/spine/BoundingBoxAttachment.h index 71d4876d30be..5d96b54ae6ac 100644 --- a/cocos/editor-support/spine/BoundingBoxAttachment.h +++ b/cocos/editor-support/spine/BoundingBoxAttachment.h @@ -1,59 +1,45 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_BOUNDINGBOXATTACHMENT_H_ -#define SPINE_BOUNDINGBOXATTACHMENT_H_ +#ifndef Spine_BoundingBoxAttachment_h +#define Spine_BoundingBoxAttachment_h -#include -#include #include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spBoundingBoxAttachment { - spVertexAttachment super; -} spBoundingBoxAttachment; - -SP_API spBoundingBoxAttachment* spBoundingBoxAttachment_create (const char* name); - -#ifdef SPINE_SHORT_NAMES -typedef spBoundingBoxAttachment BoundingBoxAttachment; -#define BoundingBoxAttachment_create(...) spBoundingBoxAttachment_create(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#include + +namespace spine { + /// Attachment that has a polygon for bounds checking. + class SP_API BoundingBoxAttachment : public VertexAttachment { + RTTI_DECL + + explicit BoundingBoxAttachment(const String& name); + }; } -#endif -#endif /* SPINE_BOUNDINGBOXATTACHMENT_H_ */ +#endif /* Spine_BoundingBoxAttachment_h */ diff --git a/cocos/editor-support/spine/CMakeLists.txt b/cocos/editor-support/spine/CMakeLists.txt index 0ccd0b188ad3..90e56f073d94 100644 --- a/cocos/editor-support/spine/CMakeLists.txt +++ b/cocos/editor-support/spine/CMakeLists.txt @@ -1,100 +1,162 @@ + +include_directories(editor-support) + set(COCOS_SPINE_HEADER editor-support/spine/Animation.h - editor-support/spine/EventData.h - editor-support/spine/SkeletonAnimation.h - editor-support/spine/SlotData.h - editor-support/spine/SkeletonClipping.h - editor-support/spine/PathAttachment.h - editor-support/spine/PointAttachment.h - editor-support/spine/Event.h - editor-support/spine/Bone.h + editor-support/spine/AnimationState.h + editor-support/spine/AnimationStateData.h editor-support/spine/Atlas.h - editor-support/spine/VertexAttachment.h - editor-support/spine/SkeletonRenderer.h - editor-support/spine/spine.h + editor-support/spine/AtlasAttachmentLoader.h + editor-support/spine/Attachment.h + editor-support/spine/AttachmentLoader.h + editor-support/spine/AttachmentTimeline.h + editor-support/spine/AttachmentType.h + editor-support/spine/AttachmentVertices.h + editor-support/spine/BlendMode.h + editor-support/spine/Bone.h + editor-support/spine/BoneData.h + editor-support/spine/BoundingBoxAttachment.h editor-support/spine/ClippingAttachment.h - editor-support/spine/spine-cocos2dx.h editor-support/spine/Color.h - editor-support/spine/Triangulator.h - editor-support/spine/RegionAttachment.h - editor-support/spine/Attachment.h - editor-support/spine/TransformConstraint.h + editor-support/spine/ColorTimeline.h + editor-support/spine/Constraint.h + editor-support/spine/ContainerUtil.h + editor-support/spine/CurveTimeline.h + editor-support/spine/Debug.h + editor-support/spine/DeformTimeline.h editor-support/spine/dll.h - editor-support/spine/SkeletonJson.h + editor-support/spine/DrawOrderTimeline.h + editor-support/spine/Event.h + editor-support/spine/EventData.h + editor-support/spine/EventTimeline.h + editor-support/spine/Extension.h + editor-support/spine/HashMap.h + editor-support/spine/HasRendererObject.h + editor-support/spine/IkConstraint.h editor-support/spine/IkConstraintData.h - editor-support/spine/AnimationStateData.h - editor-support/spine/kvec.h - editor-support/spine/Skeleton.h + editor-support/spine/IkConstraintTimeline.h editor-support/spine/Json.h - editor-support/spine/AttachmentLoader.h - editor-support/spine/Skin.h - editor-support/spine/VertexEffect.h - editor-support/spine/SkeletonBinary.h - editor-support/spine/SkeletonData.h - editor-support/spine/Array.h + editor-support/spine/LinkedMesh.h + editor-support/spine/MathUtil.h + editor-support/spine/MeshAttachment.h + editor-support/spine/MixBlend.h + editor-support/spine/MixDirection.h + editor-support/spine/PathAttachment.h + editor-support/spine/PathConstraint.h editor-support/spine/PathConstraintData.h + editor-support/spine/PathConstraintMixTimeline.h + editor-support/spine/PathConstraintPositionTimeline.h + editor-support/spine/PathConstraintSpacingTimeline.h + editor-support/spine/PointAttachment.h + editor-support/spine/Pool.h + editor-support/spine/PositionMode.h + editor-support/spine/RegionAttachment.h + editor-support/spine/RotateMode.h + editor-support/spine/RotateTimeline.h + editor-support/spine/RTTI.h + editor-support/spine/ScaleTimeline.h + editor-support/spine/ShearTimeline.h + editor-support/spine/Skeleton.h + editor-support/spine/SkeletonAnimation.h editor-support/spine/SkeletonBatch.h - editor-support/spine/TransformConstraintData.h - editor-support/spine/Cocos2dAttachmentLoader.h - editor-support/spine/extension.h - editor-support/spine/PathConstraint.h - editor-support/spine/IkConstraint.h - editor-support/spine/BoundingBoxAttachment.h - editor-support/spine/AttachmentVertices.h - editor-support/spine/SkeletonTwoColorBatch.h + editor-support/spine/SkeletonBinary.h editor-support/spine/SkeletonBounds.h + editor-support/spine/SkeletonClipping.h + editor-support/spine/SkeletonData.h + editor-support/spine/SkeletonJson.h + editor-support/spine/SkeletonRenderer.h + editor-support/spine/SkeletonTwoColorBatch.h + editor-support/spine/Skin.h editor-support/spine/Slot.h - editor-support/spine/BoneData.h - editor-support/spine/AnimationState.h - editor-support/spine/MeshAttachment.h - editor-support/spine/AtlasAttachmentLoader.h + editor-support/spine/SlotData.h + editor-support/spine/SpacingMode.h + editor-support/spine/spine-cocos2dx.h + editor-support/spine/spine.h + editor-support/spine/SpineObject.h + editor-support/spine/SpineString.h + editor-support/spine/TextureLoader.h + editor-support/spine/Timeline.h + editor-support/spine/TimelineType.h + editor-support/spine/TransformConstraint.h + editor-support/spine/TransformConstraintData.h + editor-support/spine/TransformConstraintTimeline.h + editor-support/spine/TransformMode.h + editor-support/spine/TranslateTimeline.h + editor-support/spine/Triangulator.h + editor-support/spine/TwoColorTimeline.h + editor-support/spine/Updatable.h + editor-support/spine/Vector.h + editor-support/spine/VertexAttachment.h + editor-support/spine/VertexEffect.h + editor-support/spine/Vertices.h ) set(COCOS_SPINE_SRC - editor-support/spine/Animation.c - editor-support/spine/AnimationState.c - editor-support/spine/AnimationStateData.c - editor-support/spine/Array.c - editor-support/spine/Atlas.c - editor-support/spine/AtlasAttachmentLoader.c - editor-support/spine/Attachment.c - editor-support/spine/AttachmentLoader.c + editor-support/spine/Animation.cpp + editor-support/spine/AnimationState.cpp + editor-support/spine/AnimationStateData.cpp + editor-support/spine/Atlas.cpp + editor-support/spine/AtlasAttachmentLoader.cpp + editor-support/spine/Attachment.cpp + editor-support/spine/AttachmentLoader.cpp + editor-support/spine/AttachmentTimeline.cpp editor-support/spine/AttachmentVertices.cpp - editor-support/spine/Bone.c - editor-support/spine/BoneData.c - editor-support/spine/BoundingBoxAttachment.c - editor-support/spine/ClippingAttachment.c - editor-support/spine/Cocos2dAttachmentLoader.cpp - editor-support/spine/Color.c - editor-support/spine/Event.c - editor-support/spine/EventData.c - editor-support/spine/IkConstraint.c - editor-support/spine/IkConstraintData.c - editor-support/spine/Json.c - editor-support/spine/MeshAttachment.c - editor-support/spine/PathAttachment.c - editor-support/spine/PathConstraint.c - editor-support/spine/PathConstraintData.c - editor-support/spine/PointAttachment.c - editor-support/spine/RegionAttachment.c - editor-support/spine/Skeleton.c + editor-support/spine/Bone.cpp + editor-support/spine/BoneData.cpp + editor-support/spine/BoundingBoxAttachment.cpp + editor-support/spine/ClippingAttachment.cpp + editor-support/spine/ColorTimeline.cpp + editor-support/spine/Constraint.cpp + editor-support/spine/CurveTimeline.cpp + editor-support/spine/DeformTimeline.cpp + editor-support/spine/DrawOrderTimeline.cpp + editor-support/spine/Event.cpp + editor-support/spine/EventData.cpp + editor-support/spine/EventTimeline.cpp + editor-support/spine/Extension.cpp + editor-support/spine/IkConstraint.cpp + editor-support/spine/IkConstraintData.cpp + editor-support/spine/IkConstraintTimeline.cpp + editor-support/spine/Json.cpp + editor-support/spine/LinkedMesh.cpp + editor-support/spine/MathUtil.cpp + editor-support/spine/MeshAttachment.cpp + editor-support/spine/PathAttachment.cpp + editor-support/spine/PathConstraint.cpp + editor-support/spine/PathConstraintData.cpp + editor-support/spine/PathConstraintMixTimeline.cpp + editor-support/spine/PathConstraintPositionTimeline.cpp + editor-support/spine/PathConstraintSpacingTimeline.cpp + editor-support/spine/PointAttachment.cpp + editor-support/spine/RegionAttachment.cpp + editor-support/spine/RotateTimeline.cpp + editor-support/spine/RTTI.cpp + editor-support/spine/ScaleTimeline.cpp + editor-support/spine/ShearTimeline.cpp + editor-support/spine/Skeleton.cpp editor-support/spine/SkeletonAnimation.cpp editor-support/spine/SkeletonBatch.cpp - editor-support/spine/SkeletonBinary.c - editor-support/spine/SkeletonBounds.c - editor-support/spine/SkeletonClipping.c - editor-support/spine/SkeletonData.c - editor-support/spine/SkeletonJson.c + editor-support/spine/SkeletonBinary.cpp + editor-support/spine/SkeletonBounds.cpp + editor-support/spine/SkeletonClipping.cpp + editor-support/spine/SkeletonData.cpp + editor-support/spine/SkeletonJson.cpp editor-support/spine/SkeletonRenderer.cpp editor-support/spine/SkeletonTwoColorBatch.cpp - editor-support/spine/Skin.c - editor-support/spine/Slot.c - editor-support/spine/SlotData.c - editor-support/spine/TransformConstraint.c - editor-support/spine/TransformConstraintData.c - editor-support/spine/Triangulator.c - editor-support/spine/VertexAttachment.c - editor-support/spine/VertexEffect.c - editor-support/spine/extension.c + editor-support/spine/Skin.cpp + editor-support/spine/Slot.cpp + editor-support/spine/SlotData.cpp editor-support/spine/spine-cocos2dx.cpp + editor-support/spine/SpineObject.cpp + editor-support/spine/TextureLoader.cpp + editor-support/spine/Timeline.cpp + editor-support/spine/TransformConstraint.cpp + editor-support/spine/TransformConstraintData.cpp + editor-support/spine/TransformConstraintTimeline.cpp + editor-support/spine/TranslateTimeline.cpp + editor-support/spine/Triangulator.cpp + editor-support/spine/TwoColorTimeline.cpp + editor-support/spine/Updatable.cpp + editor-support/spine/VertexAttachment.cpp + editor-support/spine/VertexEffect.cpp ) diff --git a/cocos/editor-support/spine/ClippingAttachment.cpp b/cocos/editor-support/spine/ClippingAttachment.cpp new file mode 100644 index 000000000000..6857f40e1ad8 --- /dev/null +++ b/cocos/editor-support/spine/ClippingAttachment.cpp @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +using namespace spine; + +RTTI_IMPL(ClippingAttachment, VertexAttachment) + +ClippingAttachment::ClippingAttachment(const String &name) : VertexAttachment(name), _endSlot(NULL) { +} + +SlotData *ClippingAttachment::getEndSlot() { + return _endSlot; +} + +void ClippingAttachment::setEndSlot(SlotData *inValue) { + _endSlot = inValue; +} diff --git a/cocos/editor-support/spine/ClippingAttachment.h b/cocos/editor-support/spine/ClippingAttachment.h index 134334d6d727..7eceae5eb570 100644 --- a/cocos/editor-support/spine/ClippingAttachment.h +++ b/cocos/editor-support/spine/ClippingAttachment.h @@ -1,61 +1,57 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_CLIPPINGATTACHMENT_H_ -#define SPINE_CLIPPINGATTACHMENT_H_ +#ifndef Spine_ClippingAttachment_h +#define Spine_ClippingAttachment_h -#include -#include #include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spClippingAttachment { - spVertexAttachment super; - spSlotData* endSlot; -} spClippingAttachment; - -SP_API void _spClippingAttachment_dispose(spAttachment* self); -SP_API spClippingAttachment* spClippingAttachment_create (const char* name); - -#ifdef SPINE_SHORT_NAMES -typedef spClippingAttachment ClippingAttachment; -#define ClippingAttachment_create(...) spClippingAttachment_create(__VA_ARGS__) -#endif -#ifdef __cplusplus +namespace spine { + class SlotData; + + class SP_API ClippingAttachment : public VertexAttachment { + friend class SkeletonBinary; + friend class SkeletonJson; + + friend class SkeletonClipping; + + RTTI_DECL + + public: + explicit ClippingAttachment(const String& name); + + SlotData* getEndSlot(); + void setEndSlot(SlotData* inValue); + + private: + SlotData* _endSlot; + }; } -#endif -#endif /* SPINE_CLIPPINGATTACHMENT_H_ */ +#endif /* Spine_ClippingAttachment_h */ diff --git a/cocos/editor-support/spine/Color.h b/cocos/editor-support/spine/Color.h index 272ba95524b6..b53a79418027 100644 --- a/cocos/editor-support/spine/Color.h +++ b/cocos/editor-support/spine/Color.h @@ -1,78 +1,94 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_COLOR_H_ -#define SPINE_COLOR_H_ +#ifndef SPINE_COLOR_H +#define SPINE_COLOR_H -#include +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace spine { +class SP_API Color : public SpineObject { +public: + Color() : r(0), g(0), b(0), a(0) { + } -typedef struct spColor { - float r, g, b, a; + Color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) { + clamp(); + } + + inline Color &set(float _r, float _g, float _b, float _a) { + this->r = _r; + this->g = _g; + this->b = _b; + this->a = _a; + clamp(); + return *this; + } -#ifdef __cplusplus - spColor() : - r(0), g(0), b(0), a(0) { + inline Color &set(const Color &other) { + r = other.r; + g = other.g; + b = other.b; + a = other.a; + clamp(); + return *this; } - bool operator==(const spColor& rhs) { - return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a; + inline Color &add(float _r, float _g, float _b, float _a) { + this->r += _r; + this->g += _g; + this->b += _b; + this->a += _a; + clamp(); + return *this; } -#endif -} spColor; -/* @param attachmentName May be 0 for no setup pose attachment. */ -SP_API spColor* spColor_create(); -SP_API void spColor_dispose(spColor* self); -SP_API void spColor_setFromFloats(spColor* color, float r, float g, float b, float a); -SP_API void spColor_setFromColor(spColor* color, spColor* otherColor); -SP_API void spColor_addFloats(spColor* color, float r, float g, float b, float a); -SP_API void spColor_addColor(spColor* color, spColor* otherColor); -SP_API void spColor_clamp(spColor* color); + inline Color &add(const Color &other) { + r += other.r; + g += other.g; + b += other.b; + a += other.a; + clamp(); + return *this; + } -#ifdef SPINE_SHORT_NAMES -typedef spColor color; -#define Color_create() spColor_create() -#define Color_dispose(...) spColor_dispose(__VA_ARGS__) -#define Color_setFromFloats(...) spColor_setFromFloats(__VA_ARGS__) -#define Color_setFromColor(...) spColor_setFromColor(__VA_ARGS__) -#define Color_addColor(...) spColor_addColor(__VA_ARGS__) -#define Color_addFloats(...) spColor_addFloats(__VA_ARGS__) -#define Color_clamp(...) spColor_clamp(__VA_ARGS__) -#endif + inline Color &clamp() { + r = MathUtil::clamp(this->r, 0, 1); + g = MathUtil::clamp(this->g, 0, 1); + b = MathUtil::clamp(this->b, 0, 1); + a = MathUtil::clamp(this->a, 0, 1); + return *this; + } -#ifdef __cplusplus + float r, g, b, a; +}; } -#endif -#endif /* SPINE_COLOR_H_ */ + +#endif //SPINE_COLOR_H diff --git a/cocos/editor-support/spine/ColorTimeline.cpp b/cocos/editor-support/spine/ColorTimeline.cpp new file mode 100644 index 000000000000..a764cf5601c7 --- /dev/null +++ b/cocos/editor-support/spine/ColorTimeline.cpp @@ -0,0 +1,143 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(ColorTimeline, CurveTimeline) + +const int ColorTimeline::ENTRIES = 5; +const int ColorTimeline::PREV_TIME = -5; +const int ColorTimeline::PREV_R = -4; +const int ColorTimeline::PREV_G = -3; +const int ColorTimeline::PREV_B = -2; +const int ColorTimeline::PREV_A = -1; +const int ColorTimeline::R = 1; +const int ColorTimeline::G = 2; +const int ColorTimeline::B = 3; +const int ColorTimeline::A = 4; + +ColorTimeline::ColorTimeline(int frameCount) : CurveTimeline(frameCount), _slotIndex(0) { + _frames.setSize(frameCount * ENTRIES, 0); +} + +void ColorTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + Slot *slotP = skeleton._slots[_slotIndex]; + Slot &slot = *slotP; + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + slot._color.set(slot._data._color); + return; + case MixBlend_First: { + Color &color = slot._color, setup = slot._data._color; + color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, + (setup.a - color.a) * alpha); + } + default: ; + } + return; + } + + float r, g, b, a; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + size_t i = _frames.size(); + r = _frames[i + PREV_R]; + g = _frames[i + PREV_G]; + b = _frames[i + PREV_B]; + a = _frames[i + PREV_A]; + } else { + // Interpolate between the previous frame and the current frame. + size_t frame = (size_t)Animation::binarySearch(_frames, time, ENTRIES); + r = _frames[frame + PREV_R]; + g = _frames[frame + PREV_G]; + b = _frames[frame + PREV_B]; + a = _frames[frame + PREV_A]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + r += (_frames[frame + R] - r) * percent; + g += (_frames[frame + G] - g) * percent; + b += (_frames[frame + B] - b) * percent; + a += (_frames[frame + A] - a) * percent; + } + + if (alpha == 1) { + slot.getColor().set(r, g, b, a); + } else { + Color &color = slot.getColor(); + if (blend == MixBlend_Setup) color.set(slot.getData().getColor()); + color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); + } +} + +int ColorTimeline::getPropertyId() { + return ((int) TimelineType_Color << 24) + _slotIndex; +} + +void ColorTimeline::setFrame(int frameIndex, float time, float r, float g, float b, float a) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + R] = r; + _frames[frameIndex + G] = g; + _frames[frameIndex + B] = b; + _frames[frameIndex + A] = a; +} + +int ColorTimeline::getSlotIndex() { + return _slotIndex; +} + +void ColorTimeline::setSlotIndex(int inValue) { + _slotIndex = inValue; +} + +Vector &ColorTimeline::getFrames() { + return _frames; +} diff --git a/cocos/editor-support/spine/ColorTimeline.h b/cocos/editor-support/spine/ColorTimeline.h new file mode 100644 index 000000000000..e9b580bf61dd --- /dev/null +++ b/cocos/editor-support/spine/ColorTimeline.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_ColorTimeline_h +#define Spine_ColorTimeline_h + +#include + +namespace spine { +class SP_API ColorTimeline : public CurveTimeline { + friend class SkeletonBinary; + + friend class SkeletonJson; + +RTTI_DECL + +public: + static const int ENTRIES; + + explicit ColorTimeline(int frameCount); + + virtual void + apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, MixBlend blend, + MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float r, float g, float b, float a); + + int getSlotIndex(); + + void setSlotIndex(int inValue); + + Vector &getFrames(); + +protected: + static const int PREV_TIME; + static const int PREV_R; + static const int PREV_G; + static const int PREV_B; + static const int PREV_A; + static const int R; + static const int G; + static const int B; + static const int A; + +private: + int _slotIndex; + Vector _frames; +}; +} + +#endif /* Spine_ColorTimeline_h */ diff --git a/cocos/editor-support/spine/Constraint.cpp b/cocos/editor-support/spine/Constraint.cpp new file mode 100644 index 000000000000..8a71331b3c5a --- /dev/null +++ b/cocos/editor-support/spine/Constraint.cpp @@ -0,0 +1,44 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +using namespace spine; + +RTTI_IMPL(Constraint, Updatable) + +Constraint::Constraint() { +} + +Constraint::~Constraint() { +} diff --git a/cocos/editor-support/spine/Constraint.h b/cocos/editor-support/spine/Constraint.h new file mode 100644 index 000000000000..29b06f644bea --- /dev/null +++ b/cocos/editor-support/spine/Constraint.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Constraint_h +#define Spine_Constraint_h + +#include + +namespace spine { +/// The interface for all constraints. +class SP_API Constraint : public Updatable { +RTTI_DECL + +public: + Constraint(); + + virtual ~Constraint(); + + virtual void update() = 0; + + /// The ordinal for the order a skeleton's constraints will be applied. + virtual int getOrder() = 0; +}; +} + +#endif /* Spine_Constraint_h */ diff --git a/cocos/editor-support/spine/ContainerUtil.h b/cocos/editor-support/spine/ContainerUtil.h new file mode 100644 index 000000000000..015e970e22be --- /dev/null +++ b/cocos/editor-support/spine/ContainerUtil.h @@ -0,0 +1,127 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_ContainerUtil_h +#define Spine_ContainerUtil_h + +#include +#include +#include +#include +#include + +#include + +namespace spine { + class SP_API ContainerUtil : public SpineObject { + public: + /// Finds an item by comparing each item's name. + /// It is more efficient to cache the results of this method than to call it multiple times. + /// @return May be NULL. + template + static T* findWithName(Vector& items, const String& name) { + assert(name.length() > 0); + + for (size_t i = 0; i < items.size(); ++i) { + T* item = items[i]; + if (item->getName() == name) { + return item; + } + } + + return NULL; + } + + /// @return -1 if the item was not found. + template + static int findIndexWithName(Vector& items, const String& name) { + assert(name.length() > 0); + + for (size_t i = 0, len = items.size(); i < len; ++i) { + T* item = items[i]; + if (item->getName() == name) { + return static_cast(i); + } + } + + return -1; + } + + /// Finds an item by comparing each item's name. + /// It is more efficient to cache the results of this method than to call it multiple times. + /// @return May be NULL. + template + static T* findWithDataName(Vector& items, const String& name) { + assert(name.length() > 0); + + for (size_t i = 0; i < items.size(); ++i) { + T* item = items[i]; + if (item->getData().getName() == name) { + return item; + } + } + + return NULL; + } + + /// @return -1 if the item was not found. + template + static int findIndexWithDataName(Vector& items, const String& name) { + assert(name.length() > 0); + + for (size_t i = 0, len = items.size(); i < len; ++i) { + T* item = items[i]; + if (item->getData().getName() == name) { + return static_cast(i); + } + } + + return -1; + } + + template + static void cleanUpVectorOfPointers(Vector& items) { + for (int i = (int)items.size() - 1; i >= 0; i--) { + T* item = items[i]; + + delete item; + + items.removeAt(i); + } + } + + private: + // ctor, copy ctor, and assignment should be private in a Singleton + ContainerUtil(); + ContainerUtil(const ContainerUtil&); + ContainerUtil& operator=(const ContainerUtil&); + }; +} + +#endif /* Spine_ContainerUtil_h */ diff --git a/cocos/editor-support/spine/CurveTimeline.cpp b/cocos/editor-support/spine/CurveTimeline.cpp new file mode 100644 index 000000000000..57f45f0aceb2 --- /dev/null +++ b/cocos/editor-support/spine/CurveTimeline.cpp @@ -0,0 +1,128 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +using namespace spine; + +RTTI_IMPL(CurveTimeline, Timeline) + +const float CurveTimeline::LINEAR = 0; +const float CurveTimeline::STEPPED = 1; +const float CurveTimeline::BEZIER = 2; +const int CurveTimeline::BEZIER_SIZE = 10 * 2 - 1; + +CurveTimeline::CurveTimeline(int frameCount) { + assert(frameCount > 0); + + _curves.setSize((frameCount - 1) * BEZIER_SIZE, 0); +} + +CurveTimeline::~CurveTimeline() { +} + +size_t CurveTimeline::getFrameCount() { + return _curves.size() / BEZIER_SIZE + 1; +} + +void CurveTimeline::setLinear(size_t frameIndex) { + _curves[frameIndex * BEZIER_SIZE] = LINEAR; +} + +void CurveTimeline::setStepped(size_t frameIndex) { + _curves[frameIndex * BEZIER_SIZE] = STEPPED; +} + +void CurveTimeline::setCurve(size_t frameIndex, float cx1, float cy1, float cx2, float cy2) { + float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f; + float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f; + float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy; + float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f; + + size_t i = frameIndex * BEZIER_SIZE; + _curves[i++] = BEZIER; + + float x = dfx, y = dfy; + for (size_t n = i + BEZIER_SIZE - 1; i < n; i += 2) { + _curves[i] = x; + _curves[i + 1] = y; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } +} + +float CurveTimeline::getCurvePercent(size_t frameIndex, float percent) { + percent = MathUtil::clamp(percent, 0, 1); + size_t i = frameIndex * BEZIER_SIZE; + float type = _curves[i]; + + if (type == LINEAR) { + return percent; + } + + if (type == STEPPED) { + return 0; + } + + i++; + float x = 0; + for (size_t start = i, n = i + BEZIER_SIZE - 1; i < n; i += 2) { + x = _curves[i]; + if (x >= percent) { + float prevX, prevY; + if (i == start) { + prevX = 0; + prevY = 0; + } else { + prevX = _curves[i - 2]; + prevY = _curves[i - 1]; + } + + return prevY + (_curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + } + } + + float y = _curves[i - 1]; + + return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. +} + +float CurveTimeline::getCurveType(size_t frameIndex) { + return _curves[frameIndex * BEZIER_SIZE]; +} diff --git a/cocos/editor-support/spine/CurveTimeline.h b/cocos/editor-support/spine/CurveTimeline.h new file mode 100644 index 000000000000..0e2f32d5e5ff --- /dev/null +++ b/cocos/editor-support/spine/CurveTimeline.h @@ -0,0 +1,76 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_CurveTimeline_h +#define Spine_CurveTimeline_h + +#include +#include + +namespace spine { + /// Base class for frames that use an interpolation bezier curve. + class SP_API CurveTimeline : public Timeline { + RTTI_DECL + + public: + explicit CurveTimeline(int frameCount); + + virtual ~CurveTimeline(); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction) = 0; + + virtual int getPropertyId() = 0; + + size_t getFrameCount(); + + void setLinear(size_t frameIndex); + + void setStepped(size_t frameIndex); + + /// Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + /// cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + /// the difference between the keyframe's values. + void setCurve(size_t frameIndex, float cx1, float cy1, float cx2, float cy2); + + float getCurvePercent(size_t frameIndex, float percent); + + float getCurveType(size_t frameIndex); + + protected: + static const float LINEAR; + static const float STEPPED; + static const float BEZIER; + static const int BEZIER_SIZE; + + private: + Vector _curves; // type, x, y, ... + }; +} + +#endif /* Spine_CurveTimeline_h */ diff --git a/cocos/editor-support/spine/Debug.h b/cocos/editor-support/spine/Debug.h new file mode 100644 index 000000000000..13c3213ec57b --- /dev/null +++ b/cocos/editor-support/spine/Debug.h @@ -0,0 +1,116 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef SPINE_DEBUG_H +#define SPINE_DEBUG_H + +#include + +#include + +namespace spine { +class SP_API DebugExtension : public SpineExtension { + struct Allocation { + void *address; + size_t size; + const char *fileName; + int line; + + Allocation() : address(NULL), size(0), fileName(NULL), line(0) { + } + + Allocation(void *a, size_t s, const char *f, int l) : address(a), size(s), fileName(f), line(l) { + } + }; + +public: + DebugExtension(SpineExtension* extension): _extension(extension), _allocations(0), _reallocations(0), _frees(0) { + } + + void reportLeaks() { + for (std::map::iterator it = _allocated.begin(); it != _allocated.end(); it++) { + printf("\"%s:%i (%zu bytes at %p)\n", it->second.fileName, it->second.line, it->second.size, it->second.address); + } + printf("allocations: %zu, reallocations: %zu, frees: %zu\n", _allocations, _reallocations, _frees); + if (_allocated.empty()) printf("No leaks detected"); + } + + void clearAllocations() { + _allocated.clear(); + } + + virtual void *_alloc(size_t size, const char *file, int line) { + void *result = _extension->_alloc(size, file, line); + _allocated[result] = Allocation(result, size, file, line); + _allocations++; + return result; + } + + virtual void *_calloc(size_t size, const char *file, int line) { + void *result = _extension->_calloc(size, file, line); + _allocated[result] = Allocation(result, size, file, line); + _allocations++; + return result; + } + + virtual void *_realloc(void *ptr, size_t size, const char *file, int line) { + _allocated.erase(ptr); + void *result = _extension->_realloc(ptr, size, file, line); + _reallocations++; + _allocated[result] = Allocation(result, size, file, line); + return result; + } + + virtual void _free(void *mem, const char *file, int line) { + if (_allocated.count(mem)) { + _extension->_free(mem, file, line); + _frees++; + _allocated.erase(mem); + return; + } + + printf("%s:%i (address %p): Double free or not allocated through SpineExtension\n", file, line, mem); + _extension->_free(mem, file, line); + } + + virtual char *_readFile(const String &path, int *length) { + return _extension->_readFile(path, length); + } + +private: + SpineExtension* _extension; + std::map _allocated; + size_t _allocations; + size_t _reallocations; + size_t _frees; +}; +} + + +#endif //SPINE_DEBUG_H diff --git a/cocos/editor-support/spine/DeformTimeline.cpp b/cocos/editor-support/spine/DeformTimeline.cpp new file mode 100644 index 000000000000..6683cd8dfeea --- /dev/null +++ b/cocos/editor-support/spine/DeformTimeline.cpp @@ -0,0 +1,296 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(DeformTimeline, CurveTimeline) + +DeformTimeline::DeformTimeline(int frameCount) : CurveTimeline(frameCount), _slotIndex(0), _attachment(NULL) { + _frames.ensureCapacity(frameCount); + _frameVertices.ensureCapacity(frameCount); + + _frames.setSize(frameCount, 0); + + for (int i = 0; i < frameCount; ++i) { + Vector vec; + _frameVertices.add(vec); + } +} + +void DeformTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + Slot *slotP = skeleton._slots[_slotIndex]; + Slot &slot = *slotP; + + Attachment *slotAttachment = slot.getAttachment(); + if (slotAttachment == NULL || !slotAttachment->getRTTI().instanceOf(VertexAttachment::rtti)) { + return; + } + + VertexAttachment *attachment = static_cast(slotAttachment); + if (!attachment->applyDeform(_attachment)) { + return; + } + + Vector &verticesArray = slot._attachmentVertices; + if (verticesArray.size() == 0) { + blend = MixBlend_Setup; + } + + Vector< Vector > &frameVertices = _frameVertices; + size_t vertexCount = frameVertices[0].size(); + + Vector &frames = _frames; + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + verticesArray.clear(); + return; + case MixBlend_First: { + if (alpha == 1) { + verticesArray.clear(); + return; + } + verticesArray.setSize(vertexCount, 0); + Vector &vertices = verticesArray; + if (attachment->getBones().size() == 0) { + // Unweighted vertex positions. + Vector &setupVertices = attachment->getVertices(); + for (size_t i = 0; i < vertexCount; i++) + vertices[i] += (setupVertices[i] - vertices[i]) * alpha; + } else { + // Weighted deform offsets. + alpha = 1 - alpha; + for (size_t i = 0; i < vertexCount; i++) + vertices[i] *= alpha; + } + } + case MixBlend_Replace: + case MixBlend_Add: + return; + } + } + + verticesArray.setSize(vertexCount, 0); + Vector &vertices = verticesArray; + + if (time >= frames[frames.size() - 1]) { // Time is after last frame. + Vector &lastVertices = frameVertices[frames.size() - 1]; + if (alpha == 1) { + if (blend == MixBlend_Add) { + VertexAttachment *vertexAttachment = static_cast(slotAttachment); + if (vertexAttachment->getBones().size() == 0) { + // Unweighted vertex positions, no alpha. + Vector &setupVertices = vertexAttachment->getVertices(); + for (size_t i = 0; i < vertexCount; i++) + vertices[i] += lastVertices[i] - setupVertices[i]; + } else { + // Weighted deform offsets, no alpha. + for (size_t i = 0; i < vertexCount; i++) + vertices[i] += lastVertices[i]; + } + } else { + // Vertex positions or deform offsets, no alpha. + memcpy(vertices.buffer(), lastVertices.buffer(), vertexCount * sizeof(float)); + } + } else { + switch (blend) { + case MixBlend_Setup: { + VertexAttachment *vertexAttachment = static_cast(slotAttachment); + if (vertexAttachment->getBones().size() == 0) { + // Unweighted vertex positions, with alpha. + Vector &setupVertices = vertexAttachment->getVertices(); + for (size_t i = 0; i < vertexCount; i++) { + float setup = setupVertices[i]; + vertices[i] = setup + (lastVertices[i] - setup) * alpha; + } + } else { + // Weighted deform offsets, with alpha. + for (size_t i = 0; i < vertexCount; i++) + vertices[i] = lastVertices[i] * alpha; + } + break; + } + case MixBlend_First: + case MixBlend_Replace: + // Vertex positions or deform offsets, with alpha. + for (size_t i = 0; i < vertexCount; i++) + vertices[i] += (lastVertices[i] - vertices[i]) * alpha; + break; + case MixBlend_Add: + VertexAttachment *vertexAttachment = static_cast(slotAttachment); + if (vertexAttachment->getBones().size() == 0) { + // Unweighted vertex positions, no alpha. + Vector &setupVertices = vertexAttachment->getVertices(); + for (size_t i = 0; i < vertexCount; i++) + vertices[i] += (lastVertices[i] - setupVertices[i]) * alpha; + } else { + // Weighted deform offsets, alpha. + for (size_t i = 0; i < vertexCount; i++) + vertices[i] += lastVertices[i] * alpha; + } + } + } + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(frames, time); + Vector &prevVertices = frameVertices[frame - 1]; + Vector &nextVertices = frameVertices[frame]; + float frameTime = frames[frame]; + float percent = getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); + + if (alpha == 1) { + if (blend == MixBlend_Add) { + VertexAttachment *vertexAttachment = static_cast(slotAttachment); + if (vertexAttachment->getBones().size() == 0) { + // Unweighted vertex positions, no alpha. + Vector &setupVertices = vertexAttachment->getVertices(); + for (size_t i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i]; + } + } else { + // Weighted deform offsets, no alpha. + for (size_t i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] += prev + (nextVertices[i] - prev) * percent; + } + } + } else { + // Vertex positions or deform offsets, no alpha. + for (size_t i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] = prev + (nextVertices[i] - prev) * percent; + } + } + } else { + switch (blend) { + case MixBlend_Setup: { + VertexAttachment *vertexAttachment = static_cast(slotAttachment); + if (vertexAttachment->getBones().size() == 0) { + // Unweighted vertex positions, with alpha. + Vector &setupVertices = vertexAttachment->getVertices(); + for (size_t i = 0; i < vertexCount; i++) { + float prev = prevVertices[i], setup = setupVertices[i]; + vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha; + } + } else { + // Weighted deform offsets, with alpha. + for (size_t i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha; + } + } + break; + } + case MixBlend_First: + case MixBlend_Replace: + // Vertex positions or deform offsets, with alpha. + for (size_t i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; + } + break; + case MixBlend_Add: + VertexAttachment *vertexAttachment = static_cast(slotAttachment); + if (vertexAttachment->getBones().size() == 0) { + // Unweighted vertex positions, with alpha. + Vector &setupVertices = vertexAttachment->getVertices(); + for (size_t i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha; + } + } else { + // Weighted deform offsets, with alpha. + for (size_t i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent) * alpha; + } + } + } + } +} + +int DeformTimeline::getPropertyId() { + assert(_attachment != NULL); + + return ((int) TimelineType_Deform << 24) + _attachment->_id + _slotIndex; +} + +void DeformTimeline::setFrame(int frameIndex, float time, Vector &vertices) { + _frames[frameIndex] = time; + _frameVertices[frameIndex].clear(); + _frameVertices[frameIndex].addAll(vertices); +} + +int DeformTimeline::getSlotIndex() { + return _slotIndex; +} + +void DeformTimeline::setSlotIndex(int inValue) { + _slotIndex = inValue; +} + +Vector &DeformTimeline::getFrames() { + return _frames; +} + +Vector > &DeformTimeline::getVertices() { + return _frameVertices; +} + +VertexAttachment *DeformTimeline::getAttachment() { + return _attachment; +} + +void DeformTimeline::setAttachment(VertexAttachment *inValue) { + _attachment = inValue; +} diff --git a/cocos/editor-support/spine/DeformTimeline.h b/cocos/editor-support/spine/DeformTimeline.h new file mode 100644 index 000000000000..12dd03f66fe6 --- /dev/null +++ b/cocos/editor-support/spine/DeformTimeline.h @@ -0,0 +1,69 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_DeformTimeline_h +#define Spine_DeformTimeline_h + +#include + +namespace spine { + class VertexAttachment; + + class SP_API DeformTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + explicit DeformTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, Vector& vertices); + + int getSlotIndex(); + void setSlotIndex(int inValue); + Vector& getFrames(); + Vector< Vector >& getVertices(); + VertexAttachment* getAttachment(); + void setAttachment(VertexAttachment* inValue); + + private: + int _slotIndex; + Vector _frames; + Vector< Vector > _frameVertices; + VertexAttachment* _attachment; + }; +} + +#endif /* Spine_DeformTimeline_h */ diff --git a/cocos/editor-support/spine/DrawOrderTimeline.cpp b/cocos/editor-support/spine/DrawOrderTimeline.cpp new file mode 100644 index 000000000000..d5f192ed6102 --- /dev/null +++ b/cocos/editor-support/spine/DrawOrderTimeline.cpp @@ -0,0 +1,128 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(DrawOrderTimeline, Timeline) + +DrawOrderTimeline::DrawOrderTimeline(int frameCount) : Timeline() { + _frames.ensureCapacity(frameCount); + _drawOrders.ensureCapacity(frameCount); + + _frames.setSize(frameCount, 0); + + for (int i = 0; i < frameCount; ++i) { + Vector vec; + _drawOrders.add(vec); + } +} + +void DrawOrderTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(alpha); + + Vector &drawOrder = skeleton._drawOrder; + Vector &slots = skeleton._slots; + if (direction == MixDirection_Out && blend == MixBlend_Setup) { + drawOrder.clear(); + drawOrder.ensureCapacity(slots.size()); + for (size_t i = 0, n = slots.size(); i < n; ++i) { + drawOrder.add(slots[i]); + } + return; + } + + if (time < _frames[0]) { + if (blend == MixBlend_Setup || blend == MixBlend_First) { + drawOrder.clear(); + drawOrder.ensureCapacity(slots.size()); + for (size_t i = 0, n = slots.size(); i < n; ++i) { + drawOrder.add(slots[i]); + } + } + return; + } + + size_t frame; + if (time >= _frames[_frames.size() - 1]) { + // Time is after last frame. + frame = _frames.size() - 1; + } else { + frame = (size_t)Animation::binarySearch(_frames, time) - 1; + } + + Vector &drawOrderToSetupIndex = _drawOrders[frame]; + if (drawOrderToSetupIndex.size() == 0) { + drawOrder.clear(); + for (size_t i = 0, n = slots.size(); i < n; ++i) { + drawOrder.add(slots[i]); + } + } else { + for (size_t i = 0, n = drawOrderToSetupIndex.size(); i < n; ++i) { + drawOrder[i] = slots[drawOrderToSetupIndex[i]]; + } + } +} + +int DrawOrderTimeline::getPropertyId() { + return ((int) TimelineType_DrawOrder << 24); +} + +void DrawOrderTimeline::setFrame(size_t frameIndex, float time, Vector &drawOrder) { + _frames[frameIndex] = time; + _drawOrders[frameIndex].clear(); + _drawOrders[frameIndex].addAll(drawOrder); +} + +Vector &DrawOrderTimeline::getFrames() { + return _frames; +} + +Vector > &DrawOrderTimeline::getDrawOrders() { + return _drawOrders; +} + +size_t DrawOrderTimeline::getFrameCount() { + return _frames.size(); +} diff --git a/cocos/editor-support/spine/DrawOrderTimeline.h b/cocos/editor-support/spine/DrawOrderTimeline.h new file mode 100644 index 000000000000..73ff98cb3483 --- /dev/null +++ b/cocos/editor-support/spine/DrawOrderTimeline.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_DrawOrderTimeline_h +#define Spine_DrawOrderTimeline_h + +#include + +namespace spine { + class SP_API DrawOrderTimeline : public Timeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + explicit DrawOrderTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + /// @param drawOrder May be NULL to use bind pose draw order + void setFrame(size_t frameIndex, float time, Vector& drawOrder); + + Vector& getFrames(); + Vector< Vector >& getDrawOrders(); + size_t getFrameCount(); + + private: + Vector _frames; + Vector< Vector > _drawOrders; + }; +} + +#endif /* Spine_DrawOrderTimeline_h */ diff --git a/cocos/editor-support/spine/Event.cpp b/cocos/editor-support/spine/Event.cpp new file mode 100644 index 000000000000..c92573e83ca1 --- /dev/null +++ b/cocos/editor-support/spine/Event.cpp @@ -0,0 +1,95 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +spine::Event::Event(float time, const spine::EventData &data) : + _data(data), + _time(time), + _intValue(0), + _floatValue(0), + _stringValue(), + _volume(1), + _balance(0) { +} + +const spine::EventData &spine::Event::getData() { + return _data; +} + +float spine::Event::getTime() { + return _time; +} + +int spine::Event::getIntValue() { + return _intValue; +} + +void spine::Event::setIntValue(int inValue) { + _intValue = inValue; +} + +float spine::Event::getFloatValue() { + return _floatValue; +} + +void spine::Event::setFloatValue(float inValue) { + _floatValue = inValue; +} + +const spine::String &spine::Event::getStringValue() { + return _stringValue; +} + +void spine::Event::setStringValue(const spine::String &inValue) { + _stringValue = inValue; +} + + +float spine::Event::getVolume() { + return _volume; +} + +void spine::Event::setVolume(float inValue) { + _volume = inValue; +} + +float spine::Event::getBalance() { + return _balance; +} + +void spine::Event::setBalance(float inValue) { + _balance = inValue; +} diff --git a/cocos/editor-support/spine/Event.h b/cocos/editor-support/spine/Event.h index 308fd3953000..75c3763f329f 100644 --- a/cocos/editor-support/spine/Event.h +++ b/cocos/editor-support/spine/Event.h @@ -1,72 +1,86 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_EVENT_H_ -#define SPINE_EVENT_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spEvent { - spEventData* const data; - float const time; - int intValue; - float floatValue; - const char* stringValue; - -#ifdef __cplusplus - spEvent() : - data(0), - time(0), - intValue(0), - floatValue(0), - stringValue(0) { - } -#endif -} spEvent; - -SP_API spEvent* spEvent_create (float time, spEventData* data); -SP_API void spEvent_dispose (spEvent* self); - -#ifdef SPINE_SHORT_NAMES -typedef spEvent Event; -#define Event_create(...) spEvent_create(__VA_ARGS__) -#define Event_dispose(...) spEvent_dispose(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#ifndef Spine_Event_h +#define Spine_Event_h + +#include +#include + +namespace spine { +class EventData; + +/// Stores the current pose values for an Event. +class SP_API Event : public SpineObject { + friend class SkeletonBinary; + + friend class SkeletonJson; + + friend class AnimationState; + +public: + Event(float time, const EventData &data); + + const EventData &getData(); + + /// The animation time this event was keyed. + float getTime(); + + int getIntValue(); + + void setIntValue(int inValue); + + float getFloatValue(); + + void setFloatValue(float inValue); + + const String &getStringValue(); + + void setStringValue(const String &inValue); + + float getVolume(); + + void setVolume(float inValue); + + float getBalance(); + + void setBalance(float inValue); + +private: + const EventData &_data; + const float _time; + int _intValue; + float _floatValue; + String _stringValue; + float _volume; + float _balance; +}; } -#endif -#endif /* SPINE_EVENT_H_ */ +#endif /* Spine_Event_h */ diff --git a/cocos/editor-support/spine/EventData.cpp b/cocos/editor-support/spine/EventData.cpp new file mode 100644 index 000000000000..f0a137b98aa5 --- /dev/null +++ b/cocos/editor-support/spine/EventData.cpp @@ -0,0 +1,101 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +spine::EventData::EventData(const spine::String &name) : + _name(name), + _intValue(0), + _floatValue(0), + _stringValue(), + _audioPath(), + _volume(1), + _balance(0) { + assert(_name.length() > 0); +} + +/// The name of the event, which is unique within the skeleton. +const spine::String &spine::EventData::getName() const { + return _name; +} + +int spine::EventData::getIntValue() { + return _intValue; +} + +void spine::EventData::setIntValue(int inValue) { + _intValue = inValue; +} + +float spine::EventData::getFloatValue() { + return _floatValue; +} + +void spine::EventData::setFloatValue(float inValue) { + _floatValue = inValue; +} + +const spine::String &spine::EventData::getStringValue() { + return _stringValue; +} + +void spine::EventData::setStringValue(const spine::String &inValue) { + this->_stringValue = inValue; +} + +const spine::String &spine::EventData::getAudioPath() { + return _audioPath; +} + +void spine::EventData::setAudioPath(const spine::String &inValue) { + _audioPath = inValue; +} + + +float spine::EventData::getVolume() { + return _volume; +} + +void spine::EventData::setVolume(float inValue) { + _volume = inValue; +} + +float spine::EventData::getBalance() { + return _balance; +} + +void spine::EventData::setBalance(float inValue) { + _balance = inValue; +} diff --git a/cocos/editor-support/spine/EventData.h b/cocos/editor-support/spine/EventData.h index 459d161c9ee0..44ea65757ec7 100644 --- a/cocos/editor-support/spine/EventData.h +++ b/cocos/editor-support/spine/EventData.h @@ -1,69 +1,86 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_EVENTDATA_H_ -#define SPINE_EVENTDATA_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spEventData { - const char* const name; - int intValue; - float floatValue; - const char* stringValue; - -#ifdef __cplusplus - spEventData() : - name(0), - intValue(0), - floatValue(0), - stringValue(0) { - } -#endif -} spEventData; - -SP_API spEventData* spEventData_create (const char* name); -SP_API void spEventData_dispose (spEventData* self); - -#ifdef SPINE_SHORT_NAMES -typedef spEventData EventData; -#define EventData_create(...) spEventData_create(__VA_ARGS__) -#define EventData_dispose(...) spEventData_dispose(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#ifndef Spine_EventData_h +#define Spine_EventData_h + +#include +#include + +namespace spine { +/// Stores the setup pose values for an Event. +class SP_API EventData : public SpineObject { + friend class SkeletonBinary; + + friend class SkeletonJson; + + friend class Event; + +public: + explicit EventData(const String &name); + + /// The name of the event, which is unique within the skeleton. + const String &getName() const; + + int getIntValue(); + + void setIntValue(int inValue); + + float getFloatValue(); + + void setFloatValue(float inValue); + + const String &getStringValue(); + + void setStringValue(const String &inValue); + + const String &getAudioPath(); + + void setAudioPath(const String &inValue); + + float getVolume(); + + void setVolume(float inValue); + + float getBalance(); + + void setBalance(float inValue); + +private: + const String _name; + int _intValue; + float _floatValue; + String _stringValue; + String _audioPath; + float _volume; + float _balance; +}; } -#endif -#endif /* SPINE_EVENTDATA_H_ */ +#endif /* Spine_EventData_h */ diff --git a/cocos/editor-support/spine/EventTimeline.cpp b/cocos/editor-support/spine/EventTimeline.cpp new file mode 100644 index 000000000000..05f17890ce68 --- /dev/null +++ b/cocos/editor-support/spine/EventTimeline.cpp @@ -0,0 +1,115 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(EventTimeline, Timeline) + +EventTimeline::EventTimeline(int frameCount) : Timeline() { + _frames.setSize(frameCount, 0); + _events.setSize(frameCount, NULL); +} + +EventTimeline::~EventTimeline() { + ContainerUtil::cleanUpVectorOfPointers(_events); +} + +void EventTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + if (pEvents == NULL) { + return; + } + + Vector &events = *pEvents; + + size_t frameCount = _frames.size(); + + if (lastTime > time) { + // Fire events after last time for looped animations. + apply(skeleton, lastTime, std::numeric_limits::max(), pEvents, alpha, blend, direction); + lastTime = -1.0f; + } else if (lastTime >= _frames[frameCount - 1]) { + // Last time is after last frame. + return; + } + + if (time < _frames[0]) { + return; // Time is before first frame. + } + + int frame; + if (lastTime < _frames[0]) { + frame = 0; + } else { + frame = Animation::binarySearch(_frames, lastTime); + float frameTime = _frames[frame]; + while (frame > 0) { + // Fire multiple events with the same frame. + if (_frames[frame - 1] != frameTime) { + break; + } + frame--; + } + } + + for (; (size_t)frame < frameCount && time >= _frames[frame]; ++frame) { + events.add(_events[frame]); + } +} + +int EventTimeline::getPropertyId() { + return ((int) TimelineType_Event << 24); +} + +void EventTimeline::setFrame(size_t frameIndex, Event *event) { + _frames[frameIndex] = event->getTime(); + _events[frameIndex] = event; +} + +Vector EventTimeline::getFrames() { return _frames; } + +Vector &EventTimeline::getEvents() { return _events; } + +size_t EventTimeline::getFrameCount() { return _frames.size(); } diff --git a/cocos/editor-support/spine/EventTimeline.h b/cocos/editor-support/spine/EventTimeline.h new file mode 100644 index 000000000000..acde27f003f5 --- /dev/null +++ b/cocos/editor-support/spine/EventTimeline.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_EventTimeline_h +#define Spine_EventTimeline_h + +#include + +namespace spine { + class SP_API EventTimeline : public Timeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + explicit EventTimeline(int frameCount); + + ~EventTimeline(); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(size_t frameIndex, Event* event); + + Vector getFrames(); + Vector& getEvents(); + size_t getFrameCount(); + + private: + Vector _frames; + Vector _events; + }; +} + +#endif /* Spine_EventTimeline_h */ diff --git a/cocos/editor-support/spine/Extension.cpp b/cocos/editor-support/spine/Extension.cpp new file mode 100644 index 000000000000..8a34efa8210d --- /dev/null +++ b/cocos/editor-support/spine/Extension.cpp @@ -0,0 +1,126 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include + +#include + +using namespace spine; + +SpineExtension *SpineExtension::_instance = NULL; + +void SpineExtension::setInstance(SpineExtension *inValue) { + assert(inValue); + + _instance = inValue; +} + +SpineExtension *SpineExtension::getInstance() { + if (!_instance) _instance = spine::getDefaultExtension(); + assert(_instance); + + return _instance; +} + +SpineExtension::~SpineExtension() { +} + +SpineExtension::SpineExtension() { +} + +DefaultSpineExtension::~DefaultSpineExtension() { +} + +void *DefaultSpineExtension::_alloc(size_t size, const char *file, int line) { + SP_UNUSED(file); + SP_UNUSED(line); + + if (size == 0) + return 0; + void *ptr = ::malloc(size); + return ptr; +} + +void *DefaultSpineExtension::_calloc(size_t size, const char *file, int line) { + SP_UNUSED(file); + SP_UNUSED(line); + + if (size == 0) + return 0; + + void *ptr = ::malloc(size); + if (ptr) { + memset(ptr, 0, size); + } + return ptr; +} + +void *DefaultSpineExtension::_realloc(void *ptr, size_t size, const char *file, int line) { + SP_UNUSED(file); + SP_UNUSED(line); + + void *mem = NULL; + if (size == 0) + return 0; + if (ptr == NULL) + mem = ::malloc(size); + else + mem = ::realloc(ptr, size); + return mem; +} + +void DefaultSpineExtension::_free(void *mem, const char *file, int line) { + SP_UNUSED(file); + SP_UNUSED(line); + + ::free(mem); +} + +char *DefaultSpineExtension::_readFile(const String &path, int *length) { + char *data; + FILE *file = fopen(path.buffer(), "rb"); + if (!file) return 0; + + fseek(file, 0, SEEK_END); + *length = (int) ftell(file); + fseek(file, 0, SEEK_SET); + + data = SpineExtension::alloc(*length, __FILE__, __LINE__); + fread(data, 1, *length, file); + fclose(file); + + return data; +} + +DefaultSpineExtension::DefaultSpineExtension() : SpineExtension() { +} diff --git a/cocos/editor-support/spine/Extension.h b/cocos/editor-support/spine/Extension.h new file mode 100644 index 000000000000..c442ca98d49d --- /dev/null +++ b/cocos/editor-support/spine/Extension.h @@ -0,0 +1,117 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Extension_h +#define Spine_Extension_h + +#include +#include + +#define SP_UNUSED(x) (void)(x) + +namespace spine { +class String; + +class SP_API SpineExtension { +public: + template + static T *alloc(size_t num, const char *file, int line) { + return (T *) getInstance()->_alloc(sizeof(T) * num, file, line); + } + + template + static T *calloc(size_t num, const char *file, int line) { + return (T *) getInstance()->_calloc(sizeof(T) * num, file, line); + } + + template + static T *realloc(T *ptr, size_t num, const char *file, int line) { + return (T *) getInstance()->_realloc(ptr, sizeof(T) * num, file, line); + } + + template + static void free(T *ptr, const char *file, int line) { + getInstance()->_free((void *) ptr, file, line); + } + + static char *readFile(const String &path, int *length) { + return getInstance()->_readFile(path, length); + } + + static void setInstance(SpineExtension *inSpineExtension); + + static SpineExtension *getInstance(); + + virtual ~SpineExtension(); + + /// Implement this function to use your own memory allocator + virtual void *_alloc(size_t size, const char *file, int line) = 0; + + virtual void *_calloc(size_t size, const char *file, int line) = 0; + + virtual void *_realloc(void *ptr, size_t size, const char *file, int line) = 0; + + /// If you provide a spineAllocFunc, you should also provide a spineFreeFunc + virtual void _free(void *mem, const char *file, int line) = 0; + + virtual char *_readFile(const String &path, int *length) = 0; + +protected: + SpineExtension(); + +private: + static SpineExtension *_instance; +}; + +class SP_API DefaultSpineExtension : public SpineExtension { +public: + DefaultSpineExtension(); + + virtual ~DefaultSpineExtension(); + +protected: + virtual void *_alloc(size_t size, const char *file, int line); + + virtual void *_calloc(size_t size, const char *file, int line); + + virtual void *_realloc(void *ptr, size_t size, const char *file, int line); + + virtual void _free(void *mem, const char *file, int line); + + virtual char *_readFile(const String &path, int *length); +}; + +// This function is to be implemented by engine specific runtimes to provide +// the default extension for that engine. It is called the first time +// SpineExtension::getInstance() is called, when no instance has been set +// yet. +extern SpineExtension *getDefaultExtension(); +} + +#endif /* Spine_Extension_h */ diff --git a/cocos/editor-support/spine/HasRendererObject.h b/cocos/editor-support/spine/HasRendererObject.h new file mode 100644 index 000000000000..5dd2f5a9f92b --- /dev/null +++ b/cocos/editor-support/spine/HasRendererObject.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_HasRendererObject_h +#define Spine_HasRendererObject_h + +namespace spine { + +typedef void (*DisposeRendererObject) (void* rendererObject); + +class SP_API HasRendererObject { +public: + explicit HasRendererObject() : _rendererObject(NULL), _dispose(NULL) {}; + + virtual ~HasRendererObject() { + if (_dispose && _rendererObject) + _dispose(_rendererObject); + } + + void* getRendererObject() { return _rendererObject; } + void setRendererObject(void* rendererObject, DisposeRendererObject dispose = NULL) { + _rendererObject = rendererObject; + _dispose = dispose; + } +private: + void *_rendererObject; + DisposeRendererObject _dispose; +}; + +} + +#endif diff --git a/cocos/editor-support/spine/HashMap.h b/cocos/editor-support/spine/HashMap.h new file mode 100644 index 000000000000..bbac45bb0fa5 --- /dev/null +++ b/cocos/editor-support/spine/HashMap.h @@ -0,0 +1,184 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_HashMap_h +#define Spine_HashMap_h + +#include +#include +#include + + // Required for new with line number and file name in MSVC +#ifdef _MSC_VER +#pragma warning(disable:4291) +#endif + +namespace spine { +template +class SP_API HashMap : public SpineObject { +private: + class Entry; + +public: + class SP_API Pair { + public: + explicit Pair(K &k, V &v) : key(k), value(v) {} + + K &key; + V &value; + }; + + class SP_API Entries { + public: + friend class HashMap; + + explicit Entries(Entry *entry) : _entry(NULL), _hasChecked(false) { + _start.next = entry; + _entry = &_start; + } + + Pair next() { + assert(_entry); + assert(_hasChecked); + _entry = _entry->next; + Pair pair(_entry->_key, _entry->_value); + _hasChecked = false; + return pair; + } + + bool hasNext() { + _hasChecked = true; + return _entry->next; + } + + private: + bool _hasChecked; + Entry _start; + Entry *_entry; + }; + + HashMap() : + _head(NULL), + _size(0) { + } + + ~HashMap() { + for (Entry *entry = _head; entry != NULL;) { + Entry* next = entry->next; + delete entry; + entry = next; + } + } + + size_t size() { + return _size; + } + + void put(const K &key, const V &value) { + Entry *entry = find(key); + if (entry) { + entry->_key = key; + entry->_value = value; + } else { + entry = new(__FILE__, __LINE__) Entry(); + entry->_key = key; + entry->_value = value; + + Entry *oldHead = _head; + + if (oldHead) { + _head = entry; + oldHead->prev = entry; + entry->next = oldHead; + } else { + _head = entry; + } + _size++; + } + } + + bool containsKey(const K &key) { + return find(key) != NULL; + } + + bool remove(const K &key) { + Entry *entry = find(key); + if (!entry) return false; + + Entry *prev = entry->prev; + Entry *next = entry->next; + + if (prev) prev->next = next; + else _head = next; + if (next) next->prev = entry->prev; + + delete entry; + _size--; + + return true; + } + + V operator[](const K &key) { + Entry *entry = find(key); + if (entry) return entry->_value; + else { + assert(false); + return 0; + } + } + + Entries getEntries() const { + return Entries(_head); + } + +private: + Entry *find(const K &key) { + for (Entry *entry = _head; entry != NULL; entry = entry->next) { + if (entry->_key == key) + return entry; + } + return NULL; + } + + class SP_API Entry : public SpineObject { + public: + K _key; + V _value; + Entry *next; + Entry *prev; + + Entry() : next(NULL), prev(NULL) {} + }; + + Entry *_head; + size_t _size; +}; +} + +#endif /* Spine_HashMap_h */ diff --git a/cocos/editor-support/spine/IkConstraint.cpp b/cocos/editor-support/spine/IkConstraint.cpp new file mode 100644 index 000000000000..4e614125d93a --- /dev/null +++ b/cocos/editor-support/spine/IkConstraint.cpp @@ -0,0 +1,296 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include +#include + +#include + +using namespace spine; + +RTTI_IMPL(IkConstraint, Constraint) + +void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool compress, bool stretch, bool uniform, float alpha) { + Bone *p = bone.getParent(); + float id, x, y, tx, ty, rotationIK; + if (!bone._appliedValid) bone.updateAppliedTransform(); + id = 1 / (p->_a * p->_d - p->_b * p->_c); + x = targetX - p->_worldX, y = targetY - p->_worldY; + tx = (x * p->_d - y * p->_b) * id - bone._ax; + ty = (y * p->_a - x * p->_c) * id - bone._ay; + rotationIK = MathUtil::atan2(ty, tx) * MathUtil::Rad_Deg - bone._ashearX - bone._arotation; + if (bone._ascaleX < 0) rotationIK += 180; + if (rotationIK > 180) rotationIK -= 360; + else if (rotationIK < -180) rotationIK += 360; + float sx = bone._ascaleX; + float sy = bone._ascaleY; + if (compress || stretch) { + float b = bone._data.getLength() * sx, dd = MathUtil::sqrt(tx * tx + ty * ty); + if (((compress && dd < b) || (stretch && dd > b)) && (b > 0.0001f)) { + float s = (dd / b - 1) * alpha + 1; + sx *= s; + if (uniform) sy *= s; + } + } + bone.updateWorldTransform(bone._ax, bone._ay, bone._arotation + rotationIK * alpha, sx, + sy, bone._ashearX, bone._ashearY); +} + +void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY, int bendDir, bool stretch, float alpha) { + float px, py, psx, sx, psy; + float cx, cy, csx, cwx, cwy; + int o1, o2, s2, u; + Bone *pp = parent.getParent(); + float tx, ty, dx, dy, dd, l1, l2, a1, a2, r; + float id, x, y; + if (alpha == 0) { + child.updateWorldTransform(); + return; + } + if (!parent._appliedValid) parent.updateAppliedTransform(); + if (!child._appliedValid) child.updateAppliedTransform(); + px = parent._ax; + py = parent._ay; + psx = parent._ascaleX; + sx = psx; + psy = parent._ascaleY; + csx = child._ascaleX; + if (psx < 0) { + psx = -psx; + o1 = 180; + s2 = -1; + } else { + o1 = 0; + s2 = 1; + } + if (psy < 0) { + psy = -psy; + s2 = -s2; + } + if (csx < 0) { + csx = -csx; + o2 = 180; + } else + o2 = 0; + r = psx - psy; + cx = child._ax; + u = (r < 0 ? -r : r) <= 0.0001f; + if (!u) { + cy = 0; + cwx = parent._a * cx + parent._worldX; + cwy = parent._c * cx + parent._worldY; + } else { + cy = child._ay; + cwx = parent._a * cx + parent._b * cy + parent._worldX; + cwy = parent._c * cx + parent._d * cy + parent._worldY; + } + id = 1 / (pp->_a * pp->_d - pp->_b * pp->_c); + x = targetX - pp->_worldX; + y = targetY - pp->_worldY; + tx = (x * pp->_d - y * pp->_b) * id - px; + ty = (y * pp->_a - x * pp->_c) * id - py; + dd = tx * tx + ty * ty; + x = cwx - pp->_worldX; + y = cwy - pp->_worldY; + dx = (x * pp->_d - y * pp->_b) * id - px; + dy = (y * pp->_a - x * pp->_c) * id - py; + l1 = MathUtil::sqrt(dx * dx + dy * dy); + l2 = child.getData().getLength() * csx; + if (u) { + float cosine, a, b; + l2 *= psx; + cosine = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2); + if (cosine < -1) cosine = -1; + else if (cosine > 1) { + cosine = 1; + if (stretch && l1 + l2 > 0.0001f) sx *= (MathUtil::sqrt(dd) / (l1 + l2) - 1) * alpha + 1; + } + a2 = MathUtil::acos(cosine) * bendDir; + a = l1 + l2 * cosine; + b = l2 * MathUtil::sin(a2); + a1 = MathUtil::atan2(ty * a - tx * b, tx * a + ty * b); + } else { + float a = psx * l2, b = psy * l2; + float aa = a * a, bb = b * b, ll = l1 * l1, ta = MathUtil::atan2(ty, tx); + float c0 = bb * ll + aa * dd - aa * bb, c1 = -2 * bb * l1, c2 = bb - aa; + float d = c1 * c1 - 4 * c2 * c0; + if (d >= 0) { + float q = MathUtil::sqrt(d), r0, r1; + if (c1 < 0) q = -q; + q = -(c1 + q) / 2; + r0 = q / c2; + r1 = c0 / q; + r = MathUtil::abs(r0) < MathUtil::abs(r1) ? r0 : r1; + if (r * r <= dd) { + y = MathUtil::sqrt(dd - r * r) * bendDir; + a1 = ta - MathUtil::atan2(y, r); + a2 = MathUtil::atan2(y / psy, (r - l1) / psx); + goto break_outer; + } + } + { + float minAngle = MathUtil::Pi, minX = l1 - a, minDist = minX * minX, minY = 0; + float maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0; + c0 = -a * l1 / (aa - bb); + if (c0 >= -1 && c0 <= 1) { + c0 = MathUtil::acos(c0); + x = a * MathUtil::cos(c0) + l1; + y = b * MathUtil::sin(c0); + d = x * x + y * y; + if (d < minDist) { + minAngle = c0; + minDist = d; + minX = x; + minY = y; + } + if (d > maxDist) { + maxAngle = c0; + maxDist = d; + maxX = x; + maxY = y; + } + } + if (dd <= (minDist + maxDist) / 2) { + a1 = ta - MathUtil::atan2(minY * bendDir, minX); + a2 = minAngle * bendDir; + } else { + a1 = ta - MathUtil::atan2(maxY * bendDir, maxX); + a2 = maxAngle * bendDir; + } + } + } + break_outer: + { + float os = MathUtil::atan2(cy, cx) * s2; + a1 = (a1 - os) * MathUtil::Rad_Deg + o1 - parent._arotation; + if (a1 > 180) a1 -= 360; + else if (a1 < -180) a1 += 360; + parent.updateWorldTransform(px, py, parent._rotation + a1 * alpha, sx, parent._ascaleY, 0, 0); + a2 = ((a2 + os) * MathUtil::Rad_Deg - child._ashearX) * s2 + o2 - child._arotation; + if (a2 > 180) a2 -= 360; + else if (a2 < -180) a2 += 360; + child.updateWorldTransform(cx, cy, child._arotation + a2 * alpha, child._ascaleX, child._ascaleY, + child._ashearX, child._ashearY); + } +} + +IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : Constraint(), + _data(data), + _bendDirection(data.getBendDirection()), + _compress(data.getCompress()), + _stretch(data.getStretch()), + _mix(data.getMix()), + _target(skeleton.findBone( + data.getTarget()->getName())) { + _bones.ensureCapacity(_data.getBones().size()); + for (size_t i = 0; i < _data.getBones().size(); i++) { + BoneData *boneData = _data.getBones()[i]; + _bones.add(skeleton.findBone(boneData->getName())); + } +} + +/// Applies the constraint to the constrained bones. +void IkConstraint::apply() { + update(); +} + +void IkConstraint::update() { + switch (_bones.size()) { + case 1: { + Bone *bone0 = _bones[0]; + apply(*bone0, _target->getWorldX(), _target->getWorldY(), _compress, _stretch, _data._uniform, _mix); + } + break; + case 2: { + Bone *bone0 = _bones[0]; + Bone *bone1 = _bones[1]; + apply(*bone0, *bone1, _target->getWorldX(), _target->getWorldY(), _bendDirection, _stretch, _mix); + } + break; + } +} + +int IkConstraint::getOrder() { + return _data.getOrder(); +} + +IkConstraintData &IkConstraint::getData() { + return _data; +} + +Vector &IkConstraint::getBones() { + return _bones; +} + +Bone *IkConstraint::getTarget() { + return _target; +} + +void IkConstraint::setTarget(Bone *inValue) { + _target = inValue; +} + +int IkConstraint::getBendDirection() { + return _bendDirection; +} + +void IkConstraint::setBendDirection(int inValue) { + _bendDirection = inValue; +} + +float IkConstraint::getMix() { + return _mix; +} + +void IkConstraint::setMix(float inValue) { + _mix = inValue; +} + +bool IkConstraint::getStretch() { + return _stretch; +} + +void IkConstraint::setStretch(bool inValue) { + _stretch = inValue; +} + +bool IkConstraint::getCompress() { + return _compress; +} + +void IkConstraint::setCompress(bool inValue) { + _compress = inValue; +} diff --git a/cocos/editor-support/spine/IkConstraint.h b/cocos/editor-support/spine/IkConstraint.h index e9f27ba2e7dd..b872ebe92660 100644 --- a/cocos/editor-support/spine/IkConstraint.h +++ b/cocos/editor-support/spine/IkConstraint.h @@ -1,87 +1,105 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_IKCONSTRAINT_H_ -#define SPINE_IKCONSTRAINT_H_ +#ifndef Spine_IkConstraint_h +#define Spine_IkConstraint_h + +#include + +#include + +namespace spine { +class IkConstraintData; + +class Skeleton; + +class Bone; + +class SP_API IkConstraint : public Constraint { + friend class Skeleton; + + friend class IkConstraintTimeline; + +RTTI_DECL + +public: + /// Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified + /// in the world coordinate system. + static void apply(Bone &bone, float targetX, float targetY, bool compress, bool stretch, bool uniform, float alpha); + + /// Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as + /// possible. The target is specified in the world coordinate system. + /// @param child A direct descendant of the parent bone. + static void apply(Bone &parent, Bone &child, float targetX, float targetY, int bendDir, bool stretch, float alpha); + + IkConstraint(IkConstraintData &data, Skeleton &skeleton); + + /// Applies the constraint to the constrained bones. + void apply(); + + virtual void update(); + + virtual int getOrder(); + + IkConstraintData &getData(); -#include -#include -#include + Vector &getBones(); -#ifdef __cplusplus -extern "C" { -#endif + Bone *getTarget(); -struct spSkeleton; + void setTarget(Bone *inValue); -typedef struct spIkConstraint { - spIkConstraintData* const data; + int getBendDirection(); - int bonesCount; - spBone** bones; + void setBendDirection(int inValue); - spBone* target; - int bendDirection; - float mix; + bool getCompress(); -#ifdef __cplusplus - spIkConstraint() : - data(0), - bonesCount(0), - bones(0), - target(0), - bendDirection(0), - mix(0) { - } -#endif -} spIkConstraint; + void setCompress(bool inValue); -SP_API spIkConstraint* spIkConstraint_create (spIkConstraintData* data, const struct spSkeleton* skeleton); -SP_API void spIkConstraint_dispose (spIkConstraint* self); + bool getStretch(); -SP_API void spIkConstraint_apply (spIkConstraint* self); + void setStretch(bool inValue); -SP_API void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, float alpha); -SP_API void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float targetY, int bendDirection, float alpha); + float getMix(); -#ifdef SPINE_SHORT_NAMES -typedef spIkConstraint IkConstraint; -#define IkConstraint_create(...) spIkConstraint_create(__VA_ARGS__) -#define IkConstraint_dispose(...) spIkConstraint_dispose(__VA_ARGS__) -#define IkConstraint_apply(...) spIkConstraint_apply(__VA_ARGS__) -#define IkConstraint_apply1(...) spIkConstraint_apply1(__VA_ARGS__) -#define IkConstraint_apply2(...) spIkConstraint_apply2(__VA_ARGS__) -#endif + void setMix(float inValue); -#ifdef __cplusplus +private: + IkConstraintData &_data; + Vector _bones; + int _bendDirection; + bool _compress; + bool _stretch; + float _mix; + Bone *_target; +}; } -#endif -#endif /* SPINE_IKCONSTRAINT_H_ */ +#endif /* Spine_IkConstraint_h */ diff --git a/cocos/editor-support/spine/IkConstraintData.cpp b/cocos/editor-support/spine/IkConstraintData.cpp new file mode 100644 index 000000000000..a0f52350f2a2 --- /dev/null +++ b/cocos/editor-support/spine/IkConstraintData.cpp @@ -0,0 +1,114 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +using namespace spine; + +IkConstraintData::IkConstraintData(const String &name) : + _name(name), + _order(0), + _target(NULL), + _bendDirection(1), + _compress(false), + _stretch(false), + _uniform(false), + _mix(1) { +} + +const String &IkConstraintData::getName() { + return _name; +} + +size_t IkConstraintData::getOrder() { + return _order; +} + +void IkConstraintData::setOrder(size_t inValue) { + _order = inValue; +} + +Vector &IkConstraintData::getBones() { + return _bones; +} + +BoneData *IkConstraintData::getTarget() { + return _target; +} + +void IkConstraintData::setTarget(BoneData *inValue) { + _target = inValue; +} + +int IkConstraintData::getBendDirection() { + return _bendDirection; +} + +void IkConstraintData::setBendDirection(int inValue) { + _bendDirection = inValue; +} + +float IkConstraintData::getMix() { + return _mix; +} + +void IkConstraintData::setMix(float inValue) { + _mix = inValue; +} + +bool IkConstraintData::getStretch() { + return _stretch; +} + +void IkConstraintData::setStretch(bool inValue) { + _stretch = inValue; +} + +bool IkConstraintData::getCompress() { + return _compress; +} + +void IkConstraintData::setCompress(bool inValue) { + _compress = inValue; +} + + +bool IkConstraintData::getUniform() { + return _uniform; +} + +void IkConstraintData::setUniform(bool inValue) { + _uniform = inValue; +} diff --git a/cocos/editor-support/spine/IkConstraintData.h b/cocos/editor-support/spine/IkConstraintData.h index de8f899c1d26..5079f21eff35 100644 --- a/cocos/editor-support/spine/IkConstraintData.h +++ b/cocos/editor-support/spine/IkConstraintData.h @@ -1,76 +1,92 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_IKCONSTRAINTDATA_H_ -#define SPINE_IKCONSTRAINTDATA_H_ - -#include -#include +#ifndef Spine_IkConstraintData_h +#define Spine_IkConstraintData_h -#ifdef __cplusplus -extern "C" { -#endif +#include +#include +#include -typedef struct spIkConstraintData { - const char* const name; - int order; - int bonesCount; - spBoneData** bones; +namespace spine { + class BoneData; + + class SP_API IkConstraintData : public SpineObject { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class IkConstraint; + friend class Skeleton; + friend class IkConstraintTimeline; + + public: + explicit IkConstraintData(const String& name); + + /// The IK constraint's name, which is unique within the skeleton. + const String& getName(); - spBoneData* target; - int bendDirection; - float mix; + size_t getOrder(); + void setOrder(size_t inValue); + + /// The bones that are constrained by this IK Constraint. + Vector& getBones(); + + /// The bone that is the IK target. + BoneData* getTarget(); + void setTarget(BoneData* inValue); + + /// Controls the bend direction of the IK bones, either 1 or -1. + int getBendDirection(); + void setBendDirection(int inValue); -#ifdef __cplusplus - spIkConstraintData() : - name(0), - bonesCount(0), - bones(0), - target(0), - bendDirection(0), - mix(0) { - } -#endif -} spIkConstraintData; + bool getCompress(); + void setCompress(bool inValue); -SP_API spIkConstraintData* spIkConstraintData_create (const char* name); -SP_API void spIkConstraintData_dispose (spIkConstraintData* self); + bool getStretch(); + void setStretch(bool inValue); -#ifdef SPINE_SHORT_NAMES -typedef spIkConstraintData IkConstraintData; -#define IkConstraintData_create(...) spIkConstraintData_create(__VA_ARGS__) -#define IkConstraintData_dispose(...) spIkConstraintData_dispose(__VA_ARGS__) -#endif + bool getUniform(); + void setUniform(bool inValue); + + float getMix(); + void setMix(float inValue); -#ifdef __cplusplus + private: + const String _name; + size_t _order; + Vector _bones; + BoneData* _target; + int _bendDirection; + bool _compress; + bool _stretch; + bool _uniform; + float _mix; + }; } -#endif -#endif /* SPINE_IKCONSTRAINTDATA_H_ */ +#endif /* Spine_IkConstraintData_h */ diff --git a/cocos/editor-support/spine/IkConstraintTimeline.cpp b/cocos/editor-support/spine/IkConstraintTimeline.cpp new file mode 100644 index 000000000000..80575b17b008 --- /dev/null +++ b/cocos/editor-support/spine/IkConstraintTimeline.cpp @@ -0,0 +1,156 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(IkConstraintTimeline, CurveTimeline) + +const int IkConstraintTimeline::ENTRIES = 5; +const int IkConstraintTimeline::PREV_TIME = -5; +const int IkConstraintTimeline::PREV_MIX = -4; +const int IkConstraintTimeline::PREV_BEND_DIRECTION = -3; +const int IkConstraintTimeline::PREV_COMPRESS = -2; +const int IkConstraintTimeline::PREV_STRETCH = -1; +const int IkConstraintTimeline::MIX = 1; +const int IkConstraintTimeline::BEND_DIRECTION = 2; +const int IkConstraintTimeline::COMPRESS = 3; +const int IkConstraintTimeline::STRETCH = 4; + +IkConstraintTimeline::IkConstraintTimeline(int frameCount) : CurveTimeline(frameCount), _ikConstraintIndex(0) { + _frames.setSize(frameCount * ENTRIES, 0); +} + +void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + + IkConstraint *constraintP = skeleton._ikConstraints[_ikConstraintIndex]; + IkConstraint &constraint = *constraintP; + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + constraint._mix = constraint._data._mix; + constraint._bendDirection = constraint._data._bendDirection; + constraint._compress = constraint._data._compress; + constraint._stretch = constraint._data._stretch; + return; + case MixBlend_First: + constraint._mix += (constraint._data._mix - constraint._mix) * alpha; + constraint._bendDirection = constraint._data._bendDirection; + constraint._compress = constraint._data._compress; + constraint._stretch = constraint._data._stretch; + return; + default: + return; + } + } + + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + if (blend == MixBlend_Setup) { + constraint._mix = + constraint._data._mix + (_frames[_frames.size() + PREV_MIX] - constraint._data._mix) * alpha; + if (direction == MixDirection_Out) { + constraint._bendDirection = constraint._data._bendDirection; + constraint._compress = constraint._data._compress; + constraint._stretch = constraint._data._stretch; + } else { + constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION]; + constraint._compress = _frames[_frames.size() + PREV_COMPRESS] != 0; + constraint._stretch = _frames[_frames.size() + PREV_STRETCH] != 0; + } + } else { + constraint._mix += (_frames[_frames.size() + PREV_MIX] - constraint._mix) * alpha; + if (direction == MixDirection_In) { + constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION]; + constraint._compress = _frames[_frames.size() + PREV_COMPRESS] != 0; + constraint._stretch = _frames[_frames.size() + PREV_STRETCH] != 0; + } + } + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + float mix = _frames[frame + PREV_MIX]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + if (blend == MixBlend_Setup) { + constraint._mix = + constraint._data._mix + (mix + (_frames[frame + MIX] - mix) * percent - constraint._data._mix) * alpha; + if (direction == MixDirection_Out) { + constraint._bendDirection = constraint._data._bendDirection; + constraint._compress = constraint._data._compress; + constraint._stretch = constraint._data._stretch; + } else { + constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION]; + constraint._compress = _frames[frame + PREV_COMPRESS] != 0; + constraint._stretch = _frames[frame + PREV_STRETCH] != 0; + } + } else { + constraint._mix += (mix + (_frames[frame + MIX] - mix) * percent - constraint._mix) * alpha; + if (direction == MixDirection_In) { + constraint._bendDirection = (int) _frames[frame + PREV_BEND_DIRECTION]; + constraint._compress = _frames[frame + PREV_COMPRESS] != 0; + constraint._stretch = _frames[frame + PREV_STRETCH] != 0; + } + } +} + +int IkConstraintTimeline::getPropertyId() { + return ((int) TimelineType_IkConstraint << 24) + _ikConstraintIndex; +} + +void IkConstraintTimeline::setFrame(int frameIndex, float time, float mix, int bendDirection, bool compress, bool stretch) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + MIX] = mix; + _frames[frameIndex + BEND_DIRECTION] = (float)bendDirection; + _frames[frameIndex + COMPRESS] = compress ? 1 : 0; + _frames[frameIndex + STRETCH] = stretch ? 1 : 0; +} diff --git a/cocos/editor-support/spine/IkConstraintTimeline.h b/cocos/editor-support/spine/IkConstraintTimeline.h new file mode 100644 index 000000000000..8285acf00b03 --- /dev/null +++ b/cocos/editor-support/spine/IkConstraintTimeline.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_IkConstraintTimeline_h +#define Spine_IkConstraintTimeline_h + +#include + +namespace spine { + + class SP_API IkConstraintTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + static const int ENTRIES; + + explicit IkConstraintTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time, mix and bend direction of the specified keyframe. + void setFrame (int frameIndex, float time, float mix, int bendDirection, bool compress, bool stretch); + + private: + static const int PREV_TIME; + static const int PREV_MIX; + static const int PREV_BEND_DIRECTION; + static const int PREV_COMPRESS; + static const int PREV_STRETCH; + static const int MIX; + static const int BEND_DIRECTION; + static const int COMPRESS; + static const int STRETCH; + + Vector _frames; + int _ikConstraintIndex; + }; +} + +#endif /* Spine_IkConstraintTimeline_h */ diff --git a/cocos/editor-support/spine/Json.cpp b/cocos/editor-support/spine/Json.cpp new file mode 100644 index 000000000000..78f84ac5dccc --- /dev/null +++ b/cocos/editor-support/spine/Json.cpp @@ -0,0 +1,543 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +/* Json */ +/* JSON parser in CPP, shamelessly ripped from json.c in the spine-c runtime */ + +#ifndef _DEFAULT_SOURCE +/* Bring strings.h definitions into string.h, where appropriate */ +#define _DEFAULT_SOURCE +#endif + +#ifndef _BSD_SOURCE +/* Bring strings.h definitions into string.h, where appropriate */ +#define _BSD_SOURCE +#endif + +#include +#include +#include + +#include +#include + +using namespace spine; + +const int Json::JSON_FALSE = 0; +const int Json::JSON_TRUE = 1; +const int Json::JSON_NULL = 2; +const int Json::JSON_NUMBER = 3; +const int Json::JSON_STRING = 4; +const int Json::JSON_ARRAY = 5; +const int Json::JSON_OBJECT = 6; + +const char *Json::_error = NULL; + +Json *Json::getItem(Json *object, const char *string) { + Json *c = object->_child; + while (c && json_strcasecmp(c->_name, string)) { + c = c->_next; + } + return c; +} + +const char *Json::getString(Json *object, const char *name, const char *defaultValue) { + object = getItem(object, name); + if (object) { + return object->_valueString; + } + + return defaultValue; +} + +float Json::getFloat(Json *value, const char *name, float defaultValue) { + value = getItem(value, name); + return value ? value->_valueFloat : defaultValue; +} + +int Json::getInt(Json *value, const char *name, int defaultValue) { + value = getItem(value, name); + return value ? value->_valueInt : defaultValue; +} + +const char *Json::getError() { + return _error; +} + +Json::Json(const char *value) : + _next(NULL), +#if SPINE_JSON_HAVE_PREV + _prev(NULL), +#endif + _child(NULL), + _type(0), + _size(0), + _valueString(NULL), + _valueInt(0), + _valueFloat(0), + _name(NULL) { + if (value) { + value = parseValue(this, skip(value)); + + assert(value); + } +} + +Json::~Json() { + delete _child; + + if (_valueString) { + SpineExtension::free(_valueString, __FILE__, __LINE__); + } + + if (_name) { + SpineExtension::free(_name, __FILE__, __LINE__); + } + + delete _next; +} + +const char *Json::skip(const char *inValue) { + if (!inValue) { + /* must propagate NULL since it's often called in skip(f(...)) form */ + return NULL; + } + + while (*inValue && (unsigned char) *inValue <= 32) { + inValue++; + } + + return inValue; +} + +const char *Json::parseValue(Json *item, const char *value) { + /* Referenced by constructor, parseArray(), and parseObject(). */ + /* Always called with the result of skip(). */ +#ifdef SPINE_JSON_DEBUG /* Checked at entry to graph, constructor, and after every parse call. */ + if (!value) { + /* Fail on null. */ + return NULL; + } +#endif + + switch (*value) { + case 'n': { + if (!strncmp(value + 1, "ull", 3)) { + item->_type = JSON_NULL; + return value + 4; + } + break; + } + case 'f': { + if (!strncmp(value + 1, "alse", 4)) { + item->_type = JSON_FALSE; + /* calloc prevents us needing item->_type = JSON_FALSE or valueInt = 0 here */ + return value + 5; + } + break; + } + case 't': { + if (!strncmp(value + 1, "rue", 3)) { + item->_type = JSON_TRUE; + item->_valueInt = 1; + return value + 4; + } + break; + } + case '\"': + return parseString(item, value); + case '[': + return parseArray(item, value); + case '{': + return parseObject(item, value); + case '-': /* fallthrough */ + case '0': /* fallthrough */ + case '1': /* fallthrough */ + case '2': /* fallthrough */ + case '3': /* fallthrough */ + case '4': /* fallthrough */ + case '5': /* fallthrough */ + case '6': /* fallthrough */ + case '7': /* fallthrough */ + case '8': /* fallthrough */ + case '9': + return parseNumber(item, value); + default: + break; + } + + _error = value; + return NULL; /* failure. */ +} + +static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; + +const char *Json::parseString(Json *item, const char *str) { + const char *ptr = str + 1; + char *ptr2; + char *out; + int len = 0; + unsigned uc, uc2; + if (*str != '\"') { + /* TODO: don't need this check when called from parseValue, but do need from parseObject */ + _error = str; + return 0; + } /* not a string! */ + + while (*ptr != '\"' && *ptr && ++len) { + if (*ptr++ == '\\') { + ptr++; /* Skip escaped quotes. */ + } + } + + out = SpineExtension::alloc(len + 1, __FILE__, __LINE__); /* The length needed for the string, roughly. */ + if (!out) { + return 0; + } + + ptr = str + 1; + ptr2 = out; + while (*ptr != '\"' && *ptr) { + if (*ptr != '\\') { + *ptr2++ = *ptr++; + } else { + ptr++; + switch (*ptr) { + case 'b': + *ptr2++ = '\b'; + break; + case 'f': + *ptr2++ = '\f'; + break; + case 'n': + *ptr2++ = '\n'; + break; + case 'r': + *ptr2++ = '\r'; + break; + case 't': + *ptr2++ = '\t'; + break; + case 'u': { + /* transcode utf16 to utf8. */ + sscanf(ptr + 1, "%4x", &uc); + ptr += 4; /* get the unicode char. */ + + if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) { + break; /* check for invalid. */ + } + + /* TODO provide an option to ignore surrogates, use unicode replacement character? */ + if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */ { + if (ptr[1] != '\\' || ptr[2] != 'u') { + break; /* missing second-half of surrogate. */ + } + sscanf(ptr + 3, "%4x", &uc2); + ptr += 6; + if (uc2 < 0xDC00 || uc2 > 0xDFFF) { + break; /* invalid second-half of surrogate. */ + } + uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); + } + + len = 4; + if (uc < 0x80) { + len = 1; + } else if (uc < 0x800) { + len = 2; + } else if (uc < 0x10000) { + len = 3; + } + ptr2 += len; + + switch (len) { + case 4: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + /* fallthrough */ + case 3: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + /* fallthrough */ + case 2: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + /* fallthrough */ + case 1: + *--ptr2 = (uc | firstByteMark[len]); + } + ptr2 += len; + break; + } + default: + *ptr2++ = *ptr; + break; + } + ptr++; + } + } + + *ptr2 = 0; + + if (*ptr == '\"') { + ptr++; /* TODO error handling if not \" or \0 ? */ + } + + item->_valueString = out; + item->_type = JSON_STRING; + + return ptr; +} + +const char *Json::parseNumber(Json *item, const char *num) { + double result = 0.0; + int negative = 0; + char *ptr = (char *) num; + + if (*ptr == '-') { + negative = -1; + ++ptr; + } + + while (*ptr >= '0' && *ptr <= '9') { + result = result * 10.0 + (*ptr - '0'); + ++ptr; + } + + if (*ptr == '.') { + double fraction = 0.0; + int n = 0; + ++ptr; + + while (*ptr >= '0' && *ptr <= '9') { + fraction = (fraction * 10.0) + (*ptr - '0'); + ++ptr; + ++n; + } + result += fraction / pow(10.0, n); + } + + if (negative) { + result = -result; + } + + if (*ptr == 'e' || *ptr == 'E') { + double exponent = 0; + int expNegative = 0; + int n = 0; + ++ptr; + + if (*ptr == '-') { + expNegative = -1; + ++ptr; + } else if (*ptr == '+') { + ++ptr; + } + + while (*ptr >= '0' && *ptr <= '9') { + exponent = (exponent * 10.0) + (*ptr - '0'); + ++ptr; + ++n; + } + + if (expNegative) { + result = result / pow(10, exponent); + } else { + result = result * pow(10, exponent); + } + } + + if (ptr != num) { + /* Parse success, number found. */ + item->_valueFloat = (float)result; + item->_valueInt = (int)result; + item->_type = JSON_NUMBER; + return ptr; + } else { + /* Parse failure, _error is set. */ + _error = num; + return NULL; + } +} + +const char *Json::parseArray(Json *item, const char *value) { + Json *child; + +#ifdef SPINE_JSON_DEBUG /* unnecessary, only callsite (parse_value) verifies this */ + if (*value != '[') { + ep = value; + return 0; + } /* not an array! */ +#endif + + item->_type = JSON_ARRAY; + value = skip(value + 1); + if (*value == ']') { + return value + 1; /* empty array. */ + } + + item->_child = child = new(__FILE__, __LINE__) Json(NULL); + if (!item->_child) { + return NULL; /* memory fail */ + } + + value = skip(parseValue(child, skip(value))); /* skip any spacing, get the value. */ + + if (!value) { + return NULL; + } + + item->_size = 1; + + while (*value == ',') { + Json *new_item = new(__FILE__, __LINE__) Json(NULL); + if (!new_item) { + return NULL; /* memory fail */ + } + child->_next = new_item; +#if SPINE_JSON_HAVE_PREV + new_item->prev = child; +#endif + child = new_item; + value = skip(parseValue(child, skip(value + 1))); + if (!value) { + return NULL; /* parse fail */ + } + item->_size++; + } + + if (*value == ']') { + return value + 1; /* end of array */ + } + + _error = value; + + return NULL; /* malformed. */ +} + +/* Build an object from the text. */ +const char *Json::parseObject(Json *item, const char *value) { + Json *child; + +#ifdef SPINE_JSON_DEBUG /* unnecessary, only callsite (parse_value) verifies this */ + if (*value != '{') { + ep = value; + return 0; + } /* not an object! */ +#endif + + item->_type = JSON_OBJECT; + value = skip(value + 1); + if (*value == '}') { + return value + 1; /* empty array. */ + } + + item->_child = child = new(__FILE__, __LINE__) Json(NULL); + if (!item->_child) { + return NULL; + } + value = skip(parseString(child, skip(value))); + if (!value) { + return NULL; + } + child->_name = child->_valueString; + child->_valueString = 0; + if (*value != ':') { + _error = value; + return NULL; + } /* fail! */ + + value = skip(parseValue(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) { + return NULL; + } + + item->_size = 1; + + while (*value == ',') { + Json *new_item = new(__FILE__, __LINE__) Json(NULL); + if (!new_item) { + return NULL; /* memory fail */ + } + child->_next = new_item; +#if SPINE_JSON_HAVE_PREV + new_item->prev = child; +#endif + child = new_item; + value = skip(parseString(child, skip(value + 1))); + if (!value) { + return NULL; + } + child->_name = child->_valueString; + child->_valueString = 0; + if (*value != ':') { + _error = value; + return NULL; + } /* fail! */ + + value = skip(parseValue(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) { + return NULL; + } + item->_size++; + } + + if (*value == '}') { + return value + 1; /* end of array */ + } + + _error = value; + + return NULL; /* malformed. */ +} + +int Json::json_strcasecmp(const char *s1, const char *s2) { + /* TODO we may be able to elide these NULL checks if we can prove + * the graph and input (only callsite is Json_getItem) should not have NULLs + */ + if (s1 && s2) { +#if defined(_WIN32) + return _stricmp(s1, s2); +#else + return strcasecmp(s1, s2); +#endif + } else { + if (s1 < s2) { + return -1; /* s1 is null, s2 is not */ + } else if (s1 == s2) { + return 0; /* both are null */ + } else { + return 1; /* s2 is nul s1 is not */ + } + } +} diff --git a/cocos/editor-support/spine/Json.h b/cocos/editor-support/spine/Json.h index 078a5a715827..97804da7a2ec 100644 --- a/cocos/editor-support/spine/Json.h +++ b/cocos/editor-support/spine/Json.h @@ -1,83 +1,113 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, dispose of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ - -/* Esoteric Software: Removed everything except parsing, shorter method names, more get methods, double to float, formatted. */ - -#ifndef SPINE_JSON_H_ -#define SPINE_JSON_H_ - -#ifdef __cplusplus -extern "C" { -#endif +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Json_h +#define Spine_Json_h -/* Json Types: */ -#define Json_False 0 -#define Json_True 1 -#define Json_NULL 2 -#define Json_Number 3 -#define Json_String 4 -#define Json_Array 5 -#define Json_Object 6 +#include #ifndef SPINE_JSON_HAVE_PREV -/* Spine doesn't use the "prev" link in the Json sibling lists. */ +/* spine doesn't use the "prev" link in the Json sibling lists. */ #define SPINE_JSON_HAVE_PREV 0 #endif -/* The Json structure: */ -typedef struct Json { - struct Json* next; +namespace spine { +class SP_API Json : public SpineObject { + friend class SkeletonJson; + +public: + /* Json Types: */ + static const int JSON_FALSE; + static const int JSON_TRUE; + static const int JSON_NULL; + static const int JSON_NUMBER; + static const int JSON_STRING; + static const int JSON_ARRAY; + static const int JSON_OBJECT; + + /* Get item "string" from object. Case insensitive. */ + static Json *getItem(Json *object, const char *string); + + static const char *getString(Json *object, const char *name, const char *defaultValue); + + static float getFloat(Json *object, const char *name, float defaultValue); + + static int getInt(Json *object, const char *name, int defaultValue); + + /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */ + static const char *getError(); + + /* Supply a block of JSON, and this returns a Json object you can interrogate. Call Json_dispose when finished. */ + explicit Json(const char *value); + + ~Json(); + + + +private: + static const char *_error; + + Json *_next; #if SPINE_JSON_HAVE_PREV - struct Json* prev; /* next/prev allow you to walk array/object chains. Alternatively, use getSize/getItem */ + Json* _prev; /* next/prev allow you to walk array/object chains. Alternatively, use getSize/getItem */ #endif - struct Json* child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + Json *_child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int _type; /* The type of the item, as above. */ + int _size; /* The number of children. */ - int type; /* The type of the item, as above. */ - int size; /* The number of children. */ + const char *_valueString; /* The item's string, if type==JSON_STRING */ + int _valueInt; /* The item's number, if type==JSON_NUMBER */ + float _valueFloat; /* The item's number, if type==JSON_NUMBER */ - const char* valueString; /* The item's string, if type==Json_String */ - int valueInt; /* The item's number, if type==Json_Number */ - float valueFloat; /* The item's number, if type==Json_Number */ + const char *_name; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ - const char* name; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ -} Json; + /* Utility to jump whitespace and cr/lf */ + static const char *skip(const char *inValue); -/* Supply a block of JSON, and this returns a Json object you can interrogate. Call Json_dispose when finished. */ -Json* Json_create (const char* value); + /* Parser core - when encountering text, process appropriately. */ + static const char *parseValue(Json *item, const char *value); -/* Delete a Json entity and all subentities. */ -void Json_dispose (Json* json); + /* Parse the input text into an unescaped cstring, and populate item. */ + static const char *parseString(Json *item, const char *str); -/* Get item "string" from object. Case insensitive. */ -Json* Json_getItem (Json* json, const char* string); -const char* Json_getString (Json* json, const char* name, const char* defaultValue); -float Json_getFloat (Json* json, const char* name, float defaultValue); -int Json_getInt (Json* json, const char* name, int defaultValue); + /* Parse the input text to generate a number, and populate the result into item. */ + static const char *parseNumber(Json *item, const char *num); -/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */ -const char* Json_getError (void); + /* Build an array from input text. */ + static const char *parseArray(Json *item, const char *value); -#ifdef __cplusplus + /* Build an object from the text. */ + static const char *parseObject(Json *item, const char *value); + + static int json_strcasecmp(const char *s1, const char *s2); +}; } -#endif -#endif /* SPINE_JSON_H_ */ +#endif /* Spine_Json_h */ diff --git a/cocos/editor-support/spine/LinkedMesh.cpp b/cocos/editor-support/spine/LinkedMesh.cpp new file mode 100644 index 000000000000..b6edb261c9cd --- /dev/null +++ b/cocos/editor-support/spine/LinkedMesh.cpp @@ -0,0 +1,45 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +using namespace spine; + +LinkedMesh::LinkedMesh(MeshAttachment *mesh, const String &skin, size_t slotIndex, const String &parent) : + _mesh(mesh), + _skin(skin), + _slotIndex(slotIndex), + _parent(parent) { +} diff --git a/cocos/editor-support/spine/LinkedMesh.h b/cocos/editor-support/spine/LinkedMesh.h new file mode 100644 index 000000000000..4776c0bb028c --- /dev/null +++ b/cocos/editor-support/spine/LinkedMesh.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_LinkedMesh_h +#define Spine_LinkedMesh_h + +#include +#include + +namespace spine { +class MeshAttachment; + +class SP_API LinkedMesh : public SpineObject { + friend class SkeletonBinary; + + friend class SkeletonJson; + +public: + LinkedMesh(MeshAttachment *mesh, const String &skin, size_t slotIndex, const String &parent); + +private: + MeshAttachment *_mesh; + String _skin; + size_t _slotIndex; + String _parent; +}; +} + +#endif /* Spine_LinkedMesh_h */ diff --git a/cocos/editor-support/spine/MathUtil.cpp b/cocos/editor-support/spine/MathUtil.cpp new file mode 100644 index 000000000000..1d007ed35403 --- /dev/null +++ b/cocos/editor-support/spine/MathUtil.cpp @@ -0,0 +1,127 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include +#include +#include + +// Required for division by 0 in _isNaN on MSVC +#ifdef _MSC_VER +#pragma warning(disable:4723) +#endif + +using namespace spine; + +const float MathUtil::Pi = 3.1415926535897932385f; +const float MathUtil::Pi_2 = 3.1415926535897932385f * 2; +const float MathUtil::Deg_Rad = (3.1415926535897932385f / 180.0f); +const float MathUtil::Rad_Deg = (180.0f / 3.1415926535897932385f); + +float MathUtil::abs(float v) { + return ((v) < 0 ? -(v) : (v)); +} + +float MathUtil::sign(float v) { + return ((v) < 0 ? -1.0f : (v) > 0 ? 1.0f : 0.0f); +} + +float MathUtil::clamp(float x, float min, float max) { + return ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))); +} + +float MathUtil::fmod(float a, float b) { + return (float)::fmod(a, b); +} + +/// Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323 +/// degrees), largest error of 0.00488 radians (0.2796 degrees). +float MathUtil::atan2(float y, float x) { + return (float)::atan2(y, x); +} + +/// Returns the cosine in radians from a lookup table. +float MathUtil::cos(float radians) { + return (float)::cos(radians); +} + +/// Returns the sine in radians from a lookup table. +float MathUtil::sin(float radians) { + return (float)::sin(radians); +} + +float MathUtil::sqrt(float v) { + return (float)::sqrt(v); +} + +float MathUtil::acos(float v) { + return (float)::acos(v); +} + +/// Returns the sine in radians from a lookup table. +float MathUtil::sinDeg(float degrees) { + return (float)::sin(degrees * MathUtil::Deg_Rad); +} + +/// Returns the cosine in radians from a lookup table. +float MathUtil::cosDeg(float degrees) { + return (float)::cos(degrees * MathUtil::Deg_Rad); +} + +/* Need to pass 0 as an argument, so VC++ doesn't error with C2124 */ +static bool _isNan(float value, float zero) { + float _nan = (float) 0.0 / zero; + return 0 == memcmp((void *) &value, (void *) &_nan, sizeof(value)); +} + +bool MathUtil::isNan(float v) { + return _isNan(v, 0); +} + +float MathUtil::random() { + return ::rand() / (float)RAND_MAX; +} + +float MathUtil::randomTriangular(float min, float max) { + return randomTriangular(min, max, (min + max) * 0.5f); +} + +float MathUtil::randomTriangular(float min, float max, float mode) { + float u = random(); + float d = max - min; + if (u <= (mode - min) / d) return min + sqrt(u * d * (mode - min)); + return max - sqrt((1 - u) * d * (max - mode)); +} + +float MathUtil::pow(float a, float b) { + return (float)::pow(a, b); +} diff --git a/cocos/editor-support/spine/MathUtil.h b/cocos/editor-support/spine/MathUtil.h new file mode 100644 index 000000000000..2d8b4cf42844 --- /dev/null +++ b/cocos/editor-support/spine/MathUtil.h @@ -0,0 +1,129 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_MathUtil_h +#define Spine_MathUtil_h + +#include + +#include + +namespace spine { + +class SP_API MathUtil : public SpineObject { +private: + MathUtil(); + +public: + static const float Pi; + static const float Pi_2; + static const float Deg_Rad; + static const float Rad_Deg; + + template + static inline T min(T a, T b) { return a < b ? a : b; } + + template + static inline T max(T a, T b) { return a > b ? a : b; } + + static float sign(float val); + + static float clamp(float x, float lower, float upper); + + static float abs(float v); + + /// Returns the sine in radians from a lookup table. + static float sin(float radians); + + /// Returns the cosine in radians from a lookup table. + static float cos(float radians); + + /// Returns the sine in radians from a lookup table. + static float sinDeg(float degrees); + + /// Returns the cosine in radians from a lookup table. + static float cosDeg(float degrees); + + /// Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323 + /// degrees), largest error of 0.00488 radians (0.2796 degrees). + static float atan2(float y, float x); + + static float acos(float v); + + static float sqrt(float v); + + static float fmod(float a, float b); + + static bool isNan(float v); + + static float random(); + + static float randomTriangular(float min, float max); + + static float randomTriangular(float min, float max, float mode); + + static float pow(float a, float b); +}; + +struct SP_API Interpolation { + virtual float apply(float a) = 0; + + virtual float interpolate(float start, float end, float a) { + return start + (end - start) * apply(a); + } + + virtual ~Interpolation() {}; +}; + +struct SP_API PowInterpolation: public Interpolation { + PowInterpolation(int power): power(power) { + } + + float apply(float a) { + if (a <= 0.5f) return MathUtil::pow(a * 2.0f, (float)power) / 2.0f; + return MathUtil::pow((a - 1.0f) * 2.0f, (float)power) / (power % 2 == 0 ? -2.0f : 2.0f) + 1.0f; + } + + int power; +}; + +struct SP_API PowOutInterpolation: public Interpolation { + PowOutInterpolation(int power): power(power) { + } + + float apply(float a) { + return MathUtil::pow(a - 1, (float)power) * (power % 2 == 0 ? -1.0f : 1.0f) + 1.0f; + } + + int power; +}; + +} + +#endif /* Spine_MathUtil_h */ diff --git a/cocos/editor-support/spine/MeshAttachment.cpp b/cocos/editor-support/spine/MeshAttachment.cpp new file mode 100644 index 000000000000..558e88a52e8e --- /dev/null +++ b/cocos/editor-support/spine/MeshAttachment.cpp @@ -0,0 +1,263 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include + +using namespace spine; + +RTTI_IMPL(MeshAttachment, VertexAttachment) + +MeshAttachment::MeshAttachment(const String &name) : VertexAttachment(name), HasRendererObject(), + _regionOffsetX(0), + _regionOffsetY(0), + _regionWidth(0), + _regionHeight(0), + _regionOriginalWidth(0), + _regionOriginalHeight(0), + _parentMesh(NULL), + _path(), + _regionU(0), + _regionV(0), + _regionU2(0), + _regionV2(0), + _width(0), + _height(0), + _color(1, 1, 1, 1), + _hullLength(0), + _inheritDeform(false), + _regionRotate(false) { +} + +MeshAttachment::~MeshAttachment() {} + +void MeshAttachment::updateUVs() { + if (_uvs.size() != _regionUVs.size()) { + _uvs.setSize(_regionUVs.size(), 0); + } + + if (_regionRotate) { + float textureHeight = _regionWidth / (_regionV2 - _regionV); + float textureWidth = _regionHeight / (_regionU2 - _regionU); + float u = _regionU - (_regionOriginalHeight - _regionOffsetY - _regionHeight) / textureWidth; + float v = _regionV - (_regionOriginalWidth - _regionOffsetX - _regionWidth) / textureHeight; + float width = _regionOriginalHeight / textureWidth; + float height = _regionOriginalWidth / textureHeight; + for (size_t i = 0, n = _uvs.size(); i < n; i += 2) { + _uvs[i] = u + _regionUVs[i + 1] * width; + _uvs[i + 1] = v + height - _regionUVs[i] * height; + } + } else { + float textureWidth = _regionWidth / (_regionU2 - _regionU); + float textureHeight = _regionHeight / (_regionV2 - _regionV); + float u = _regionU - _regionOffsetX / textureWidth; + float v = _regionV - (_regionOriginalHeight - _regionOffsetY - _regionHeight) / textureHeight; + float width = _regionOriginalWidth / textureWidth; + float height = _regionOriginalHeight / textureHeight; + for (size_t i = 0, n = _uvs.size(); i < n; i += 2) { + _uvs[i] = u + _regionUVs[i] * width; + _uvs[i + 1] = v + _regionUVs[i + 1] * height; + } + } +} + +bool MeshAttachment::applyDeform(VertexAttachment *sourceAttachment) { + return this == sourceAttachment || (_inheritDeform && _parentMesh == sourceAttachment); +} + +int MeshAttachment::getHullLength() { + return _hullLength; +} + +void MeshAttachment::setHullLength(int inValue) { + _hullLength = inValue; +} + +Vector &MeshAttachment::getRegionUVs() { + return _regionUVs; +} + +Vector &MeshAttachment::getUVs() { + return _uvs; +} + +Vector &MeshAttachment::getTriangles() { + return _triangles; +} + +const String &MeshAttachment::getPath() { + return _path; +} + +void MeshAttachment::setPath(const String &inValue) { + _path = inValue; +} + +float MeshAttachment::getRegionU() { + return _regionU; +} + +void MeshAttachment::setRegionU(float inValue) { + _regionU = inValue; +} + +float MeshAttachment::getRegionV() { + return _regionV; +} + +void MeshAttachment::setRegionV(float inValue) { + _regionV = inValue; +} + +float MeshAttachment::getRegionU2() { + return _regionU2; +} + +void MeshAttachment::setRegionU2(float inValue) { + _regionU2 = inValue; +} + +float MeshAttachment::getRegionV2() { + return _regionV2; +} + +void MeshAttachment::setRegionV2(float inValue) { + _regionV2 = inValue; +} + +bool MeshAttachment::getRegionRotate() { + return _regionRotate; +} + +void MeshAttachment::setRegionRotate(bool inValue) { + _regionRotate = inValue; +} + +float MeshAttachment::getRegionOffsetX() { + return _regionOffsetX; +} + +void MeshAttachment::setRegionOffsetX(float inValue) { + _regionOffsetX = inValue; +} + +float MeshAttachment::getRegionOffsetY() { + return _regionOffsetY; +} + +void MeshAttachment::setRegionOffsetY(float inValue) { + _regionOffsetY = inValue; +} + +float MeshAttachment::getRegionWidth() { + return _regionWidth; +} + +void MeshAttachment::setRegionWidth(float inValue) { + _regionWidth = inValue; +} + +float MeshAttachment::getRegionHeight() { + return _regionHeight; +} + +void MeshAttachment::setRegionHeight(float inValue) { + _regionHeight = inValue; +} + +float MeshAttachment::getRegionOriginalWidth() { + return _regionOriginalWidth; +} + +void MeshAttachment::setRegionOriginalWidth(float inValue) { + _regionOriginalWidth = inValue; +} + +float MeshAttachment::getRegionOriginalHeight() { + return _regionOriginalHeight; +} + +void MeshAttachment::setRegionOriginalHeight(float inValue) { + _regionOriginalHeight = inValue; +} + +bool MeshAttachment::getInheritDeform() { + return _inheritDeform; +} + +void MeshAttachment::setInheritDeform(bool inValue) { + _inheritDeform = inValue; +} + +MeshAttachment *MeshAttachment::getParentMesh() { + return _parentMesh; +} + +void MeshAttachment::setParentMesh(MeshAttachment *inValue) { + _parentMesh = inValue; + if (inValue != NULL) { + _bones.clearAndAddAll(inValue->_bones); + _vertices.clearAndAddAll(inValue->_vertices); + _worldVerticesLength = inValue->_worldVerticesLength; + _regionUVs.clearAndAddAll(inValue->_regionUVs); + _triangles.clearAndAddAll(inValue->_triangles); + _hullLength = inValue->_hullLength; + _edges.clearAndAddAll(inValue->_edges); + _width = inValue->_width; + _height = inValue->_height; + } +} + +Vector &MeshAttachment::getEdges() { + return _edges; +} + +float MeshAttachment::getWidth() { + return _width; +} + +void MeshAttachment::setWidth(float inValue) { + _width = inValue; +} + +float MeshAttachment::getHeight() { + return _height; +} + +void MeshAttachment::setHeight(float inValue) { + _height = inValue; +} + +spine::Color &MeshAttachment::getColor() { + return _color; +} diff --git a/cocos/editor-support/spine/MeshAttachment.h b/cocos/editor-support/spine/MeshAttachment.h index 20a7e75ac44c..c44790a78837 100644 --- a/cocos/editor-support/spine/MeshAttachment.h +++ b/cocos/editor-support/spine/MeshAttachment.h @@ -1,91 +1,141 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_MESHATTACHMENT_H_ -#define SPINE_MESHATTACHMENT_H_ +#ifndef Spine_MeshAttachment_h +#define Spine_MeshAttachment_h -#include -#include #include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spMeshAttachment spMeshAttachment; -struct spMeshAttachment { - spVertexAttachment super; - - void* rendererObject; - int regionOffsetX, regionOffsetY; /* Pixels stripped from the bottom left, unrotated. */ - int regionWidth, regionHeight; /* Unrotated, stripped pixel size. */ - int regionOriginalWidth, regionOriginalHeight; /* Unrotated, unstripped pixel size. */ - float regionU, regionV, regionU2, regionV2; - int/*bool*/regionRotate; - - const char* path; - - float* regionUVs; - float* uvs; - - int trianglesCount; - unsigned short* triangles; - - spColor color; - - int hullLength; - - spMeshAttachment* const parentMesh; - int/*bool*/inheritDeform; - - /* Nonessential. */ - int edgesCount; - int* edges; - float width, height; -}; - -SP_API spMeshAttachment* spMeshAttachment_create (const char* name); -SP_API void spMeshAttachment_updateUVs (spMeshAttachment* self); -SP_API void spMeshAttachment_setParentMesh (spMeshAttachment* self, spMeshAttachment* parentMesh); - -#ifdef SPINE_SHORT_NAMES -typedef spMeshAttachment MeshAttachment; -#define MeshAttachment_create(...) spMeshAttachment_create(__VA_ARGS__) -#define MeshAttachment_updateUVs(...) spMeshAttachment_updateUVs(__VA_ARGS__) -#define MeshAttachment_setParentMesh(...) spMeshAttachment_setParentMesh(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#include +#include +#include + +namespace spine { + /// Attachment that displays a texture region using a mesh. + class SP_API MeshAttachment : public VertexAttachment, public HasRendererObject { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class AtlasAttachmentLoader; + + RTTI_DECL + + public: + explicit MeshAttachment(const String& name); + + virtual ~MeshAttachment(); + + void updateUVs(); + + virtual bool applyDeform(VertexAttachment* sourceAttachment); + + int getHullLength(); + void setHullLength(int inValue); + + Vector& getRegionUVs(); + + /// The UV pair for each vertex, normalized within the entire texture. See also MeshAttachment::updateUVs + Vector& getUVs(); + + Vector& getTriangles(); + + Color& getColor(); + + const String& getPath(); + void setPath(const String& inValue); + + float getRegionU(); + void setRegionU(float inValue); + + float getRegionV(); + void setRegionV(float inValue); + + float getRegionU2(); + void setRegionU2(float inValue); + + float getRegionV2(); + void setRegionV2(float inValue); + + bool getRegionRotate(); + void setRegionRotate(bool inValue); + + float getRegionOffsetX(); + void setRegionOffsetX(float inValue); + + // Pixels stripped from the bottom left, unrotated. + float getRegionOffsetY(); + void setRegionOffsetY(float inValue); + + float getRegionWidth(); + void setRegionWidth(float inValue); + + // Unrotated, stripped size. + float getRegionHeight(); + void setRegionHeight(float inValue); + + float getRegionOriginalWidth(); + void setRegionOriginalWidth(float inValue); + + // Unrotated, unstripped size. + float getRegionOriginalHeight(); + void setRegionOriginalHeight(float inValue); + + bool getInheritDeform(); + void setInheritDeform(bool inValue); + + MeshAttachment* getParentMesh(); + void setParentMesh(MeshAttachment* inValue); + + // Nonessential. + Vector& getEdges(); + float getWidth(); + void setWidth(float inValue); + float getHeight(); + void setHeight(float inValue); + + private: + float _regionOffsetX, _regionOffsetY, _regionWidth, _regionHeight, _regionOriginalWidth, _regionOriginalHeight; + MeshAttachment* _parentMesh; + Vector _uvs; + Vector _regionUVs; + Vector _triangles; + Vector _edges; + String _path; + float _regionU; + float _regionV; + float _regionU2; + float _regionV2; + float _width; + float _height; + Color _color; + int _hullLength; + bool _inheritDeform; + bool _regionRotate; + }; } -#endif -#endif /* SPINE_MESHATTACHMENT_H_ */ +#endif /* Spine_MeshAttachment_h */ diff --git a/cocos/editor-support/spine/MixBlend.h b/cocos/editor-support/spine/MixBlend.h new file mode 100644 index 000000000000..fa9879d7bc32 --- /dev/null +++ b/cocos/editor-support/spine/MixBlend.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_MixPose_h +#define Spine_MixPose_h + +namespace spine { +/// +/// Controls how a timeline is mixed with the setup or current pose. +/// See also Timeline::apply(Skeleton&, float, float, Vector&, float, Blend, MixDirection) +enum MixBlend { + MixBlend_Setup = 0, + MixBlend_First, + MixBlend_Replace, + MixBlend_Add +}; +} + +#endif /* Spine_MixPose_h */ diff --git a/cocos/editor-support/spine/MixDirection.h b/cocos/editor-support/spine/MixDirection.h new file mode 100644 index 000000000000..8a313a610242 --- /dev/null +++ b/cocos/editor-support/spine/MixDirection.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_MixDirection_h +#define Spine_MixDirection_h + +namespace spine { +/// +/// Indicates whether a timeline's alpha is mixing out over time toward 0 (the setup or current pose) or mixing in toward 1 (the timeline's pose). +/// See also Timeline::apply(Skeleton&, float, float, Vector&, float, MixPose, MixDirection) +enum MixDirection { + MixDirection_In = 0, + MixDirection_Out +}; +} + +#endif /* Spine_MixDirection_h */ diff --git a/cocos/editor-support/spine/PathAttachment.cpp b/cocos/editor-support/spine/PathAttachment.cpp new file mode 100644 index 000000000000..f3bd42670c27 --- /dev/null +++ b/cocos/editor-support/spine/PathAttachment.cpp @@ -0,0 +1,60 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +using namespace spine; + +RTTI_IMPL(PathAttachment, VertexAttachment) + +PathAttachment::PathAttachment(const String &name) : VertexAttachment(name), _closed(false), _constantSpeed(false) { +} + +Vector &PathAttachment::getLengths() { + return _lengths; +} + +bool PathAttachment::isClosed() { + return _closed; +} + +void PathAttachment::setClosed(bool inValue) { + _closed = inValue; +} + +bool PathAttachment::isConstantSpeed() { + return _constantSpeed; +} + +void PathAttachment::setConstantSpeed(bool inValue) { + _constantSpeed = inValue; +} diff --git a/cocos/editor-support/spine/PathAttachment.h b/cocos/editor-support/spine/PathAttachment.h index 6584733bd3a1..1060d3d835f0 100644 --- a/cocos/editor-support/spine/PathAttachment.h +++ b/cocos/editor-support/spine/PathAttachment.h @@ -1,63 +1,59 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_PATHATTACHMENT_H_ -#define SPINE_PATHATTACHMENT_H_ +#ifndef Spine_PathAttachment_h +#define Spine_PathAttachment_h -#include -#include #include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spPathAttachment { - spVertexAttachment super; - int lengthsLength; - float* lengths; - int/*bool*/ closed, constantSpeed; -} spPathAttachment; - -SP_API spPathAttachment* spPathAttachment_create (const char* name); - -#ifdef SPINE_SHORT_NAMES -typedef spPathAttachment PathAttachment; -#define PathAttachment_create(...) spPathAttachment_create(__VA_ARGS__) -#define PathAttachment_computeWorldVertices(...) spPathAttachment_computeWorldVertices(__VA_ARGS__) -#endif -#ifdef __cplusplus +namespace spine { + class SP_API PathAttachment : public VertexAttachment { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + explicit PathAttachment(const String& name); + + /// The length in the setup pose from the start of the path to the end of each curve. + Vector& getLengths(); + bool isClosed(); + void setClosed(bool inValue); + bool isConstantSpeed(); + void setConstantSpeed(bool inValue); + + private: + Vector _lengths; + bool _closed; + bool _constantSpeed; + }; } -#endif -#endif /* SPINE_PATHATTACHMENT_H_ */ +#endif /* Spine_PathAttachment_h */ diff --git a/cocos/editor-support/spine/PathConstraint.cpp b/cocos/editor-support/spine/PathConstraint.cpp new file mode 100644 index 000000000000..7e87b8d64d56 --- /dev/null +++ b/cocos/editor-support/spine/PathConstraint.cpp @@ -0,0 +1,570 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include +#include +#include +#include + +#include +#include + +using namespace spine; + +RTTI_IMPL(PathConstraint, Constraint) + +const float PathConstraint::EPSILON = 0.00001f; +const int PathConstraint::NONE = -1; +const int PathConstraint::BEFORE = -2; +const int PathConstraint::AFTER = -3; + +PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : Constraint(), + _data(data), + _target(skeleton.findSlot( + data.getTarget()->getName())), + _position(data.getPosition()), + _spacing(data.getSpacing()), + _rotateMix(data.getRotateMix()), + _translateMix(data.getTranslateMix()) { + _bones.ensureCapacity(_data.getBones().size()); + for (size_t i = 0; i < _data.getBones().size(); i++) { + BoneData *boneData = _data.getBones()[i]; + _bones.add(skeleton.findBone(boneData->getName())); + } + + _segments.setSize(10, 0); +} + +void PathConstraint::apply() { + update(); +} + +void PathConstraint::update() { + Attachment *baseAttachment = _target->getAttachment(); + if (baseAttachment == NULL || !baseAttachment->getRTTI().instanceOf(PathAttachment::rtti)) { + return; + } + + PathAttachment *attachment = static_cast(baseAttachment); + + float rotateMix = _rotateMix; + float translateMix = _translateMix; + bool translate = translateMix > 0; + bool rotate = rotateMix > 0; + if (!translate && !rotate) { + return; + } + + PathConstraintData &data = _data; + bool percentSpacing = data._spacingMode == SpacingMode_Percent; + RotateMode rotateMode = data._rotateMode; + bool tangents = rotateMode == RotateMode_Tangent, scale = rotateMode == RotateMode_ChainScale; + size_t boneCount = _bones.size(); + size_t spacesCount = tangents ? boneCount : boneCount + 1; + _spaces.setSize(spacesCount, 0); + float spacing = _spacing; + if (scale || !percentSpacing) { + if (scale) { + _lengths.setSize(boneCount, 0); + } + bool lengthSpacing = data._spacingMode == SpacingMode_Length; + + for (size_t i = 0, n = spacesCount - 1; i < n;) { + Bone *boneP = _bones[i]; + Bone &bone = *boneP; + float setupLength = bone._data.getLength(); + if (setupLength < PathConstraint::EPSILON) { + if (scale) { + _lengths[i] = 0; + } + _spaces[++i] = 0; + } else if (percentSpacing) { + if (scale) { + float x = setupLength * bone._a, y = setupLength * bone._c; + float length = MathUtil::sqrt(x * x + y * y); + _lengths[i] = length; + } + _spaces[++i] = spacing; + } else { + float x = setupLength * bone._a; + float y = setupLength * bone._c; + float length = MathUtil::sqrt(x * x + y * y); + if (scale) { + _lengths[i] = length; + } + + _spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength; + } + } + } else { + for (size_t i = 1; i < spacesCount; ++i) { + _spaces[i] = spacing; + } + } + + Vector& positions = computeWorldPositions(*attachment, spacesCount, tangents, + data.getPositionMode() == PositionMode_Percent, + percentSpacing); + float boneX = positions[0]; + float boneY = positions[1]; + float offsetRotation = data.getOffsetRotation(); + bool tip; + if (offsetRotation == 0) { + tip = rotateMode == RotateMode_Chain; + } else { + tip = false; + Bone &p = _target->getBone(); + offsetRotation *= p.getA() * p.getD() - p.getB() * p.getC() > 0 ? MathUtil::Deg_Rad : -MathUtil::Deg_Rad; + } + + for (size_t i = 0, p = 3; i < boneCount; i++, p += 3) { + Bone *boneP = _bones[i]; + Bone &bone = *boneP; + bone._worldX += (boneX - bone._worldX) * translateMix; + bone._worldY += (boneY - bone._worldY) * translateMix; + float x = positions[p]; + float y = positions[p + 1]; + float dx = x - boneX; + float dy = y - boneY; + if (scale) { + float length = _lengths[i]; + if (length >= PathConstraint::EPSILON) { + float s = (MathUtil::sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1; + bone._a *= s; + bone._c *= s; + } + } + + boneX = x; + boneY = y; + + if (rotate) { + float a = bone._a, b = bone._b, c = bone._c, d = bone._d, r, cos, sin; + if (tangents) { + r = positions[p - 1]; + } else if (_spaces[i + 1] < PathConstraint::EPSILON) { + r = positions[p + 2]; + } else { + r = MathUtil::atan2(dy, dx); + } + + r -= MathUtil::atan2(c, a); + + if (tip) { + cos = MathUtil::cos(r); + sin = MathUtil::sin(r); + float length = bone._data.getLength(); + boneX += (length * (cos * a - sin * c) - dx) * rotateMix; + boneY += (length * (sin * a + cos * c) - dy) * rotateMix; + } else { + r += offsetRotation; + } + + if (r > MathUtil::Pi) { + r -= MathUtil::Pi_2; + } else if (r < -MathUtil::Pi) { + r += MathUtil::Pi_2; + } + + r *= rotateMix; + cos = MathUtil::cos(r); + sin = MathUtil::sin(r); + bone._a = cos * a - sin * c; + bone._b = cos * b - sin * d; + bone._c = sin * a + cos * c; + bone._d = sin * b + cos * d; + } + + bone._appliedValid = false; + } +} + +int PathConstraint::getOrder() { + return _data.getOrder(); +} + +float PathConstraint::getPosition() { + return _position; +} + +void PathConstraint::setPosition(float inValue) { + _position = inValue; +} + +float PathConstraint::getSpacing() { + return _spacing; +} + +void PathConstraint::setSpacing(float inValue) { + _spacing = inValue; +} + +float PathConstraint::getRotateMix() { + return _rotateMix; +} + +void PathConstraint::setRotateMix(float inValue) { + _rotateMix = inValue; +} + +float PathConstraint::getTranslateMix() { + return _translateMix; +} + +void PathConstraint::setTranslateMix(float inValue) { + _translateMix = inValue; +} + +Vector &PathConstraint::getBones() { + return _bones; +} + +Slot *PathConstraint::getTarget() { + return _target; +} + +void PathConstraint::setTarget(Slot *inValue) { + _target = inValue; +} + +PathConstraintData &PathConstraint::getData() { + return _data; +} + +Vector& +PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, bool tangents, bool percentPosition, + bool percentSpacing) { + Slot &target = *_target; + float position = _position; + _positions.setSize(spacesCount * 3 + 2, 0); + Vector &out = _positions; + Vector &world = _world; + bool closed = path.isClosed(); + int verticesLength = path.getWorldVerticesLength(); + int curveCount = verticesLength / 6; + int prevCurve = NONE; + + float pathLength; + if (!path.isConstantSpeed()) { + Vector &lengths = path.getLengths(); + curveCount -= closed ? 1 : 2; + pathLength = lengths[curveCount]; + if (percentPosition) { + position *= pathLength; + } + + if (percentSpacing) { + for (int i = 1; i < spacesCount; ++i) { + _spaces[i] *= pathLength; + } + } + + world.setSize(8, 0); + for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { + float space = _spaces[i]; + position += space; + float p = position; + + if (closed) { + p = MathUtil::fmod(p, pathLength); + + if (p < 0) { + p += pathLength; + } + curve = 0; + } else if (p < 0) { + if (prevCurve != BEFORE) { + prevCurve = BEFORE; + path.computeWorldVertices(target, 2, 4, world, 0); + } + + addBeforePosition(p, world, 0, out, o); + + continue; + } else if (p > pathLength) { + if (prevCurve != AFTER) { + prevCurve = AFTER; + path.computeWorldVertices(target, verticesLength - 6, 4, world, 0); + } + + addAfterPosition(p - pathLength, world, 0, out, o); + + continue; + } + + // Determine curve containing position. + for (;; curve++) { + float length = lengths[curve]; + if (p > length) { + continue; + } + + if (curve == 0) { + p /= length; + } else { + float prev = lengths[curve - 1]; + p = (p - prev) / (length - prev); + } + break; + } + + if (curve != prevCurve) { + prevCurve = curve; + if (closed && curve == curveCount) { + path.computeWorldVertices(target, verticesLength - 4, 4, world, 0); + path.computeWorldVertices(target, 0, 4, world, 4); + } else { + path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0); + } + } + + addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], + out, o, tangents || (i > 0 && space < EPSILON)); + } + return out; + } + + // World vertices. + if (closed) { + verticesLength += 2; + world.setSize(verticesLength, 0); + path.computeWorldVertices(target, 2, verticesLength - 4, world, 0); + path.computeWorldVertices(target, 0, 2, world, verticesLength - 4); + world[verticesLength - 2] = world[0]; + world[verticesLength - 1] = world[1]; + } else { + curveCount--; + verticesLength -= 4; + world.setSize(verticesLength, 0); + path.computeWorldVertices(target, 2, verticesLength, world, 0); + } + + // Curve lengths. + _curves.setSize(curveCount, 0); + pathLength = 0; + float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; + float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy; + for (int i = 0, w = 2; i < curveCount; i++, w += 6) { + cx1 = world[w]; + cy1 = world[w + 1]; + cx2 = world[w + 2]; + cy2 = world[w + 3]; + x2 = world[w + 4]; + y2 = world[w + 5]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f; + pathLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + pathLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + pathLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + pathLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); + _curves[i] = pathLength; + x1 = x2; + y1 = y2; + } + + if (percentPosition) { + position *= pathLength; + } else { + position *= pathLength / path.getLengths()[curveCount - 1]; + } + + if (percentSpacing) { + for (int i = 1; i < spacesCount; ++i) { + _spaces[i] *= pathLength; + } + } + + float curveLength = 0; + for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { + float space = _spaces[i]; + position += space; + float p = position; + + if (closed) { + p = MathUtil::fmod(p, pathLength); + + if (p < 0) { + p += pathLength; + } + curve = 0; + } else if (p < 0) { + addBeforePosition(p, world, 0, out, o); + continue; + } else if (p > pathLength) { + addAfterPosition(p - pathLength, world, verticesLength - 4, out, o); + continue; + } + + // Determine curve containing position. + for (;; curve++) { + float length = _curves[curve]; + if (p > length) { + continue; + } + + if (curve == 0) { + p /= length; + } else { + float prev = _curves[curve - 1]; + p = (p - prev) / (length - prev); + } + break; + } + + // Curve segment lengths. + if (curve != prevCurve) { + prevCurve = curve; + int ii = curve * 6; + x1 = world[ii]; + y1 = world[ii + 1]; + cx1 = world[ii + 2]; + cy1 = world[ii + 3]; + cx2 = world[ii + 4]; + cy2 = world[ii + 5]; + x2 = world[ii + 6]; + y2 = world[ii + 7]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.03f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; + curveLength = MathUtil::sqrt(dfx * dfx + dfy * dfy); + _segments[0] = curveLength; + for (ii = 1; ii < 8; ii++) { + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + curveLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); + _segments[ii] = curveLength; + } + dfx += ddfx; + dfy += ddfy; + curveLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); + _segments[8] = curveLength; + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + curveLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); + _segments[9] = curveLength; + segment = 0; + } + + // Weight by segment length. + p *= curveLength; + for (;; segment++) { + float length = _segments[segment]; + if (p > length) { + continue; + } + + if (segment == 0) { + p /= length; + } else { + float prev = _segments[segment - 1]; + p = segment + (p - prev) / (length - prev); + } + break; + } + addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, + tangents || (i > 0 && space < EPSILON)); + } + + return out; +} + +void PathConstraint::addBeforePosition(float p, Vector &temp, int i, Vector &output, int o) { + float x1 = temp[i]; + float y1 = temp[i + 1]; + float dx = temp[i + 2] - x1; + float dy = temp[i + 3] - y1; + float r = MathUtil::atan2(dy, dx); + + output[o] = x1 + p * MathUtil::cos(r); + output[o + 1] = y1 + p * MathUtil::sin(r); + output[o + 2] = r; +} + +void PathConstraint::addAfterPosition(float p, Vector &temp, int i, Vector &output, int o) { + float x1 = temp[i + 2]; + float y1 = temp[i + 3]; + float dx = x1 - temp[i]; + float dy = y1 - temp[i + 1]; + float r = MathUtil::atan2(dy, dx); + output[o] = x1 + p * MathUtil::cos(r); + output[o + 1] = y1 + p * MathUtil::sin(r); + output[o + 2] = r; +} + +void PathConstraint::addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, + float y2, Vector &output, int o, bool tangents) { + if (p < EPSILON || MathUtil::isNan(p)) { + output[o] = x1; + output[o + 1] = y1; + output[o + 2] = MathUtil::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; + output[o] = x; + output[o + 1] = y; + if (tangents) { + if (p < 0.001) { + output[o + 2] = MathUtil::atan2(cy1 - y1, cx1 - x1); + } else { + output[o + 2] = MathUtil::atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), + x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); + } + } +} diff --git a/cocos/editor-support/spine/PathConstraint.h b/cocos/editor-support/spine/PathConstraint.h index d6c761eb3a05..e5ec1d2971e0 100644 --- a/cocos/editor-support/spine/PathConstraint.h +++ b/cocos/editor-support/spine/PathConstraint.h @@ -1,113 +1,109 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_PATHCONSTRAINT_H_ -#define SPINE_PATHCONSTRAINT_H_ - -#include -#include -#include -#include -#include "PathAttachment.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct spSkeleton; - -typedef struct spPathConstraint { - spPathConstraintData* const data; - int bonesCount; - spBone** const bones; - spSlot* target; - float position, spacing, rotateMix, translateMix; - - int spacesCount; - float* spaces; - - int positionsCount; - float* positions; - - int worldCount; - float* world; - - int curvesCount; - float* curves; - - int lengthsCount; - float* lengths; - - float segments[10]; - -#ifdef __cplusplus - spPathConstraint() : - data(0), - bonesCount(0), - bones(0), - target(0), - position(0), - spacing(0), - rotateMix(0), - translateMix(0), - spacesCount(0), - spaces(0), - positionsCount(0), - positions(0), - worldCount(0), - world(0), - curvesCount(0), - curves(0), - lengthsCount(0), - lengths(0) { - } -#endif -} spPathConstraint; - -#define SP_PATHCONSTRAINT_ - -SP_API spPathConstraint* spPathConstraint_create (spPathConstraintData* data, const struct spSkeleton* skeleton); -SP_API void spPathConstraint_dispose (spPathConstraint* self); - -SP_API void spPathConstraint_apply (spPathConstraint* self); -SP_API float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAttachment* path, int spacesCount, int/*bool*/ tangents, int/*bool*/percentPosition, int/**/percentSpacing); - -#ifdef SPINE_SHORT_NAMES -typedef spPathConstraint PathConstraint; -#define PathConstraint_create(...) spPathConstraint_create(__VA_ARGS__) -#define PathConstraint_dispose(...) spPathConstraint_dispose(__VA_ARGS__) -#define PathConstraint_apply(...) spPathConstraint_apply(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#ifndef Spine_PathConstraint_h +#define Spine_PathConstraint_h + +#include + +#include + +namespace spine { + class PathConstraintData; + class Skeleton; + class PathAttachment; + class Bone; + class Slot; + + class SP_API PathConstraint : public Constraint { + friend class Skeleton; + friend class PathConstraintMixTimeline; + friend class PathConstraintPositionTimeline; + friend class PathConstraintSpacingTimeline; + + RTTI_DECL + + public: + PathConstraint(PathConstraintData& data, Skeleton& skeleton); + + /// Applies the constraint to the constrained bones. + void apply(); + + virtual void update(); + + virtual int getOrder(); + + float getPosition(); + void setPosition(float inValue); + + float getSpacing(); + void setSpacing(float inValue); + + float getRotateMix(); + void setRotateMix(float inValue); + + float getTranslateMix(); + void setTranslateMix(float inValue); + + Vector& getBones(); + + Slot* getTarget(); + void setTarget(Slot* inValue); + + PathConstraintData& getData(); + + private: + static const float EPSILON; + static const int NONE; + static const int BEFORE; + static const int AFTER; + + PathConstraintData& _data; + Vector _bones; + Slot* _target; + float _position, _spacing, _rotateMix, _translateMix; + + Vector _spaces; + Vector _positions; + Vector _world; + Vector _curves; + Vector _lengths; + Vector _segments; + + Vector& computeWorldPositions(PathAttachment& path, int spacesCount, bool tangents, bool percentPosition, bool percentSpacing); + + static void addBeforePosition(float p, Vector& temp, int i, Vector& output, int o); + + static void addAfterPosition(float p, Vector& temp, int i, Vector& output, int o); + + static void addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, Vector& output, int o, bool tangents); + }; } -#endif -#endif /* SPINE_PATHCONSTRAINT_H_ */ +#endif /* Spine_PathConstraint_h */ diff --git a/cocos/editor-support/spine/PathConstraintData.cpp b/cocos/editor-support/spine/PathConstraintData.cpp new file mode 100644 index 000000000000..72a9a714ce43 --- /dev/null +++ b/cocos/editor-support/spine/PathConstraintData.cpp @@ -0,0 +1,144 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include + +using namespace spine; + +PathConstraintData::PathConstraintData(const String &name) : + _name(name), + _order(0), + _target(NULL), + _positionMode(PositionMode_Fixed), + _spacingMode(SpacingMode_Length), + _rotateMode(RotateMode_Tangent), + _offsetRotation(0), + _position(0), + _spacing(0), + _rotateMix(0), + _translateMix(0) { + assert(_name.length() > 0); +} + +const String &PathConstraintData::getName() { + return _name; +} + +int PathConstraintData::getOrder() { + return _order; +} + +void PathConstraintData::setOrder(int inValue) { + _order = inValue; +} + +Vector &PathConstraintData::getBones() { + return _bones; +} + +SlotData *PathConstraintData::getTarget() { + return _target; +} + +void PathConstraintData::setTarget(SlotData *inValue) { + _target = inValue; +} + +PositionMode PathConstraintData::getPositionMode() { + return _positionMode; +} + +void PathConstraintData::setPositionMode(PositionMode inValue) { + _positionMode = inValue; +} + +SpacingMode PathConstraintData::getSpacingMode() { + return _spacingMode; +} + +void PathConstraintData::setSpacingMode(SpacingMode inValue) { + _spacingMode = inValue; +} + +RotateMode PathConstraintData::getRotateMode() { + return _rotateMode; +} + +void PathConstraintData::setRotateMode(RotateMode inValue) { + _rotateMode = inValue; +} + +float PathConstraintData::getOffsetRotation() { + return _offsetRotation; +} + +void PathConstraintData::setOffsetRotation(float inValue) { + _offsetRotation = inValue; +} + +float PathConstraintData::getPosition() { + return _position; +} + +void PathConstraintData::setPosition(float inValue) { + _position = inValue; +} + +float PathConstraintData::getSpacing() { + return _spacing; +} + +void PathConstraintData::setSpacing(float inValue) { + _spacing = inValue; +} + +float PathConstraintData::getRotateMix() { + return _rotateMix; +} + +void PathConstraintData::setRotateMix(float inValue) { + _rotateMix = inValue; +} + +float PathConstraintData::getTranslateMix() { + return _translateMix; +} + +void PathConstraintData::setTranslateMix(float inValue) { + _translateMix = inValue; +} diff --git a/cocos/editor-support/spine/PathConstraintData.h b/cocos/editor-support/spine/PathConstraintData.h index 0c515dbea202..0fa4bcf8f2e8 100644 --- a/cocos/editor-support/spine/PathConstraintData.h +++ b/cocos/editor-support/spine/PathConstraintData.h @@ -1,97 +1,104 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_PATHCONSTRAINTDATA_H_ -#define SPINE_PATHCONSTRAINTDATA_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - SP_POSITION_MODE_FIXED, SP_POSITION_MODE_PERCENT -} spPositionMode; - -typedef enum { - SP_SPACING_MODE_LENGTH, SP_SPACING_MODE_FIXED, SP_SPACING_MODE_PERCENT -} spSpacingMode; - -typedef enum { - SP_ROTATE_MODE_TANGENT, SP_ROTATE_MODE_CHAIN, SP_ROTATE_MODE_CHAIN_SCALE -} spRotateMode; - -typedef struct spPathConstraintData { - const char* const name; - int order; - int bonesCount; - spBoneData** const bones; - spSlotData* target; - spPositionMode positionMode; - spSpacingMode spacingMode; - spRotateMode rotateMode; - float offsetRotation; - float position, spacing, rotateMix, translateMix; +#ifndef Spine_PathConstraintData_h +#define Spine_PathConstraintData_h -#ifdef __cplusplus - spPathConstraintData() : - name(0), - bonesCount(0), - bones(0), - target(0), - positionMode(SP_POSITION_MODE_FIXED), - spacingMode(SP_SPACING_MODE_LENGTH), - rotateMode(SP_ROTATE_MODE_TANGENT), - offsetRotation(0), - position(0), - spacing(0), - rotateMix(0), - translateMix(0) { - } -#endif -} spPathConstraintData; +#include +#include +#include +#include +#include +#include -SP_API spPathConstraintData* spPathConstraintData_create (const char* name); -SP_API void spPathConstraintData_dispose (spPathConstraintData* self); +namespace spine { + class BoneData; + class SlotData; + + class SP_API PathConstraintData : public SpineObject { + friend class SkeletonBinary; + friend class SkeletonJson; + + friend class PathConstraint; + friend class Skeleton; + friend class PathConstraintMixTimeline; + friend class PathConstraintPositionTimeline; + friend class PathConstraintSpacingTimeline; + + public: + explicit PathConstraintData(const String& name); -#ifdef SPINE_SHORT_NAMES -typedef spPathConstraintData PathConstraintData; -#define PathConstraintData_create(...) spPathConstraintData_create(__VA_ARGS__) -#define PathConstraintData_dispose(...) spPathConstraintData_dispose(__VA_ARGS__) -#endif + const String& getName(); + + int getOrder(); + void setOrder(int inValue); + + Vector& getBones(); + + SlotData* getTarget(); + void setTarget(SlotData* inValue); + + PositionMode getPositionMode(); + void setPositionMode(PositionMode inValue); + + SpacingMode getSpacingMode(); + void setSpacingMode(SpacingMode inValue); + + RotateMode getRotateMode(); + void setRotateMode(RotateMode inValue); + + float getOffsetRotation(); + void setOffsetRotation(float inValue); + + float getPosition(); + void setPosition(float inValue); + + float getSpacing(); + void setSpacing(float inValue); + + float getRotateMix(); + void setRotateMix(float inValue); + + float getTranslateMix(); + void setTranslateMix(float inValue); -#ifdef __cplusplus + private: + const String _name; + int _order; + Vector _bones; + SlotData* _target; + PositionMode _positionMode; + SpacingMode _spacingMode; + RotateMode _rotateMode; + float _offsetRotation; + float _position, _spacing, _rotateMix, _translateMix; + }; } -#endif -#endif /* SPINE_PATHCONSTRAINTDATA_H_ */ +#endif /* Spine_PathConstraintData_h */ diff --git a/cocos/editor-support/spine/PathConstraintMixTimeline.cpp b/cocos/editor-support/spine/PathConstraintMixTimeline.cpp new file mode 100644 index 000000000000..fea4015f6436 --- /dev/null +++ b/cocos/editor-support/spine/PathConstraintMixTimeline.cpp @@ -0,0 +1,123 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(PathConstraintMixTimeline, CurveTimeline) + +const int PathConstraintMixTimeline::ENTRIES = 3; +const int PathConstraintMixTimeline::PREV_TIME = -3; +const int PathConstraintMixTimeline::PREV_ROTATE = -2; +const int PathConstraintMixTimeline::PREV_TRANSLATE = -1; +const int PathConstraintMixTimeline::ROTATE = 1; +const int PathConstraintMixTimeline::TRANSLATE = 2; + +PathConstraintMixTimeline::PathConstraintMixTimeline(int frameCount) : CurveTimeline(frameCount), + _pathConstraintIndex(0) { + _frames.setSize(frameCount * ENTRIES, 0); +} + +void +PathConstraintMixTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex]; + PathConstraint &constraint = *constraintP; + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + constraint._rotateMix = constraint._data._rotateMix; + constraint._translateMix = constraint._data._translateMix; + return; + case MixBlend_First: + constraint._rotateMix += (constraint._data._rotateMix - constraint._rotateMix) * alpha; + constraint._translateMix += (constraint._data._translateMix - constraint._translateMix) * alpha; + return; + default: + return; + } + } + + float rotate, translate; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + rotate = _frames[_frames.size() + PREV_ROTATE]; + translate = _frames[_frames.size() + PREV_TRANSLATE]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + rotate = _frames[frame + PREV_ROTATE]; + translate = _frames[frame + PREV_TRANSLATE]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + rotate += (_frames[frame + ROTATE] - rotate) * percent; + translate += (_frames[frame + TRANSLATE] - translate) * percent; + } + + if (blend == MixBlend_Setup) { + constraint._rotateMix = constraint._data._rotateMix + (rotate - constraint._data._rotateMix) * alpha; + constraint._translateMix = + constraint._data._translateMix + (translate - constraint._data._translateMix) * alpha; + } else { + constraint._rotateMix += (rotate - constraint._rotateMix) * alpha; + constraint._translateMix += (translate - constraint._translateMix) * alpha; + } +} + +int PathConstraintMixTimeline::getPropertyId() { + return ((int) TimelineType_PathConstraintMix << 24) + _pathConstraintIndex; +} + +void PathConstraintMixTimeline::setFrame(int frameIndex, float time, float rotateMix, float translateMix) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + ROTATE] = rotateMix; + _frames[frameIndex + TRANSLATE] = translateMix; +} diff --git a/cocos/editor-support/spine/PathConstraintMixTimeline.h b/cocos/editor-support/spine/PathConstraintMixTimeline.h new file mode 100644 index 000000000000..8c5981f7f04e --- /dev/null +++ b/cocos/editor-support/spine/PathConstraintMixTimeline.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PathConstraintMixTimeline_h +#define Spine_PathConstraintMixTimeline_h + +#include + +namespace spine { +#define SP_PATHCONSTRAINTMIXTIMELINE_ENTRIES 5 + + class SP_API PathConstraintMixTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + static const int ENTRIES; + + explicit PathConstraintMixTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + private: + static const int PREV_TIME; + static const int PREV_ROTATE; + static const int PREV_TRANSLATE; + static const int ROTATE; + static const int TRANSLATE; + + Vector _frames; + int _pathConstraintIndex; + + /// Sets the time and mixes of the specified keyframe. + void setFrame(int frameIndex, float time, float rotateMix, float translateMix); + }; +} + +#endif /* Spine_PathConstraintMixTimeline_h */ diff --git a/cocos/editor-support/spine/PathConstraintPositionTimeline.cpp b/cocos/editor-support/spine/PathConstraintPositionTimeline.cpp new file mode 100644 index 000000000000..75470ef1ffc4 --- /dev/null +++ b/cocos/editor-support/spine/PathConstraintPositionTimeline.cpp @@ -0,0 +1,113 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(PathConstraintPositionTimeline, CurveTimeline) + +const int PathConstraintPositionTimeline::ENTRIES = 2; +const int PathConstraintPositionTimeline::PREV_TIME = -2; +const int PathConstraintPositionTimeline::PREV_VALUE = -1; +const int PathConstraintPositionTimeline::VALUE = 1; + +PathConstraintPositionTimeline::PathConstraintPositionTimeline(int frameCount) : CurveTimeline(frameCount), + _pathConstraintIndex(0) { + _frames.setSize(frameCount * ENTRIES, 0); +} + +PathConstraintPositionTimeline::~PathConstraintPositionTimeline() { +} + +void PathConstraintPositionTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, + float alpha, MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex]; + PathConstraint &constraint = *constraintP; + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + constraint._position = constraint._data._position; + return; + case MixBlend_First: + constraint._position += (constraint._data._position - constraint._position) * alpha; + return; + default: + return; + } + } + + float position; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + position = _frames[_frames.size() + PREV_VALUE]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + position = _frames[frame + PREV_VALUE]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + position += (_frames[frame + VALUE] - position) * percent; + } + if (blend == MixBlend_Setup) { + constraint._position = constraint._data._position + (position - constraint._data._position) * alpha; + } else { + constraint._position += (position - constraint._position) * alpha; + } +} + +int PathConstraintPositionTimeline::getPropertyId() { + return ((int) TimelineType_PathConstraintPosition << 24) + _pathConstraintIndex; +} + +void PathConstraintPositionTimeline::setFrame(int frameIndex, float time, float value) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + VALUE] = value; +} diff --git a/cocos/editor-support/spine/PathConstraintPositionTimeline.h b/cocos/editor-support/spine/PathConstraintPositionTimeline.h new file mode 100644 index 000000000000..4e413118988f --- /dev/null +++ b/cocos/editor-support/spine/PathConstraintPositionTimeline.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PathConstraintPositionTimeline_h +#define Spine_PathConstraintPositionTimeline_h + +#include + +namespace spine { + + class SP_API PathConstraintPositionTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + static const int ENTRIES; + + explicit PathConstraintPositionTimeline(int frameCount); + + virtual ~PathConstraintPositionTimeline(); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float value); + + protected: + static const int PREV_TIME; + static const int PREV_VALUE; + static const int VALUE; + + Vector _frames; + int _pathConstraintIndex; + }; +} + +#endif /* Spine_PathConstraintPositionTimeline_h */ diff --git a/cocos/editor-support/spine/PathConstraintSpacingTimeline.cpp b/cocos/editor-support/spine/PathConstraintSpacingTimeline.cpp new file mode 100644 index 000000000000..a611e1ed5160 --- /dev/null +++ b/cocos/editor-support/spine/PathConstraintSpacingTimeline.cpp @@ -0,0 +1,99 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(PathConstraintSpacingTimeline, PathConstraintPositionTimeline) + +PathConstraintSpacingTimeline::PathConstraintSpacingTimeline(int frameCount) : PathConstraintPositionTimeline( + frameCount) { +} + +void PathConstraintSpacingTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, + float alpha, MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex]; + PathConstraint &constraint = *constraintP; + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + constraint._spacing = constraint._data._spacing; + return; + case MixBlend_First: + constraint._spacing += (constraint._data._spacing - constraint._spacing) * alpha; + return; + default: + return; + } + } + + float spacing; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + spacing = _frames[_frames.size() + PREV_VALUE]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + spacing = _frames[frame + PREV_VALUE]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + spacing += (_frames[frame + VALUE] - spacing) * percent; + } + + if (blend == MixBlend_Setup) { + constraint._spacing = constraint._data._spacing + (spacing - constraint._data._spacing) * alpha; + } else { + constraint._spacing += (spacing - constraint._spacing) * alpha; + } +} + +int PathConstraintSpacingTimeline::getPropertyId() { + return ((int) TimelineType_PathConstraintSpacing << 24) + _pathConstraintIndex; +} diff --git a/cocos/editor-support/spine/PathConstraintSpacingTimeline.h b/cocos/editor-support/spine/PathConstraintSpacingTimeline.h new file mode 100644 index 000000000000..eaca67628652 --- /dev/null +++ b/cocos/editor-support/spine/PathConstraintSpacingTimeline.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PathConstraintSpacingTimeline_h +#define Spine_PathConstraintSpacingTimeline_h + +#include + +namespace spine { + class SP_API PathConstraintSpacingTimeline : public PathConstraintPositionTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + explicit PathConstraintSpacingTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + }; +} + +#endif /* Spine_PathConstraintSpacingTimeline_h */ diff --git a/cocos/editor-support/spine/PointAttachment.cpp b/cocos/editor-support/spine/PointAttachment.cpp new file mode 100644 index 000000000000..5ea8cfad8129 --- /dev/null +++ b/cocos/editor-support/spine/PointAttachment.cpp @@ -0,0 +1,82 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +#include + +using namespace spine; + +RTTI_IMPL(PointAttachment, Attachment) + +PointAttachment::PointAttachment(const String &name) : Attachment(name), _x(0), _y(0), _rotation(0) { +} + +void PointAttachment::computeWorldPosition(Bone &bone, float &ox, float &oy) { + bone.localToWorld(_x, _y, ox, oy); +} + +float PointAttachment::computeWorldRotation(Bone &bone) { + float cos = MathUtil::cosDeg(_rotation); + float sin = MathUtil::sinDeg(_rotation); + float ix = cos * bone._a + sin * bone._b; + float iy = cos * bone._c + sin * bone._d; + + return MathUtil::atan2(iy, ix) * MathUtil::Rad_Deg; +} + +float PointAttachment::getX() { + return _x; +} + +void PointAttachment::setX(float inValue) { + _x = inValue; +} + +float PointAttachment::getY() { + return _y; +} + +void PointAttachment::setY(float inValue) { + _y = inValue; +} + +float PointAttachment::getRotation() { + return _rotation; +} + +void PointAttachment::setRotation(float inValue) { + _rotation = inValue; +} diff --git a/cocos/editor-support/spine/PointAttachment.h b/cocos/editor-support/spine/PointAttachment.h index da8df6cbbdfd..2b800bcb637e 100644 --- a/cocos/editor-support/spine/PointAttachment.h +++ b/cocos/editor-support/spine/PointAttachment.h @@ -1,65 +1,72 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_POINTATTACHMENT_H_ -#define SPINE_POINTATTACHMENT_H_ +#ifndef Spine_PointAttachment_h +#define Spine_PointAttachment_h -#include #include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spPointAttachment { - spVertexAttachment super; - float x, y, rotation; - spColor color; -} spPointAttachment; - -SP_API spPointAttachment* spPointAttachment_create (const char* name); -SP_API void spPointAttachment_computeWorldPosition (spPointAttachment* self, spBone* bone, float* x, float* y); -SP_API float spPointAttachment_computeWorldRotation (spPointAttachment* self, spBone* bone); - -#ifdef SPINE_SHORT_NAMES -typedef spPointAttachment PointAttachment; -#define PointAttachment_create(...) spPointAttachment_create(__VA_ARGS__) -#define PointAttachment_computeWorldPosition(...) spPointAttachment_computeWorldPosition(__VA_ARGS__) -#define PointAttachment_computeWorldRotation(...) spPointAttachment_computeWorldRotation(__VA_ARGS__) -#endif -#ifdef __cplusplus +namespace spine { + class Bone; + + /// + /// An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be + /// used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a + /// skin. + /// + /// See http://esotericsoftware.com/spine-point-attachments for Point Attachments in the Spine User Guide. + /// + class SP_API PointAttachment : public Attachment { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + explicit PointAttachment(const String& name); + + void computeWorldPosition(Bone& bone, float& ox, float& oy); + + float computeWorldRotation(Bone& bone); + + float getX(); + void setX(float inValue); + + float getY(); + void setY(float inValue); + + float getRotation(); + void setRotation(float inValue); + + private: + float _x, _y, _rotation; + }; } -#endif -#endif /* SPINE_POINTATTACHMENT_H_ */ +#endif /* Spine_PointAttachment_h */ diff --git a/cocos/editor-support/spine/Pool.h b/cocos/editor-support/spine/Pool.h new file mode 100644 index 000000000000..9ab5a7497954 --- /dev/null +++ b/cocos/editor-support/spine/Pool.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Pool_h +#define Spine_Pool_h + +#include +#include +#include +#include + +namespace spine { +template +class SP_API Pool : public SpineObject { +public: + Pool() { + } + + ~Pool() { + ContainerUtil::cleanUpVectorOfPointers(_objects); + } + + T *obtain() { + if (_objects.size() > 0) { + T **object = &_objects[_objects.size() - 1]; + T *ret = *object; + _objects.removeAt(_objects.size() - 1); + + return ret; + } else { + T *ret = new(__FILE__, __LINE__) T(); + + return ret; + } + } + + void free(T *object) { + if (!_objects.contains(object)) { + _objects.add(object); + } + } + +private: + Vector _objects; +}; +} + +#endif /* Spine_Pool_h */ diff --git a/cocos/editor-support/spine/PositionMode.h b/cocos/editor-support/spine/PositionMode.h new file mode 100644 index 000000000000..b9951aa42b75 --- /dev/null +++ b/cocos/editor-support/spine/PositionMode.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PositionMode_h +#define Spine_PositionMode_h + +namespace spine { +enum PositionMode { + PositionMode_Fixed = 0, + PositionMode_Percent +}; +} + +#endif /* Spine_PositionMode_h */ diff --git a/cocos/editor-support/spine/RTTI.cpp b/cocos/editor-support/spine/RTTI.cpp new file mode 100644 index 000000000000..727c2b08d74d --- /dev/null +++ b/cocos/editor-support/spine/RTTI.cpp @@ -0,0 +1,65 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include + +using namespace spine; + +RTTI::RTTI(const std::string &className) : _className(className), _pBaseRTTI(NULL) { +} + +RTTI::RTTI(const std::string &className, const RTTI &baseRTTI) : _className(className), _pBaseRTTI(&baseRTTI) { +} + +const std::string &RTTI::getClassName() const { + return _className; +} + +bool RTTI::isExactly(const RTTI &rtti) const { + return (this->_className == rtti._className); +} + +bool RTTI::instanceOf(const RTTI &rtti) const { + const RTTI *pCompare = this; + + while (pCompare) { + if (pCompare->_className == rtti._className) { + return true; + } + + pCompare = pCompare->_pBaseRTTI; + } + + return false; +} diff --git a/cocos/editor-support/spine/RTTI.h b/cocos/editor-support/spine/RTTI.h new file mode 100644 index 000000000000..36308dd479e1 --- /dev/null +++ b/cocos/editor-support/spine/RTTI.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_RTTI_h +#define Spine_RTTI_h + +#include + +#include + +namespace spine { +class SP_API RTTI : public SpineObject { +public: + explicit RTTI(const std::string &className); + + RTTI(const std::string &className, const RTTI &baseRTTI); + + const std::string &getClassName() const; + + bool isExactly(const RTTI &rtti) const; + + bool instanceOf(const RTTI &rtti) const; + +private: + // Prevent copying + RTTI(const RTTI &obj); + + RTTI &operator=(const RTTI &obj); + + const std::string _className; + const RTTI *_pBaseRTTI; +}; +} + +#define RTTI_DECL \ +public: \ +static const spine::RTTI rtti; \ +virtual const spine::RTTI& getRTTI() const; + +#define RTTI_IMPL_NOPARENT(name) \ +const spine::RTTI name::rtti(#name); \ +const spine::RTTI& name::getRTTI() const { return rtti; } + +#define RTTI_IMPL(name, parent) \ +const spine::RTTI name::rtti(#name, parent::rtti); \ +const spine::RTTI& name::getRTTI() const { return rtti; } + +#endif /* Spine_RTTI_h */ diff --git a/cocos/editor-support/spine/RegionAttachment.cpp b/cocos/editor-support/spine/RegionAttachment.cpp new file mode 100644 index 000000000000..85c72dce515a --- /dev/null +++ b/cocos/editor-support/spine/RegionAttachment.cpp @@ -0,0 +1,283 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +#include + +using namespace spine; + +RTTI_IMPL(RegionAttachment, Attachment) + +const int RegionAttachment::BLX = 0; +const int RegionAttachment::BLY = 1; +const int RegionAttachment::ULX = 2; +const int RegionAttachment::ULY = 3; +const int RegionAttachment::URX = 4; +const int RegionAttachment::URY = 5; +const int RegionAttachment::BRX = 6; +const int RegionAttachment::BRY = 7; + +RegionAttachment::RegionAttachment(const String &name) : Attachment(name), HasRendererObject(), + _x(0), + _y(0), + _rotation(0), + _scaleX(1), + _scaleY(1), + _width(0), + _height(0), + _regionOffsetX(0), + _regionOffsetY(0), + _regionWidth(0), + _regionHeight(0), + _regionOriginalWidth(0), + _regionOriginalHeight(0), + _path(), + _regionU(0), + _regionV(0), + _regionU2(0), + _regionV2(0), + _color(1, 1, 1, 1) { + _vertexOffset.setSize(NUM_UVS, 0); + _uvs.setSize(NUM_UVS, 0); +} + +void RegionAttachment::updateOffset() { + float regionScaleX = _width / _regionOriginalWidth * _scaleX; + float regionScaleY = _height / _regionOriginalHeight * _scaleY; + float localX = -_width / 2 * _scaleX + _regionOffsetX * regionScaleX; + float localY = -_height / 2 * _scaleY + _regionOffsetY * regionScaleY; + float localX2 = localX + _regionWidth * regionScaleX; + float localY2 = localY + _regionHeight * regionScaleY; + float cos = MathUtil::cosDeg(_rotation); + float sin = MathUtil::sinDeg(_rotation); + float localXCos = localX * cos + _x; + float localXSin = localX * sin; + float localYCos = localY * cos + _y; + float localYSin = localY * sin; + float localX2Cos = localX2 * cos + _x; + float localX2Sin = localX2 * sin; + float localY2Cos = localY2 * cos + _y; + float localY2Sin = localY2 * sin; + + _vertexOffset[BLX] = localXCos - localYSin; + _vertexOffset[BLY] = localYCos + localXSin; + _vertexOffset[ULX] = localXCos - localY2Sin; + _vertexOffset[ULY] = localY2Cos + localXSin; + _vertexOffset[URX] = localX2Cos - localY2Sin; + _vertexOffset[URY] = localY2Cos + localX2Sin; + _vertexOffset[BRX] = localX2Cos - localYSin; + _vertexOffset[BRY] = localYCos + localX2Sin; +} + +void RegionAttachment::setUVs(float u, float v, float u2, float v2, bool rotate) { + if (rotate) { + _uvs[URX] = u; + _uvs[URY] = v2; + _uvs[BRX] = u; + _uvs[BRY] = v; + _uvs[BLX] = u2; + _uvs[BLY] = v; + _uvs[ULX] = u2; + _uvs[ULY] = v2; + } else { + _uvs[ULX] = u; + _uvs[ULY] = v2; + _uvs[URX] = u; + _uvs[URY] = v; + _uvs[BRX] = u2; + _uvs[BRY] = v; + _uvs[BLX] = u2; + _uvs[BLY] = v2; + } +} + +void RegionAttachment::computeWorldVertices(Bone &bone, Vector &worldVertices, size_t offset, size_t stride) { + assert(worldVertices.size() >= (offset + 8)); + computeWorldVertices(bone, worldVertices.buffer(), offset, stride); +} + +void RegionAttachment::computeWorldVertices(Bone &bone, float* worldVertices, size_t offset, size_t stride) { + float x = bone.getWorldX(), y = bone.getWorldY(); + float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD(); + float offsetX, offsetY; + + offsetX = _vertexOffset[BRX]; + offsetY = _vertexOffset[BRY]; + worldVertices[offset] = offsetX * a + offsetY * b + x; // br + worldVertices[offset + 1] = offsetX * c + offsetY * d + y; + offset += stride; + + offsetX = _vertexOffset[BLX]; + offsetY = _vertexOffset[BLY]; + worldVertices[offset] = offsetX * a + offsetY * b + x; // bl + worldVertices[offset + 1] = offsetX * c + offsetY * d + y; + offset += stride; + + offsetX = _vertexOffset[ULX]; + offsetY = _vertexOffset[ULY]; + worldVertices[offset] = offsetX * a + offsetY * b + x; // ul + worldVertices[offset + 1] = offsetX * c + offsetY * d + y; + offset += stride; + + offsetX = _vertexOffset[URX]; + offsetY = _vertexOffset[URY]; + worldVertices[offset] = offsetX * a + offsetY * b + x; // ur + worldVertices[offset + 1] = offsetX * c + offsetY * d + y; +} + +float RegionAttachment::getX() { + return _x; +} + +void RegionAttachment::setX(float inValue) { + _x = inValue; +} + +float RegionAttachment::getY() { + return _y; +} + +void RegionAttachment::setY(float inValue) { + _y = inValue; +} + +float RegionAttachment::getRotation() { + return _rotation; +} + +void RegionAttachment::setRotation(float inValue) { + _rotation = inValue; +} + +float RegionAttachment::getScaleX() { + return _scaleX; +} + +void RegionAttachment::setScaleX(float inValue) { + _scaleX = inValue; +} + +float RegionAttachment::getScaleY() { + return _scaleY; +} + +void RegionAttachment::setScaleY(float inValue) { + _scaleY = inValue; +} + +float RegionAttachment::getWidth() { + return _width; +} + +void RegionAttachment::setWidth(float inValue) { + _width = inValue; +} + +float RegionAttachment::getHeight() { + return _height; +} + +void RegionAttachment::setHeight(float inValue) { + _height = inValue; +} + +const String &RegionAttachment::getPath() { + return _path; +} + +void RegionAttachment::setPath(const String &inValue) { + _path = inValue; +} + +float RegionAttachment::getRegionOffsetX() { + return _regionOffsetX; +} + +void RegionAttachment::setRegionOffsetX(float inValue) { + _regionOffsetX = inValue; +} + +float RegionAttachment::getRegionOffsetY() { + return _regionOffsetY; +} + +void RegionAttachment::setRegionOffsetY(float inValue) { + _regionOffsetY = inValue; +} + +float RegionAttachment::getRegionWidth() { + return _regionWidth; +} + +void RegionAttachment::setRegionWidth(float inValue) { + _regionWidth = inValue; +} + +float RegionAttachment::getRegionHeight() { + return _regionHeight; +} + +void RegionAttachment::setRegionHeight(float inValue) { + _regionHeight = inValue; +} + +float RegionAttachment::getRegionOriginalWidth() { + return _regionOriginalWidth; +} + +void RegionAttachment::setRegionOriginalWidth(float inValue) { + _regionOriginalWidth = inValue; +} + +float RegionAttachment::getRegionOriginalHeight() { + return _regionOriginalHeight; +} + +void RegionAttachment::setRegionOriginalHeight(float inValue) { + _regionOriginalHeight = inValue; +} + +Vector &RegionAttachment::getOffset() { + return _vertexOffset; +} + +Vector &RegionAttachment::getUVs() { + return _uvs; +} + +spine::Color &RegionAttachment::getColor() { + return _color; +} diff --git a/cocos/editor-support/spine/RegionAttachment.h b/cocos/editor-support/spine/RegionAttachment.h index 59249c218aa1..f79db881f73e 100644 --- a/cocos/editor-support/spine/RegionAttachment.h +++ b/cocos/editor-support/spine/RegionAttachment.h @@ -1,75 +1,132 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_REGIONATTACHMENT_H_ -#define SPINE_REGIONATTACHMENT_H_ +#ifndef Spine_RegionAttachment_h +#define Spine_RegionAttachment_h -#include #include -#include -#include +#include +#include + +#include +#include + +#define NUM_UVS 8 + +namespace spine { + class Bone; + + /// Attachment that displays a texture region. + class SP_API RegionAttachment : public Attachment, public HasRendererObject { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class AtlasAttachmentLoader; + + RTTI_DECL + + public: + explicit RegionAttachment(const String& name); + + void updateOffset(); + + void setUVs(float u, float v, float u2, float v2, bool rotate); + + /// Transforms the attachment's four vertices to world coordinates. + /// @param bone The parent bone. + /// @param worldVertices The output world vertices. Must have a length greater than or equal to offset + 8. + /// @param offset The worldVertices index to begin writing values. + /// @param stride The number of worldVertices entries between the value pairs written. + void computeWorldVertices(Bone& bone, float *worldVertices, size_t offset, size_t stride = 2); + void computeWorldVertices(Bone& bone, Vector& worldVertices, size_t offset, size_t stride = 2); + + float getX(); + void setX(float inValue); + float getY(); + void setY(float inValue); + float getRotation(); + void setRotation(float inValue); + float getScaleX(); + void setScaleX(float inValue); + float getScaleY(); + void setScaleY(float inValue); + float getWidth(); + void setWidth(float inValue); + float getHeight(); + void setHeight(float inValue); + + Color& getColor(); -#ifdef __cplusplus -extern "C" { -#endif + const String& getPath(); + void setPath(const String& inValue); -typedef struct spRegionAttachment { - spAttachment super; - const char* path; - float x, y, scaleX, scaleY, rotation, width, height; - spColor color; + float getRegionOffsetX(); + void setRegionOffsetX(float inValue); - void* rendererObject; - int regionOffsetX, regionOffsetY; /* Pixels stripped from the bottom left, unrotated. */ - int regionWidth, regionHeight; /* Unrotated, stripped pixel size. */ - int regionOriginalWidth, regionOriginalHeight; /* Unrotated, unstripped pixel size. */ + float getRegionOffsetY(); + void setRegionOffsetY(float inValue); - float offset[8]; - float uvs[8]; -} spRegionAttachment; + float getRegionWidth(); + void setRegionWidth(float inValue); -SP_API spRegionAttachment* spRegionAttachment_create (const char* name); -SP_API void spRegionAttachment_setUVs (spRegionAttachment* self, float u, float v, float u2, float v2, int/*bool*/rotate); -SP_API void spRegionAttachment_updateOffset (spRegionAttachment* self); -SP_API void spRegionAttachment_computeWorldVertices (spRegionAttachment* self, spBone* bone, float* vertices, int offset, int stride); + float getRegionHeight(); + void setRegionHeight(float inValue); -#ifdef SPINE_SHORT_NAMES -typedef spRegionAttachment RegionAttachment; -#define RegionAttachment_create(...) spRegionAttachment_create(__VA_ARGS__) -#define RegionAttachment_setUVs(...) spRegionAttachment_setUVs(__VA_ARGS__) -#define RegionAttachment_updateOffset(...) spRegionAttachment_updateOffset(__VA_ARGS__) -#define RegionAttachment_computeWorldVertices(...) spRegionAttachment_computeWorldVertices(__VA_ARGS__) -#endif + float getRegionOriginalWidth(); + void setRegionOriginalWidth(float inValue); -#ifdef __cplusplus + float getRegionOriginalHeight(); + void setRegionOriginalHeight(float inValue); + + Vector& getOffset(); + Vector& getUVs(); + + private: + static const int BLX; + static const int BLY; + static const int ULX; + static const int ULY; + static const int URX; + static const int URY; + static const int BRX; + static const int BRY; + + float _x, _y, _rotation, _scaleX, _scaleY, _width, _height; + float _regionOffsetX, _regionOffsetY, _regionWidth, _regionHeight, _regionOriginalWidth, _regionOriginalHeight; + Vector _vertexOffset; + Vector _uvs; + String _path; + float _regionU; + float _regionV; + float _regionU2; + float _regionV2; + Color _color; + }; } -#endif -#endif /* SPINE_REGIONATTACHMENT_H_ */ +#endif /* Spine_RegionAttachment_h */ diff --git a/cocos/editor-support/spine/RotateMode.h b/cocos/editor-support/spine/RotateMode.h new file mode 100644 index 000000000000..d680811c5b6c --- /dev/null +++ b/cocos/editor-support/spine/RotateMode.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_RotateMode_h +#define Spine_RotateMode_h + +namespace spine { +enum RotateMode { + RotateMode_Tangent = 0, + RotateMode_Chain, + RotateMode_ChainScale +}; +} + +#endif /* Spine_RotateMode_h */ diff --git a/cocos/editor-support/spine/RotateTimeline.cpp b/cocos/editor-support/spine/RotateTimeline.cpp new file mode 100644 index 000000000000..a06e43e29c6a --- /dev/null +++ b/cocos/editor-support/spine/RotateTimeline.cpp @@ -0,0 +1,138 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(RotateTimeline, CurveTimeline) + +RotateTimeline::RotateTimeline(int frameCount) : CurveTimeline(frameCount), _boneIndex(0) { + _frames.setSize(frameCount << 1, 0); +} + +void RotateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + Bone *bone = skeleton.getBones()[_boneIndex]; + + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: { + bone->_rotation = bone->_data._rotation; + break; + } + case MixBlend_First: { + float r = bone->_data._rotation - bone->_rotation; + bone->_rotation += (r - (16384 - (int) (16384.499999999996 - r / 360)) * 360) * alpha; + break; + } + default: { + // TODO? + break; + } + } + + return; + } + + if (time >= _frames[_frames.size() - ENTRIES]) { + float r = _frames[_frames.size() + PREV_ROTATION]; + switch (blend) { + case MixBlend_Setup: + bone->_rotation = bone->_data._rotation + r * alpha; + break; + case MixBlend_First: + case MixBlend_Replace: + r += bone->_data._rotation - bone->_rotation; + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + // Fall through. + case MixBlend_Add: + bone->_rotation += r * alpha; + } + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + float prevRotation = _frames[frame + PREV_ROTATION]; + float frameTime = _frames[frame]; + float percent = getCurvePercent((frame >> 1) - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + float r = _frames[frame + ROTATION] - prevRotation; + r = prevRotation + (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * percent; + switch (blend) { + case MixBlend_Setup: + bone->_rotation = bone->_data._rotation + (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * alpha; + break; + case MixBlend_First: + case MixBlend_Replace: + r += bone->_data._rotation - bone->_rotation; + // Fall through. + case MixBlend_Add: + bone->_rotation += (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * alpha; + } +} + +int RotateTimeline::getPropertyId() { + return ((int) TimelineType_Rotate << 24) + _boneIndex; +} + +void RotateTimeline::setFrame(int frameIndex, float time, float degrees) { + frameIndex <<= 1; + _frames[frameIndex] = time; + _frames[frameIndex + ROTATION] = degrees; +} + +int RotateTimeline::getBoneIndex() { + return _boneIndex; +} + +void RotateTimeline::setBoneIndex(int inValue) { + _boneIndex = inValue; +} + +Vector &RotateTimeline::getFrames() { + return _frames; +} diff --git a/cocos/editor-support/spine/RotateTimeline.h b/cocos/editor-support/spine/RotateTimeline.h new file mode 100644 index 000000000000..54a9fe1c3efc --- /dev/null +++ b/cocos/editor-support/spine/RotateTimeline.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_RotateTimeline_h +#define Spine_RotateTimeline_h + +#include + +namespace spine { + class SP_API RotateTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class AnimationState; + + RTTI_DECL + + public: + static const int ENTRIES = 2; + + explicit RotateTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float degrees); + + int getBoneIndex(); + void setBoneIndex(int inValue); + + Vector& getFrames(); + + private: + static const int PREV_TIME = -2; + static const int PREV_ROTATION = -1; + static const int ROTATION = 1; + + int _boneIndex; + Vector _frames; // time, angle, ... + }; +} + +#endif /* Spine_RotateTimeline_h */ diff --git a/cocos/editor-support/spine/ScaleTimeline.cpp b/cocos/editor-support/spine/ScaleTimeline.cpp new file mode 100644 index 000000000000..05a7e7181aaf --- /dev/null +++ b/cocos/editor-support/spine/ScaleTimeline.cpp @@ -0,0 +1,149 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(ScaleTimeline, TranslateTimeline) + +ScaleTimeline::ScaleTimeline(int frameCount) : TranslateTimeline(frameCount) { +} + +void ScaleTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + + Bone *boneP = skeleton._bones[_boneIndex]; + Bone &bone = *boneP; + + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + bone._scaleX = bone._data._scaleX; + bone._scaleY = bone._data._scaleY; + return; + case MixBlend_First: + bone._scaleX += (bone._data._scaleX - bone._scaleX) * alpha; + bone._scaleY += (bone._data._scaleY - bone._scaleY) * alpha; + default: {} + } + return; + } + + float x, y; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + x = _frames[_frames.size() + PREV_X] * bone._data._scaleX; + y = _frames[_frames.size() + PREV_Y] * bone._data._scaleY; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + x = _frames[frame + PREV_X]; + y = _frames[frame + PREV_Y]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + x = (x + (_frames[frame + X] - x) * percent) * bone._data._scaleX; + y = (y + (_frames[frame + Y] - y) * percent) * bone._data._scaleY; + } + + if (alpha == 1) { + if (blend == MixBlend_Add) { + bone._scaleX += x - bone._data._scaleX; + bone._scaleY += y - bone._data._scaleY; + } else { + bone._scaleX = x; + bone._scaleY = y; + } + } else { + // Mixing out uses sign of setup or current pose, else use sign of key. + float bx, by; + if (direction == MixDirection_Out) { + switch (blend) { + case MixBlend_Setup: + bx = bone._data._scaleX; + by = bone._data._scaleY; + bone._scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha; + bone._scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha; + break; + case MixBlend_First: + case MixBlend_Replace: + bx = bone._scaleX; + by = bone._scaleY; + bone._scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha; + bone._scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha; + break; + case MixBlend_Add: + bx = bone._scaleX; + by = bone._scaleY; + bone._scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bone._data._scaleX) * alpha; + bone._scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - bone._data._scaleY) * alpha; + } + } else { + switch (blend) { + case MixBlend_Setup: + bx = MathUtil::abs(bone._data._scaleX) * MathUtil::sign(x); + by = MathUtil::abs(bone._data._scaleY) * MathUtil::sign(y); + bone._scaleX = bx + (x - bx) * alpha; + bone._scaleY = by + (y - by) * alpha; + break; + case MixBlend_First: + case MixBlend_Replace: + bx = MathUtil::abs(bone._scaleX) * MathUtil::sign(x); + by = MathUtil::abs(bone._scaleY) * MathUtil::sign(y); + bone._scaleX = bx + (x - bx) * alpha; + bone._scaleY = by + (y - by) * alpha; + break; + case MixBlend_Add: + bx = MathUtil::sign(x); + by = MathUtil::sign(y); + bone._scaleX = MathUtil::abs(bone._scaleX) * bx + (x - MathUtil::abs(bone._data._scaleX) * bx) * alpha; + bone._scaleY = MathUtil::abs(bone._scaleY) * by + (y - MathUtil::abs(bone._data._scaleY) * by) * alpha; + } + } + } +} + +int ScaleTimeline::getPropertyId() { + return ((int) TimelineType_Scale << 24) + _boneIndex; +} diff --git a/cocos/editor-support/spine/ScaleTimeline.h b/cocos/editor-support/spine/ScaleTimeline.h new file mode 100644 index 000000000000..dab19a7ad7c9 --- /dev/null +++ b/cocos/editor-support/spine/ScaleTimeline.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_ScaleTimeline_h +#define Spine_ScaleTimeline_h + +#include + +namespace spine { + class SP_API ScaleTimeline : public TranslateTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + explicit ScaleTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + }; +} + +#endif /* Spine_ScaleTimeline_h */ diff --git a/cocos/editor-support/spine/ShearTimeline.cpp b/cocos/editor-support/spine/ShearTimeline.cpp new file mode 100644 index 000000000000..99e52ebdc6ee --- /dev/null +++ b/cocos/editor-support/spine/ShearTimeline.cpp @@ -0,0 +1,110 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(ShearTimeline, TranslateTimeline) + +ShearTimeline::ShearTimeline(int frameCount) : TranslateTimeline(frameCount) { +} + +void ShearTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + Bone *boneP = skeleton._bones[_boneIndex]; + Bone &bone = *boneP; + + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + bone._shearX = bone._data._shearX; + bone._shearY = bone._data._shearY; + return; + case MixBlend_First: + bone._shearX += (bone._data._shearX - bone._shearX) * alpha; + bone._shearY += (bone._data._shearY - bone._shearY) * alpha; + default: {} + } + return; + } + + float x, y; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + x = _frames[_frames.size() + PREV_X]; + y = _frames[_frames.size() + PREV_Y]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + x = _frames[frame + PREV_X]; + y = _frames[frame + PREV_Y]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + x = x + (_frames[frame + X] - x) * percent; + y = y + (_frames[frame + Y] - y) * percent; + } + + switch (blend) { + case MixBlend_Setup: + bone._shearX = bone._data._shearX + x * alpha; + bone._shearY = bone._data._shearY + y * alpha; + break; + case MixBlend_First: + case MixBlend_Replace: + bone._shearX += (bone._data._shearX + x - bone._shearX) * alpha; + bone._shearY += (bone._data._shearY + y - bone._shearY) * alpha; + break; + case MixBlend_Add: + bone._shearX += x * alpha; + bone._shearY += y * alpha; + } +} + +int ShearTimeline::getPropertyId() { + return ((int) TimelineType_Shear << 24) + _boneIndex; +} diff --git a/cocos/editor-support/spine/ShearTimeline.h b/cocos/editor-support/spine/ShearTimeline.h new file mode 100644 index 000000000000..cf0562da0a2e --- /dev/null +++ b/cocos/editor-support/spine/ShearTimeline.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_ShearTimeline_h +#define Spine_ShearTimeline_h + +#include + +namespace spine { + class SP_API ShearTimeline : public TranslateTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + explicit ShearTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + }; +} + +#endif /* Spine_ShearTimeline_h */ diff --git a/cocos/editor-support/spine/Skeleton.cpp b/cocos/editor-support/spine/Skeleton.cpp new file mode 100644 index 000000000000..f533449a6987 --- /dev/null +++ b/cocos/editor-support/spine/Skeleton.cpp @@ -0,0 +1,652 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace spine; + +Skeleton::Skeleton(SkeletonData *skeletonData) : + _data(skeletonData), + _skin(NULL), + _color(1, 1, 1, 1), + _time(0), + _scaleX(1), + _scaleY(1), + _x(0), + _y(0) { + _bones.ensureCapacity(_data->getBones().size()); + for (size_t i = 0; i < _data->getBones().size(); ++i) { + BoneData *data = _data->getBones()[i]; + + Bone *bone; + if (data->getParent() == NULL) { + bone = new(__FILE__, __LINE__) Bone(*data, *this, NULL); + } else { + Bone *parent = _bones[data->getParent()->getIndex()]; + bone = new(__FILE__, __LINE__) Bone(*data, *this, parent); + parent->getChildren().add(bone); + } + + _bones.add(bone); + } + + _slots.ensureCapacity(_data->getSlots().size()); + _drawOrder.ensureCapacity(_data->getSlots().size()); + for (size_t i = 0; i < _data->getSlots().size(); ++i) { + SlotData *data = _data->getSlots()[i]; + + Bone *bone = _bones[data->getBoneData().getIndex()]; + Slot *slot = new(__FILE__, __LINE__) Slot(*data, *bone); + + _slots.add(slot); + _drawOrder.add(slot); + } + + _ikConstraints.ensureCapacity(_data->getIkConstraints().size()); + for (size_t i = 0; i < _data->getIkConstraints().size(); ++i) { + IkConstraintData *data = _data->getIkConstraints()[i]; + + IkConstraint *constraint = new(__FILE__, __LINE__) IkConstraint(*data, *this); + + _ikConstraints.add(constraint); + } + + _transformConstraints.ensureCapacity(_data->getTransformConstraints().size()); + for (size_t i = 0; i < _data->getTransformConstraints().size(); ++i) { + TransformConstraintData *data = _data->getTransformConstraints()[i]; + + TransformConstraint *constraint = new(__FILE__, __LINE__) TransformConstraint(*data, *this); + + _transformConstraints.add(constraint); + } + + _pathConstraints.ensureCapacity(_data->getPathConstraints().size()); + for (size_t i = 0; i < _data->getPathConstraints().size(); ++i) { + PathConstraintData *data = _data->getPathConstraints()[i]; + + PathConstraint *constraint = new(__FILE__, __LINE__) PathConstraint(*data, *this); + + _pathConstraints.add(constraint); + } + + updateCache(); +} + +Skeleton::~Skeleton() { + ContainerUtil::cleanUpVectorOfPointers(_bones); + ContainerUtil::cleanUpVectorOfPointers(_slots); + ContainerUtil::cleanUpVectorOfPointers(_ikConstraints); + ContainerUtil::cleanUpVectorOfPointers(_transformConstraints); + ContainerUtil::cleanUpVectorOfPointers(_pathConstraints); +} + +void Skeleton::updateCache() { + _updateCache.clear(); + _updateCacheReset.clear(); + + for (size_t i = 0, n = _bones.size(); i < n; ++i) { + _bones[i]->_sorted = false; + } + + size_t ikCount = _ikConstraints.size(); + size_t transformCount = _transformConstraints.size(); + size_t pathCount = _pathConstraints.size(); + + size_t constraintCount = ikCount + transformCount + pathCount; + + size_t i = 0; + continue_outer: + for (; i < constraintCount; ++i) { + for (size_t ii = 0; ii < ikCount; ++ii) { + IkConstraint *constraint = _ikConstraints[ii]; + if (constraint->getData().getOrder() == i) { + sortIkConstraint(constraint); + i++; + goto continue_outer; + } + } + + for (size_t ii = 0; ii < transformCount; ++ii) { + TransformConstraint *constraint = _transformConstraints[ii]; + if (constraint->getData().getOrder() == (int)i) { + sortTransformConstraint(constraint); + i++; + goto continue_outer; + } + } + + for (size_t ii = 0; ii < pathCount; ++ii) { + PathConstraint *constraint = _pathConstraints[ii]; + if (constraint->getData().getOrder() == (int)i) { + sortPathConstraint(constraint); + i++; + goto continue_outer; + } + } + } + + size_t n = _bones.size(); + for (i = 0; i < n; ++i) { + sortBone(_bones[i]); + } +} + +void Skeleton::printUpdateCache() { + for (size_t i = 0; i < _updateCache.size(); i++) { + Updatable *updatable = _updateCache[i]; + if (updatable->getRTTI().isExactly(Bone::rtti)) { + printf("bone %s\n", ((Bone *) updatable)->getData().getName().buffer()); + } else if (updatable->getRTTI().isExactly(TransformConstraint::rtti)) { + printf("transform constraint %s\n", ((TransformConstraint *) updatable)->getData().getName().buffer()); + } else if (updatable->getRTTI().isExactly(IkConstraint::rtti)) { + printf("ik constraint %s\n", ((IkConstraint *) updatable)->getData().getName().buffer()); + } else if (updatable->getRTTI().isExactly(PathConstraint::rtti)) { + printf("path constraint %s\n", ((PathConstraint *) updatable)->getData().getName().buffer()); + } + } +} + +void Skeleton::updateWorldTransform() { + for (size_t i = 0, n = _updateCacheReset.size(); i < n; ++i) { + Bone *boneP = _updateCacheReset[i]; + Bone &bone = *boneP; + bone._ax = bone._x; + bone._ay = bone._y; + bone._arotation = bone._rotation; + bone._ascaleX = bone._scaleX; + bone._ascaleY = bone._scaleY; + bone._ashearX = bone._shearX; + bone._ashearY = bone._shearY; + bone._appliedValid = true; + } + + for (size_t i = 0, n = _updateCache.size(); i < n; ++i) { + _updateCache[i]->update(); + } +} + +void Skeleton::setToSetupPose() { + setBonesToSetupPose(); + setSlotsToSetupPose(); +} + +void Skeleton::setBonesToSetupPose() { + for (size_t i = 0, n = _bones.size(); i < n; ++i) { + _bones[i]->setToSetupPose(); + } + + for (size_t i = 0, n = _ikConstraints.size(); i < n; ++i) { + IkConstraint *constraintP = _ikConstraints[i]; + IkConstraint &constraint = *constraintP; + + constraint._bendDirection = constraint._data._bendDirection; + constraint._compress = constraint._data._compress; + constraint._stretch = constraint._data._stretch; + constraint._mix = constraint._data._mix; + } + + for (size_t i = 0, n = _transformConstraints.size(); i < n; ++i) { + TransformConstraint *constraintP = _transformConstraints[i]; + TransformConstraint &constraint = *constraintP; + TransformConstraintData &constraintData = constraint._data; + + constraint._rotateMix = constraintData._rotateMix; + constraint._translateMix = constraintData._translateMix; + constraint._scaleMix = constraintData._scaleMix; + constraint._shearMix = constraintData._shearMix; + } + + for (size_t i = 0, n = _pathConstraints.size(); i < n; ++i) { + PathConstraint *constraintP = _pathConstraints[i]; + PathConstraint &constraint = *constraintP; + PathConstraintData &constraintData = constraint._data; + + constraint._position = constraintData._position; + constraint._spacing = constraintData._spacing; + constraint._rotateMix = constraintData._rotateMix; + constraint._translateMix = constraintData._translateMix; + } +} + +void Skeleton::setSlotsToSetupPose() { + _drawOrder.clear(); + for (size_t i = 0, n = _slots.size(); i < n; ++i) { + _drawOrder.add(_slots[i]); + } + + for (size_t i = 0, n = _slots.size(); i < n; ++i) { + _slots[i]->setToSetupPose(); + } +} + +Bone *Skeleton::findBone(const String &boneName) { + return ContainerUtil::findWithDataName(_bones, boneName); +} + +int Skeleton::findBoneIndex(const String &boneName) { + return ContainerUtil::findIndexWithDataName(_bones, boneName); +} + +Slot *Skeleton::findSlot(const String &slotName) { + return ContainerUtil::findWithDataName(_slots, slotName); +} + +int Skeleton::findSlotIndex(const String &slotName) { + return ContainerUtil::findIndexWithDataName(_slots, slotName); +} + +void Skeleton::setSkin(const String &skinName) { + Skin *foundSkin = _data->findSkin(skinName); + + assert(foundSkin != NULL); + + setSkin(foundSkin); +} + +void Skeleton::setSkin(Skin *newSkin) { + if (newSkin != NULL) { + if (_skin != NULL) { + Skeleton &thisRef = *this; + newSkin->attachAll(thisRef, *_skin); + } else { + for (size_t i = 0, n = _slots.size(); i < n; ++i) { + Slot *slotP = _slots[i]; + Slot &slot = *slotP; + const String &name = slot._data.getAttachmentName(); + if (name.length() > 0) { + Attachment *attachment = newSkin->getAttachment(i, name); + if (attachment != NULL) { + slot.setAttachment(attachment); + } + } + } + } + } + + _skin = newSkin; +} + +Attachment *Skeleton::getAttachment(const String &slotName, const String &attachmentName) { + return getAttachment(_data->findSlotIndex(slotName), attachmentName); +} + +Attachment *Skeleton::getAttachment(int slotIndex, const String &attachmentName) { + assert(attachmentName.length() > 0); + + if (_skin != NULL) { + Attachment *attachment = _skin->getAttachment(slotIndex, attachmentName); + if (attachment != NULL) { + return attachment; + } + } + + return _data->getDefaultSkin() != NULL ? _data->getDefaultSkin()->getAttachment(slotIndex, attachmentName) : NULL; +} + +void Skeleton::setAttachment(const String &slotName, const String &attachmentName) { + assert(slotName.length() > 0); + + for (size_t i = 0, n = _slots.size(); i < n; ++i) { + Slot *slot = _slots[i]; + if (slot->_data.getName() == slotName) { + Attachment *attachment = NULL; + if (attachmentName.length() > 0) { + attachment = getAttachment(i, attachmentName); + + assert(attachment != NULL); + } + + slot->setAttachment(attachment); + + return; + } + } + + printf("Slot not found: %s", slotName.buffer()); + + assert(false); +} + +IkConstraint *Skeleton::findIkConstraint(const String &constraintName) { + assert(constraintName.length() > 0); + + for (size_t i = 0, n = _ikConstraints.size(); i < n; ++i) { + IkConstraint *ikConstraint = _ikConstraints[i]; + if (ikConstraint->_data.getName() == constraintName) { + return ikConstraint; + } + } + return NULL; +} + +TransformConstraint *Skeleton::findTransformConstraint(const String &constraintName) { + assert(constraintName.length() > 0); + + for (size_t i = 0, n = _transformConstraints.size(); i < n; ++i) { + TransformConstraint *transformConstraint = _transformConstraints[i]; + if (transformConstraint->_data.getName() == constraintName) { + return transformConstraint; + } + } + + return NULL; +} + +PathConstraint *Skeleton::findPathConstraint(const String &constraintName) { + assert(constraintName.length() > 0); + + for (size_t i = 0, n = _pathConstraints.size(); i < n; ++i) { + PathConstraint *constraint = _pathConstraints[i]; + if (constraint->_data.getName() == constraintName) { + return constraint; + } + } + + return NULL; +} + +void Skeleton::update(float delta) { + _time += delta; +} + +void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHeight, Vector &outVertexBuffer) { + float minX = std::numeric_limits::max(); + float minY = std::numeric_limits::max(); + float maxX = std::numeric_limits::min(); + float maxY = std::numeric_limits::min(); + + for (size_t i = 0; i < _drawOrder.size(); ++i) { + Slot *slot = _drawOrder[i]; + size_t verticesLength = 0; + Attachment *attachment = slot->getAttachment(); + + if (attachment != NULL && attachment->getRTTI().instanceOf(RegionAttachment::rtti)) { + RegionAttachment *regionAttachment = static_cast(attachment); + + verticesLength = 8; + if (outVertexBuffer.size() < 8) { + outVertexBuffer.setSize(8, 0); + } + regionAttachment->computeWorldVertices(slot->getBone(), outVertexBuffer, 0); + } else if (attachment != NULL && attachment->getRTTI().instanceOf(MeshAttachment::rtti)) { + MeshAttachment *mesh = static_cast(attachment); + + verticesLength = mesh->getWorldVerticesLength(); + if (outVertexBuffer.size() < verticesLength) { + outVertexBuffer.setSize(verticesLength, 0); + } + + mesh->computeWorldVertices(*slot, 0, verticesLength, outVertexBuffer, 0); + } + + for (size_t ii = 0; ii < verticesLength; ii += 2) { + float vx = outVertexBuffer[ii]; + float vy = outVertexBuffer[ii + 1]; + + minX = MathUtil::min(minX, vx); + minY = MathUtil::min(minY, vy); + maxX = MathUtil::max(maxX, vx); + maxY = MathUtil::max(maxY, vy); + } + } + + outX = minX; + outY = minY; + outWidth = maxX - minX; + outHeight = maxY - minY; +} + +Bone *Skeleton::getRootBone() { + return _bones.size() == 0 ? NULL : _bones[0]; +} + +SkeletonData *Skeleton::getData() { + return _data; +} + +Vector &Skeleton::getBones() { + return _bones; +} + +Vector &Skeleton::getUpdateCacheList() { + return _updateCache; +} + +Vector &Skeleton::getSlots() { + return _slots; +} + +Vector &Skeleton::getDrawOrder() { + return _drawOrder; +} + +Vector &Skeleton::getIkConstraints() { + return _ikConstraints; +} + +Vector &Skeleton::getPathConstraints() { + return _pathConstraints; +} + +Vector &Skeleton::getTransformConstraints() { + return _transformConstraints; +} + +Skin *Skeleton::getSkin() { + return _skin; +} + +Color &Skeleton::getColor() { + return _color; +} + +float Skeleton::getTime() { + return _time; +} + +void Skeleton::setTime(float inValue) { + _time = inValue; +} + +void Skeleton::setPosition(float x, float y) { + _x = x; + _y = y; +} + +float Skeleton::getX() { + return _x; +} + +void Skeleton::setX(float inValue) { + _x = inValue; +} + +float Skeleton::getY() { + return _y; +} + +void Skeleton::setY(float inValue) { + _y = inValue; +} + +float Skeleton::getScaleX() { + return _scaleX; +} + +void Skeleton::setScaleX(float inValue) { + _scaleX = inValue; +} + +float Skeleton::getScaleY() { + return _scaleY * (Bone::isYDown() ? -1 : 1); +} + +void Skeleton::setScaleY(float inValue) { + _scaleY = inValue; +} + +void Skeleton::sortIkConstraint(IkConstraint *constraint) { + Bone *target = constraint->getTarget(); + sortBone(target); + + Vector &constrained = constraint->getBones(); + Bone *parent = constrained[0]; + sortBone(parent); + + if (constrained.size() > 1) { + Bone *child = constrained[constrained.size() - 1]; + if (!_updateCache.contains(child)) _updateCacheReset.add(child); + } + + _updateCache.add(constraint); + + sortReset(parent->getChildren()); + constrained[constrained.size() - 1]->_sorted = true; +} + +void Skeleton::sortPathConstraint(PathConstraint *constraint) { + Slot *slot = constraint->getTarget(); + int slotIndex = slot->getData().getIndex(); + Bone &slotBone = slot->getBone(); + if (_skin != NULL) sortPathConstraintAttachment(_skin, slotIndex, slotBone); + if (_data->_defaultSkin != NULL && _data->_defaultSkin != _skin) + sortPathConstraintAttachment(_data->_defaultSkin, slotIndex, slotBone); + for (size_t ii = 0, nn = _data->_skins.size(); ii < nn; ii++) + sortPathConstraintAttachment(_data->_skins[ii], slotIndex, slotBone); + + Attachment *attachment = slot->getAttachment(); + if (attachment != NULL && attachment->getRTTI().instanceOf(PathAttachment::rtti)) + sortPathConstraintAttachment(attachment, slotBone); + + Vector &constrained = constraint->getBones(); + size_t boneCount = constrained.size(); + for (size_t i = 0; i < boneCount; ++i) { + sortBone(constrained[i]); + } + + _updateCache.add(constraint); + + for (size_t i = 0; i < boneCount; i++) + sortReset(constrained[i]->getChildren()); + for (size_t i = 0; i < boneCount; i++) + constrained[i]->_sorted = true; +} + +void Skeleton::sortTransformConstraint(TransformConstraint *constraint) { + sortBone(constraint->getTarget()); + + Vector &constrained = constraint->getBones(); + size_t boneCount = constrained.size(); + if (constraint->_data.isLocal()) { + for (size_t i = 0; i < boneCount; i++) { + Bone *child = constrained[i]; + sortBone(child->getParent()); + if (!_updateCache.contains(child)) _updateCacheReset.add(child); + } + } else { + for (size_t i = 0; i < boneCount; ++i) { + sortBone(constrained[i]); + } + } + + _updateCache.add(constraint); + + for (size_t i = 0; i < boneCount; ++i) + sortReset(constrained[i]->getChildren()); + for (size_t i = 0; i < boneCount; ++i) + constrained[i]->_sorted = true; +} + +void Skeleton::sortPathConstraintAttachment(Skin *skin, size_t slotIndex, Bone &slotBone) { + Skin::AttachmentMap::Entries attachments = skin->getAttachments(); + + while (attachments.hasNext()) { + Skin::AttachmentMap::Entry entry = attachments.next(); + if (entry._slotIndex == slotIndex) { + Attachment *value = entry._attachment; + sortPathConstraintAttachment(value, slotBone); + } + } +} + +void Skeleton::sortPathConstraintAttachment(Attachment *attachment, Bone &slotBone) { + if (attachment == NULL || !attachment->getRTTI().instanceOf(PathAttachment::rtti)) return; + Vector &pathBones = static_cast(attachment)->getBones(); + if (pathBones.size() == 0) + sortBone(&slotBone); + else { + for (size_t i = 0, n = pathBones.size(); i < n;) { + size_t nn = pathBones[i++]; + nn += i; + while (i < nn) { + sortBone(_bones[pathBones[i++]]); + } + } + } +} + +void Skeleton::sortBone(Bone *bone) { + if (bone->_sorted) return; + Bone *parent = bone->_parent; + if (parent != NULL) sortBone(parent); + bone->_sorted = true; + _updateCache.add(bone); +} + +void Skeleton::sortReset(Vector &bones) { + for (size_t i = 0, n = bones.size(); i < n; ++i) { + Bone *bone = bones[i]; + if (bone->_sorted) sortReset(bone->getChildren()); + bone->_sorted = false; + } +} diff --git a/cocos/editor-support/spine/Skeleton.h b/cocos/editor-support/spine/Skeleton.h index 74bc528d19db..adf2f333513d 100644 --- a/cocos/editor-support/spine/Skeleton.h +++ b/cocos/editor-support/spine/Skeleton.h @@ -1,174 +1,246 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_SKELETON_H_ -#define SPINE_SKELETON_H_ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spSkeleton { - spSkeletonData* const data; - - int bonesCount; - spBone** bones; - spBone* const root; - - int slotsCount; - spSlot** slots; - spSlot** drawOrder; - - int ikConstraintsCount; - spIkConstraint** ikConstraints; - - int transformConstraintsCount; - spTransformConstraint** transformConstraints; - - int pathConstraintsCount; - spPathConstraint** pathConstraints; - - spSkin* const skin; - spColor color; - float time; - int/*bool*/flipX, flipY; - float x, y; - -#ifdef __cplusplus - spSkeleton() : - data(0), - bonesCount(0), - bones(0), - root(0), - slotsCount(0), - slots(0), - drawOrder(0), - - ikConstraintsCount(0), - ikConstraints(0), - - transformConstraintsCount(0), - transformConstraints(0), - - skin(0), - color(), - time(0), - flipX(0), - flipY(0), - x(0), y(0) { - } -#endif -} spSkeleton; - -SP_API spSkeleton* spSkeleton_create (spSkeletonData* data); -SP_API void spSkeleton_dispose (spSkeleton* self); - -/* Caches information about bones and constraints. Must be called if bones or constraints, or weighted path attachments - * are added or removed. */ -SP_API void spSkeleton_updateCache (spSkeleton* self); -SP_API void spSkeleton_updateWorldTransform (const spSkeleton* self); - -/* Sets the bones, constraints, and slots to their setup pose values. */ -SP_API void spSkeleton_setToSetupPose (const spSkeleton* self); -/* Sets the bones and constraints to their setup pose values. */ -SP_API void spSkeleton_setBonesToSetupPose (const spSkeleton* self); -SP_API void spSkeleton_setSlotsToSetupPose (const spSkeleton* self); - -/* Returns 0 if the bone was not found. */ -SP_API spBone* spSkeleton_findBone (const spSkeleton* self, const char* boneName); -/* Returns -1 if the bone was not found. */ -SP_API int spSkeleton_findBoneIndex (const spSkeleton* self, const char* boneName); - -/* Returns 0 if the slot was not found. */ -SP_API spSlot* spSkeleton_findSlot (const spSkeleton* self, const char* slotName); -/* Returns -1 if the slot was not found. */ -SP_API int spSkeleton_findSlotIndex (const spSkeleton* self, const char* slotName); - -/* Sets the skin used to look up attachments before looking in the SkeletonData defaultSkin. Attachments from the new skin are - * attached if the corresponding attachment from the old skin was attached. If there was no old skin, each slot's setup mode - * attachment is attached from the new skin. - * @param skin May be 0.*/ -SP_API void spSkeleton_setSkin (spSkeleton* self, spSkin* skin); -/* Returns 0 if the skin was not found. See spSkeleton_setSkin. - * @param skinName May be 0. */ -SP_API int spSkeleton_setSkinByName (spSkeleton* self, const char* skinName); - -/* Returns 0 if the slot or attachment was not found. */ -SP_API spAttachment* spSkeleton_getAttachmentForSlotName (const spSkeleton* self, const char* slotName, const char* attachmentName); -/* Returns 0 if the slot or attachment was not found. */ -SP_API spAttachment* spSkeleton_getAttachmentForSlotIndex (const spSkeleton* self, int slotIndex, const char* attachmentName); -/* Returns 0 if the slot or attachment was not found. - * @param attachmentName May be 0. */ -SP_API int spSkeleton_setAttachment (spSkeleton* self, const char* slotName, const char* attachmentName); - -/* Returns 0 if the IK constraint was not found. */ -SP_API spIkConstraint* spSkeleton_findIkConstraint (const spSkeleton* self, const char* constraintName); - -/* Returns 0 if the transform constraint was not found. */ -SP_API spTransformConstraint* spSkeleton_findTransformConstraint (const spSkeleton* self, const char* constraintName); - -/* Returns 0 if the path constraint was not found. */ -SP_API spPathConstraint* spSkeleton_findPathConstraint (const spSkeleton* self, const char* constraintName); - -SP_API void spSkeleton_update (spSkeleton* self, float deltaTime); - -#ifdef SPINE_SHORT_NAMES -typedef spSkeleton Skeleton; -#define Skeleton_create(...) spSkeleton_create(__VA_ARGS__) -#define Skeleton_dispose(...) spSkeleton_dispose(__VA_ARGS__) -#define Skeleton_updateWorldTransform(...) spSkeleton_updateWorldTransform(__VA_ARGS__) -#define Skeleton_setToSetupPose(...) spSkeleton_setToSetupPose(__VA_ARGS__) -#define Skeleton_setBonesToSetupPose(...) spSkeleton_setBonesToSetupPose(__VA_ARGS__) -#define Skeleton_setSlotsToSetupPose(...) spSkeleton_setSlotsToSetupPose(__VA_ARGS__) -#define Skeleton_findBone(...) spSkeleton_findBone(__VA_ARGS__) -#define Skeleton_findBoneIndex(...) spSkeleton_findBoneIndex(__VA_ARGS__) -#define Skeleton_findSlot(...) spSkeleton_findSlot(__VA_ARGS__) -#define Skeleton_findSlotIndex(...) spSkeleton_findSlotIndex(__VA_ARGS__) -#define Skeleton_setSkin(...) spSkeleton_setSkin(__VA_ARGS__) -#define Skeleton_setSkinByName(...) spSkeleton_setSkinByName(__VA_ARGS__) -#define Skeleton_getAttachmentForSlotName(...) spSkeleton_getAttachmentForSlotName(__VA_ARGS__) -#define Skeleton_getAttachmentForSlotIndex(...) spSkeleton_getAttachmentForSlotIndex(__VA_ARGS__) -#define Skeleton_setAttachment(...) spSkeleton_setAttachment(__VA_ARGS__) -#define Skeleton_update(...) spSkeleton_update(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#ifndef Spine_Skeleton_h +#define Spine_Skeleton_h + +#include +#include +#include +#include +#include + +#include // std::numeric_limits + +namespace spine { +class SkeletonData; + +class Bone; + +class Updatable; + +class Slot; + +class IkConstraint; + +class PathConstraint; + +class TransformConstraint; + +class Skin; + +class Attachment; + +class SP_API Skeleton : public SpineObject { + friend class AnimationState; + + friend class SkeletonBounds; + + friend class SkeletonClipping; + + friend class AttachmentTimeline; + + friend class ColorTimeline; + + friend class DeformTimeline; + + friend class DrawOrderTimeline; + + friend class EventTimeline; + + friend class IkConstraintTimeline; + + friend class PathConstraintMixTimeline; + + friend class PathConstraintPositionTimeline; + + friend class PathConstraintSpacingTimeline; + + friend class ScaleTimeline; + + friend class ShearTimeline; + + friend class TransformConstraintTimeline; + + friend class TranslateTimeline; + + friend class TwoColorTimeline; + +public: + explicit Skeleton(SkeletonData *skeletonData); + + ~Skeleton(); + + /// Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added + /// or removed. + void updateCache(); + + void printUpdateCache(); + + /// Updates the world transform for each bone and applies constraints. + void updateWorldTransform(); + + /// Sets the bones, constraints, and slots to their setup pose values. + void setToSetupPose(); + + /// Sets the bones and constraints to their setup pose values. + void setBonesToSetupPose(); + + void setSlotsToSetupPose(); + + /// @return May be NULL. + Bone *findBone(const String &boneName); + + /// @return -1 if the bone was not found. + int findBoneIndex(const String &boneName); + + /// @return May be NULL. + Slot *findSlot(const String &slotName); + + /// @return -1 if the bone was not found. + int findSlotIndex(const String &slotName); + + /// Sets a skin by name (see setSkin). + void setSkin(const String &skinName); + + /// Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. + /// If there was no old skin, each slot's setup mode attachment is attached from the new skin. + /// After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling + /// See Skeleton::setSlotsToSetupPose() + /// Also, often AnimationState::apply(Skeleton&) is called before the next time the + /// skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin. + /// + /// @param newSkin May be NULL. + void setSkin(Skin *newSkin); + + /// @return May be NULL. + Attachment *getAttachment(const String &slotName, const String &attachmentName); + + /// @return May be NULL. + Attachment *getAttachment(int slotIndex, const String &attachmentName); + + /// @param attachmentName May be empty. + void setAttachment(const String &slotName, const String &attachmentName); + + /// @return May be NULL. + IkConstraint *findIkConstraint(const String &constraintName); + + /// @return May be NULL. + TransformConstraint *findTransformConstraint(const String &constraintName); + + /// @return May be NULL. + PathConstraint *findPathConstraint(const String &constraintName); + + void update(float delta); + + /// Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose. + /// @param outX The horizontal distance between the skeleton origin and the left side of the AABB. + /// @param outY The vertical distance between the skeleton origin and the bottom side of the AABB. + /// @param outWidth The width of the AABB + /// @param outHeight The height of the AABB. + /// @param outVertexBuffer Reference to hold a Vector of floats. This method will assign it with new floats as needed. + void getBounds(float &outX, float &outY, float &outWidth, float &outHeight, Vector &outVertexBuffer); + + Bone *getRootBone(); + + SkeletonData *getData(); + + Vector &getBones(); + + Vector &getUpdateCacheList(); + + Vector &getSlots(); + + Vector &getDrawOrder(); + + Vector &getIkConstraints(); + + Vector &getPathConstraints(); + + Vector &getTransformConstraints(); + + Skin *getSkin(); + + Color &getColor(); + + float getTime(); + + void setTime(float inValue); + + void setPosition(float x, float y); + + float getX(); + + void setX(float inValue); + + float getY(); + + void setY(float inValue); + + float getScaleX(); + + void setScaleX(float inValue); + + float getScaleY(); + + void setScaleY(float inValue); + +private: + SkeletonData *_data; + Vector _bones; + Vector _slots; + Vector _drawOrder; + Vector _ikConstraints; + Vector _transformConstraints; + Vector _pathConstraints; + Vector _updateCache; + Vector _updateCacheReset; + Skin *_skin; + Color _color; + float _time; + float _scaleX, _scaleY; + float _x, _y; + + void sortIkConstraint(IkConstraint *constraint); + + void sortPathConstraint(PathConstraint *constraint); + + void sortTransformConstraint(TransformConstraint *constraint); + + void sortPathConstraintAttachment(Skin *skin, size_t slotIndex, Bone &slotBone); + + void sortPathConstraintAttachment(Attachment *attachment, Bone &slotBone); + + void sortBone(Bone *bone); + + static void sortReset(Vector &bones); +}; } -#endif -#endif /* SPINE_SKELETON_H_*/ +#endif /* Spine_Skeleton_h */ diff --git a/cocos/editor-support/spine/SkeletonAnimation.cpp b/cocos/editor-support/spine/SkeletonAnimation.cpp index acf53fea8b57..a02dc7670da5 100644 --- a/cocos/editor-support/spine/SkeletonAnimation.cpp +++ b/cocos/editor-support/spine/SkeletonAnimation.cpp @@ -1,36 +1,35 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include #include -#include +#include #include USING_NS_CC; @@ -49,34 +48,34 @@ typedef struct _TrackEntryListeners { EventListener eventListener; } _TrackEntryListeners; -void animationCallback (spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event) { - ((SkeletonAnimation*)state->rendererObject)->onAnimationStateEvent(entry, type, event); +void animationCallback (AnimationState* state, EventType type, TrackEntry* entry, Event* event) { + ((SkeletonAnimation*)state->getRendererObject())->onAnimationStateEvent(entry, type, event); } -void trackEntryCallback (spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event) { - ((SkeletonAnimation*)state->rendererObject)->onTrackEntryEvent(entry, type, event); - if (type == SP_ANIMATION_DISPOSE) - if (entry->rendererObject) delete (spine::_TrackEntryListeners*)entry->rendererObject; +void trackEntryCallback (AnimationState* state, EventType type, TrackEntry* entry, Event* event) { + ((SkeletonAnimation*)state->getRendererObject())->onTrackEntryEvent(entry, type, event); + if (type == EventType_Dispose) + if (entry->getRendererObject()) delete (spine::_TrackEntryListeners*)entry->getRendererObject(); } -static _TrackEntryListeners* getListeners (spTrackEntry* entry) { - if (!entry->rendererObject) { - entry->rendererObject = new spine::_TrackEntryListeners(); - entry->listener = trackEntryCallback; +static _TrackEntryListeners* getListeners (TrackEntry* entry) { + if (!entry->getRendererObject()) { + entry->setRendererObject(new spine::_TrackEntryListeners()); + entry->setListener(trackEntryCallback); } - return (_TrackEntryListeners*)entry->rendererObject; + return (_TrackEntryListeners*)entry->getRendererObject(); } // -SkeletonAnimation* SkeletonAnimation::createWithData (spSkeletonData* skeletonData, bool ownsSkeletonData) { +SkeletonAnimation* SkeletonAnimation::createWithData (SkeletonData* skeletonData, bool ownsSkeletonData) { SkeletonAnimation* node = new SkeletonAnimation(); node->initWithData(skeletonData, ownsSkeletonData); node->autorelease(); return node; } -SkeletonAnimation* SkeletonAnimation::createWithJsonFile (const std::string& skeletonJsonFile, spAtlas* atlas, float scale) { +SkeletonAnimation* SkeletonAnimation::createWithJsonFile (const std::string& skeletonJsonFile, Atlas* atlas, float scale) { SkeletonAnimation* node = new SkeletonAnimation(); node->initWithJsonFile(skeletonJsonFile, atlas, scale); node->autorelease(); @@ -85,13 +84,12 @@ SkeletonAnimation* SkeletonAnimation::createWithJsonFile (const std::string& ske SkeletonAnimation* SkeletonAnimation::createWithJsonFile (const std::string& skeletonJsonFile, const std::string& atlasFile, float scale) { SkeletonAnimation* node = new SkeletonAnimation(); - spAtlas* atlas = spAtlas_createFromFile(atlasFile.c_str(), 0); - node->initWithJsonFile(skeletonJsonFile, atlas, scale); + node->initWithJsonFile(skeletonJsonFile, atlasFile, scale); node->autorelease(); return node; } -SkeletonAnimation* SkeletonAnimation::createWithBinaryFile (const std::string& skeletonBinaryFile, spAtlas* atlas, float scale) { +SkeletonAnimation* SkeletonAnimation::createWithBinaryFile (const std::string& skeletonBinaryFile, Atlas* atlas, float scale) { SkeletonAnimation* node = new SkeletonAnimation(); node->initWithBinaryFile(skeletonBinaryFile, atlas, scale); node->autorelease(); @@ -100,8 +98,7 @@ SkeletonAnimation* SkeletonAnimation::createWithBinaryFile (const std::string& s SkeletonAnimation* SkeletonAnimation::createWithBinaryFile (const std::string& skeletonBinaryFile, const std::string& atlasFile, float scale) { SkeletonAnimation* node = new SkeletonAnimation(); - spAtlas* atlas = spAtlas_createFromFile(atlasFile.c_str(), 0); - node->initWithBinaryFile(skeletonBinaryFile, atlas, scale); + node->initWithBinaryFile(skeletonBinaryFile, atlasFile, scale); node->autorelease(); return node; } @@ -111,9 +108,11 @@ void SkeletonAnimation::initialize () { super::initialize(); _ownsAnimationStateData = true; - _state = spAnimationState_create(spAnimationStateData_create(_skeleton->data)); - _state->rendererObject = this; - _state->listener = animationCallback; + _state = new (__FILE__, __LINE__) AnimationState(new (__FILE__, __LINE__) AnimationStateData(_skeleton->getData())); + _state->setRendererObject(this); + _state->setListener(animationCallback); + + _firstDraw = true; } SkeletonAnimation::SkeletonAnimation () @@ -121,124 +120,132 @@ SkeletonAnimation::SkeletonAnimation () } SkeletonAnimation::~SkeletonAnimation () { - if (_ownsAnimationStateData) spAnimationStateData_dispose(_state->data); - spAnimationState_dispose(_state); + if (_ownsAnimationStateData) delete _state->getData(); + delete _state; } void SkeletonAnimation::update (float deltaTime) { super::update(deltaTime); deltaTime *= _timeScale; - spAnimationState_update(_state, deltaTime); - spAnimationState_apply(_state, _skeleton); - spSkeleton_updateWorldTransform(_skeleton); + _state->update(deltaTime); + _state->apply(*_skeleton); + _skeleton->updateWorldTransform(); +} + +void SkeletonAnimation::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t transformFlags) { + if (_firstDraw) { + _firstDraw = false; + update(0); + } + super::draw(renderer, transform, transformFlags); } -void SkeletonAnimation::setAnimationStateData (spAnimationStateData* stateData) { +void SkeletonAnimation::setAnimationStateData (AnimationStateData* stateData) { CCASSERT(stateData, "stateData cannot be null."); - if (_ownsAnimationStateData) spAnimationStateData_dispose(_state->data); - spAnimationState_dispose(_state); + if (_ownsAnimationStateData) delete _state->getData(); + delete _state; _ownsAnimationStateData = false; - _state = spAnimationState_create(stateData); - _state->rendererObject = this; - _state->listener = animationCallback; + _state = new (__FILE__, __LINE__) AnimationState(stateData); + _state->setRendererObject(this); + _state->setListener(animationCallback); } void SkeletonAnimation::setMix (const std::string& fromAnimation, const std::string& toAnimation, float duration) { - spAnimationStateData_setMixByName(_state->data, fromAnimation.c_str(), toAnimation.c_str(), duration); + _state->getData()->setMix(fromAnimation.c_str(), toAnimation.c_str(), duration); } -spTrackEntry* SkeletonAnimation::setAnimation (int trackIndex, const std::string& name, bool loop) { - spAnimation* animation = spSkeletonData_findAnimation(_skeleton->data, name.c_str()); +TrackEntry* SkeletonAnimation::setAnimation (int trackIndex, const std::string& name, bool loop) { + Animation* animation = _skeleton->getData()->findAnimation(name.c_str()); if (!animation) { log("Spine: Animation not found: %s", name.c_str()); return 0; } - return spAnimationState_setAnimation(_state, trackIndex, animation, loop); + return _state->setAnimation(trackIndex, animation, loop); } -spTrackEntry* SkeletonAnimation::addAnimation (int trackIndex, const std::string& name, bool loop, float delay) { - spAnimation* animation = spSkeletonData_findAnimation(_skeleton->data, name.c_str()); +TrackEntry* SkeletonAnimation::addAnimation (int trackIndex, const std::string& name, bool loop, float delay) { + Animation* animation = _skeleton->getData()->findAnimation(name.c_str()); if (!animation) { log("Spine: Animation not found: %s", name.c_str()); return 0; } - return spAnimationState_addAnimation(_state, trackIndex, animation, loop, delay); + return _state->addAnimation(trackIndex, animation, loop, delay); } -spTrackEntry* SkeletonAnimation::setEmptyAnimation (int trackIndex, float mixDuration) { - return spAnimationState_setEmptyAnimation(_state, trackIndex, mixDuration); +TrackEntry* SkeletonAnimation::setEmptyAnimation (int trackIndex, float mixDuration) { + return _state->setEmptyAnimation(trackIndex, mixDuration); } void SkeletonAnimation::setEmptyAnimations (float mixDuration) { - spAnimationState_setEmptyAnimations(_state, mixDuration); + _state->setEmptyAnimations(mixDuration); } -spTrackEntry* SkeletonAnimation::addEmptyAnimation (int trackIndex, float mixDuration, float delay) { - return spAnimationState_addEmptyAnimation(_state, trackIndex, mixDuration, delay); +TrackEntry* SkeletonAnimation::addEmptyAnimation (int trackIndex, float mixDuration, float delay) { + return _state->addEmptyAnimation(trackIndex, mixDuration, delay); } -spAnimation* SkeletonAnimation::findAnimation(const std::string& name) const { - return spSkeletonData_findAnimation(_skeleton->data, name.c_str()); +Animation* SkeletonAnimation::findAnimation(const std::string& name) const { + return _skeleton->getData()->findAnimation(name.c_str()); } -spTrackEntry* SkeletonAnimation::getCurrent (int trackIndex) { - return spAnimationState_getCurrent(_state, trackIndex); +TrackEntry* SkeletonAnimation::getCurrent (int trackIndex) { + return _state->getCurrent(trackIndex); } void SkeletonAnimation::clearTracks () { - spAnimationState_clearTracks(_state); + _state->clearTracks(); } void SkeletonAnimation::clearTrack (int trackIndex) { - spAnimationState_clearTrack(_state, trackIndex); + _state->clearTrack(trackIndex); } -void SkeletonAnimation::onAnimationStateEvent (spTrackEntry* entry, spEventType type, spEvent* event) { +void SkeletonAnimation::onAnimationStateEvent (TrackEntry* entry, EventType type, Event* event) { switch (type) { - case SP_ANIMATION_START: + case EventType_Start: if (_startListener) _startListener(entry); break; - case SP_ANIMATION_INTERRUPT: + case EventType_Interrupt: if (_interruptListener) _interruptListener(entry); break; - case SP_ANIMATION_END: + case EventType_End: if (_endListener) _endListener(entry); break; - case SP_ANIMATION_DISPOSE: + case EventType_Dispose: if (_disposeListener) _disposeListener(entry); break; - case SP_ANIMATION_COMPLETE: + case EventType_Complete: if (_completeListener) _completeListener(entry); break; - case SP_ANIMATION_EVENT: + case EventType_Event: if (_eventListener) _eventListener(entry, event); break; } } -void SkeletonAnimation::onTrackEntryEvent (spTrackEntry* entry, spEventType type, spEvent* event) { - if (!entry->rendererObject) return; - _TrackEntryListeners* listeners = (_TrackEntryListeners*)entry->rendererObject; +void SkeletonAnimation::onTrackEntryEvent (TrackEntry* entry, EventType type, Event* event) { + if (!entry->getRendererObject()) return; + _TrackEntryListeners* listeners = (_TrackEntryListeners*)entry->getRendererObject(); switch (type) { - case SP_ANIMATION_START: + case EventType_Start: if (listeners->startListener) listeners->startListener(entry); break; - case SP_ANIMATION_INTERRUPT: + case EventType_Interrupt: if (listeners->interruptListener) listeners->interruptListener(entry); break; - case SP_ANIMATION_END: + case EventType_End: if (listeners->endListener) listeners->endListener(entry); break; - case SP_ANIMATION_DISPOSE: + case EventType_Dispose: if (listeners->disposeListener) listeners->disposeListener(entry); break; - case SP_ANIMATION_COMPLETE: + case EventType_Complete: if (listeners->completeListener) listeners->completeListener(entry); break; - case SP_ANIMATION_EVENT: + case EventType_Event: if (listeners->eventListener) listeners->eventListener(entry, event); break; } @@ -268,31 +275,31 @@ void SkeletonAnimation::setEventListener (const EventListener& listener) { _eventListener = listener; } -void SkeletonAnimation::setTrackStartListener (spTrackEntry* entry, const StartListener& listener) { +void SkeletonAnimation::setTrackStartListener (TrackEntry* entry, const StartListener& listener) { getListeners(entry)->startListener = listener; } -void SkeletonAnimation::setTrackInterruptListener (spTrackEntry* entry, const InterruptListener& listener) { +void SkeletonAnimation::setTrackInterruptListener (TrackEntry* entry, const InterruptListener& listener) { getListeners(entry)->interruptListener = listener; } -void SkeletonAnimation::setTrackEndListener (spTrackEntry* entry, const EndListener& listener) { +void SkeletonAnimation::setTrackEndListener (TrackEntry* entry, const EndListener& listener) { getListeners(entry)->endListener = listener; } -void SkeletonAnimation::setTrackDisposeListener (spTrackEntry* entry, const DisposeListener& listener) { +void SkeletonAnimation::setTrackDisposeListener (TrackEntry* entry, const DisposeListener& listener) { getListeners(entry)->disposeListener = listener; } -void SkeletonAnimation::setTrackCompleteListener (spTrackEntry* entry, const CompleteListener& listener) { +void SkeletonAnimation::setTrackCompleteListener (TrackEntry* entry, const CompleteListener& listener) { getListeners(entry)->completeListener = listener; } -void SkeletonAnimation::setTrackEventListener (spTrackEntry* entry, const EventListener& listener) { +void SkeletonAnimation::setTrackEventListener (TrackEntry* entry, const EventListener& listener) { getListeners(entry)->eventListener = listener; } -spAnimationState* SkeletonAnimation::getState() const { +AnimationState* SkeletonAnimation::getState() const { return _state; } diff --git a/cocos/editor-support/spine/SkeletonAnimation.h b/cocos/editor-support/spine/SkeletonAnimation.h index fc7f163cae23..aef5ff626633 100644 --- a/cocos/editor-support/spine/SkeletonAnimation.h +++ b/cocos/editor-support/spine/SkeletonAnimation.h @@ -1,31 +1,30 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef SPINE_SKELETONANIMATION_H_ @@ -37,26 +36,26 @@ namespace spine { -typedef std::function StartListener; -typedef std::function InterruptListener; -typedef std::function EndListener; -typedef std::function DisposeListener; -typedef std::function CompleteListener; -typedef std::function EventListener; +typedef std::function StartListener; +typedef std::function InterruptListener; +typedef std::function EndListener; +typedef std::function DisposeListener; +typedef std::function CompleteListener; +typedef std::function EventListener; /** Draws an animated skeleton, providing an AnimationState for applying one or more animations and queuing animations to be * played later. */ class SkeletonAnimation: public SkeletonRenderer { public: CREATE_FUNC(SkeletonAnimation); - static SkeletonAnimation* createWithData (spSkeletonData* skeletonData, bool ownsSkeletonData = false); - static SkeletonAnimation* createWithJsonFile (const std::string& skeletonJsonFile, spAtlas* atlas, float scale = 1); + static SkeletonAnimation* createWithData (SkeletonData* skeletonData, bool ownsSkeletonData = false); + static SkeletonAnimation* createWithJsonFile (const std::string& skeletonJsonFile, Atlas* atlas, float scale = 1); static SkeletonAnimation* createWithJsonFile (const std::string& skeletonJsonFile, const std::string& atlasFile, float scale = 1); - static SkeletonAnimation* createWithBinaryFile (const std::string& skeletonBinaryFile, spAtlas* atlas, float scale = 1); + static SkeletonAnimation* createWithBinaryFile (const std::string& skeletonBinaryFile, Atlas* atlas, float scale = 1); static SkeletonAnimation* createWithBinaryFile (const std::string& skeletonBinaryFile, const std::string& atlasFile, float scale = 1); // Use createWithJsonFile instead - CC_DEPRECATED_ATTRIBUTE static SkeletonAnimation* createWithFile (const std::string& skeletonJsonFile, spAtlas* atlas, float scale = 1) + CC_DEPRECATED_ATTRIBUTE static SkeletonAnimation* createWithFile (const std::string& skeletonJsonFile, Atlas* atlas, float scale = 1) { return SkeletonAnimation::createWithJsonFile(skeletonJsonFile, atlas, scale); } @@ -67,17 +66,18 @@ class SkeletonAnimation: public SkeletonRenderer { } virtual void update (float deltaTime) override; + virtual void draw (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags) override; - void setAnimationStateData (spAnimationStateData* stateData); + void setAnimationStateData (AnimationStateData* stateData); void setMix (const std::string& fromAnimation, const std::string& toAnimation, float duration); - spTrackEntry* setAnimation (int trackIndex, const std::string& name, bool loop); - spTrackEntry* addAnimation (int trackIndex, const std::string& name, bool loop, float delay = 0); - spTrackEntry* setEmptyAnimation (int trackIndex, float mixDuration); + TrackEntry* setAnimation (int trackIndex, const std::string& name, bool loop); + TrackEntry* addAnimation (int trackIndex, const std::string& name, bool loop, float delay = 0); + TrackEntry* setEmptyAnimation (int trackIndex, float mixDuration); void setEmptyAnimations (float mixDuration); - spTrackEntry* addEmptyAnimation (int trackIndex, float mixDuration, float delay = 0); - spAnimation* findAnimation(const std::string& name) const; - spTrackEntry* getCurrent (int trackIndex = 0); + TrackEntry* addEmptyAnimation (int trackIndex, float mixDuration, float delay = 0); + Animation* findAnimation(const std::string& name) const; + TrackEntry* getCurrent (int trackIndex = 0); void clearTracks (); void clearTrack (int trackIndex = 0); @@ -88,17 +88,17 @@ class SkeletonAnimation: public SkeletonRenderer { void setCompleteListener (const CompleteListener& listener); void setEventListener (const EventListener& listener); - void setTrackStartListener (spTrackEntry* entry, const StartListener& listener); - void setTrackInterruptListener (spTrackEntry* entry, const InterruptListener& listener); - void setTrackEndListener (spTrackEntry* entry, const EndListener& listener); - void setTrackDisposeListener (spTrackEntry* entry, const DisposeListener& listener); - void setTrackCompleteListener (spTrackEntry* entry, const CompleteListener& listener); - void setTrackEventListener (spTrackEntry* entry, const EventListener& listener); + void setTrackStartListener (TrackEntry* entry, const StartListener& listener); + void setTrackInterruptListener (TrackEntry* entry, const InterruptListener& listener); + void setTrackEndListener (TrackEntry* entry, const EndListener& listener); + void setTrackDisposeListener (TrackEntry* entry, const DisposeListener& listener); + void setTrackCompleteListener (TrackEntry* entry, const CompleteListener& listener); + void setTrackEventListener (TrackEntry* entry, const EventListener& listener); - virtual void onAnimationStateEvent (spTrackEntry* entry, spEventType type, spEvent* event); - virtual void onTrackEntryEvent (spTrackEntry* entry, spEventType type, spEvent* event); + virtual void onAnimationStateEvent (TrackEntry* entry, EventType type, Event* event); + virtual void onTrackEntryEvent (TrackEntry* entry, EventType type, Event* event); - spAnimationState* getState() const; + AnimationState* getState() const; CC_CONSTRUCTOR_ACCESS: SkeletonAnimation (); @@ -106,9 +106,10 @@ class SkeletonAnimation: public SkeletonRenderer { virtual void initialize () override; protected: - spAnimationState* _state; + AnimationState* _state; bool _ownsAnimationStateData; + bool _firstDraw; StartListener _startListener; InterruptListener _interruptListener; diff --git a/cocos/editor-support/spine/SkeletonBatch.cpp b/cocos/editor-support/spine/SkeletonBatch.cpp index 1978abb0117c..94de08fef99c 100644 --- a/cocos/editor-support/spine/SkeletonBatch.cpp +++ b/cocos/editor-support/spine/SkeletonBatch.cpp @@ -1,35 +1,34 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include -#include +#include #include USING_NS_CC; @@ -37,6 +36,8 @@ USING_NS_CC; using std::max; #define INITIAL_SIZE (10000) +#include "renderer/ccShaders.h" + namespace spine { static SkeletonBatch* instance = nullptr; @@ -54,14 +55,24 @@ void SkeletonBatch::destroyInstance () { } SkeletonBatch::SkeletonBatch () { + + _programState = new backend::ProgramState(positionTextureColor_vert, positionTextureColor_frag); + + auto vertexLayout = _programState->getVertexLayout(); + + vertexLayout->setAttribute("a_position", 0, backend::VertexFormat::FLOAT3, offsetof(V3F_C4B_T2F, vertices), false); + vertexLayout->setAttribute("a_color", 2, backend::VertexFormat::UBYTE4, offsetof(V3F_C4B_T2F, colors), true); + vertexLayout->setAttribute("a_texCoord", 1, backend::VertexFormat::FLOAT2, offsetof(V3F_C4B_T2F, texCoords), false); + vertexLayout->setLayout(sizeof(_vertices[0])); + + + _locMVP = _programState->getUniformLocation("u_MVPMatrix"); + _locTexture = _programState->getUniformLocation("u_texture"); + for (unsigned int i = 0; i < INITIAL_SIZE; i++) { - _commandsPool.push_back(new TrianglesCommand()); + _commandsPool.push_back(createNewTrianglesCommand()); } - - _indices = spUnsignedShortArray_create(8); - - reset (); - + reset (); // callback after drawing is finished so we can clear out the batch state // for the next frame Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_AFTER_DRAW_RESET_POSITION, [this](EventCustom* eventCustom){ @@ -71,13 +82,13 @@ SkeletonBatch::SkeletonBatch () { SkeletonBatch::~SkeletonBatch () { Director::getInstance()->getEventDispatcher()->removeCustomEventListeners(EVENT_AFTER_DRAW_RESET_POSITION); - - spUnsignedShortArray_dispose(_indices); for (unsigned int i = 0; i < _commandsPool.size(); i++) { + CC_SAFE_RELEASE(_commandsPool[i]->getPipelineDescriptor().programState); delete _commandsPool[i]; _commandsPool[i] = nullptr; } + CC_SAFE_RELEASE(_programState); } void SkeletonBatch::update (float delta) { @@ -107,11 +118,11 @@ void SkeletonBatch::deallocateVertices(uint32_t numVertices) { unsigned short* SkeletonBatch::allocateIndices(uint32_t numIndices) { - if (_indices->capacity - _indices->size < numIndices) { - unsigned short* oldData = _indices->items; - int oldSize = _indices->size; - spUnsignedShortArray_ensureCapacity(_indices, _indices->size + numIndices); - unsigned short* newData = _indices->items; + if (_indices.getCapacity() - _indices.size() < numIndices) { + unsigned short* oldData = _indices.buffer(); + int oldSize = _indices.size(); + _indices.ensureCapacity(_indices.size() + numIndices); + unsigned short* newData = _indices.buffer(); for (uint32_t i = 0; i < this->_nextFreeCommand; i++) { TrianglesCommand* command = _commandsPool[i]; cocos2d::TrianglesCommand::Triangles& triangles = (cocos2d::TrianglesCommand::Triangles&)command->getTriangles(); @@ -121,36 +132,57 @@ unsigned short* SkeletonBatch::allocateIndices(uint32_t numIndices) { } } - unsigned short* indices = _indices->items + _indices->size; - _indices->size += numIndices; + unsigned short* indices = _indices.buffer() + _indices.size(); + _indices.setSize(_indices.size() + numIndices, 0); return indices; } void SkeletonBatch::deallocateIndices(uint32_t numIndices) { - _indices->size -= numIndices; + _indices.setSize(_indices.size() - numIndices, 0); } -cocos2d::TrianglesCommand* SkeletonBatch::addCommand(cocos2d::Renderer* renderer, float globalOrder, cocos2d::Texture2D* texture, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags) { +cocos2d::TrianglesCommand* SkeletonBatch::addCommand(cocos2d::Renderer* renderer, float globalOrder, cocos2d::Texture2D* texture, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags) { TrianglesCommand* command = nextFreeCommand(); - command->init(globalOrder, texture, blendType, triangles, mv, flags); - renderer->addCommand(command); + const cocos2d::Mat4& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); + auto &pipelineDescriptor = command->getPipelineDescriptor(); + + auto programState = command->getPipelineDescriptor().programState; + CCASSERT(programState, "programState should not be null"); + + programState->setUniform(_locMVP, projectionMat.m, sizeof(projectionMat.m)); + programState->setTexture(_locTexture, 0, texture->getBackendTexture()); + + command->init(globalOrder, texture, blendType, triangles, mv, flags); + renderer->addCommand(command); return command; } void SkeletonBatch::reset() { _nextFreeCommand = 0; _numVertices = 0; - _indices->size = 0; + _indices.setSize(0, 0); } cocos2d::TrianglesCommand* SkeletonBatch::nextFreeCommand() { if (_commandsPool.size() <= _nextFreeCommand) { - size_t newSize = _commandsPool.size() * 2 + 1; - for (size_t i = _commandsPool.size(); i < newSize; i++) { - _commandsPool.push_back(new TrianglesCommand()); + unsigned int newSize = _commandsPool.size() * 2 + 1; + for (int i = _commandsPool.size(); i < newSize; i++) { + _commandsPool.push_back(createNewTrianglesCommand()); } } - return _commandsPool[_nextFreeCommand++]; + auto *command = _commandsPool[_nextFreeCommand++]; + auto& pipelineDescriptor = command->getPipelineDescriptor(); + if (pipelineDescriptor.programState == nullptr) + { + CCASSERT(_programState, "programState should not be null"); + pipelineDescriptor.programState = _programState->clone(); + } + return command; +} + +cocos2d::TrianglesCommand *SkeletonBatch::createNewTrianglesCommand() { + auto* command = new TrianglesCommand(); + return command; } } diff --git a/cocos/editor-support/spine/SkeletonBatch.h b/cocos/editor-support/spine/SkeletonBatch.h index eea569739f1a..c1be80840cca 100644 --- a/cocos/editor-support/spine/SkeletonBatch.h +++ b/cocos/editor-support/spine/SkeletonBatch.h @@ -1,31 +1,30 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef SPINE_SKELETONBATCH_H_ @@ -49,7 +48,7 @@ namespace spine { void deallocateVertices(uint32_t numVertices); unsigned short* allocateIndices(uint32_t numIndices); void deallocateIndices(uint32_t numVertices); - cocos2d::TrianglesCommand* addCommand(cocos2d::Renderer* renderer, float globalOrder, cocos2d::Texture2D* texture, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); + cocos2d::TrianglesCommand* addCommand(cocos2d::Renderer* renderer, float globalOrder, cocos2d::Texture2D* texture, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); protected: SkeletonBatch (); @@ -58,17 +57,22 @@ namespace spine { void reset (); cocos2d::TrianglesCommand* nextFreeCommand (); + + cocos2d::TrianglesCommand* createNewTrianglesCommand(); + cocos2d::backend::ProgramState * _programState = nullptr; + cocos2d::backend::UniformLocation _locMVP; + cocos2d::backend::UniformLocation _locTexture; // pool of commands std::vector _commandsPool; - uint32_t _nextFreeCommand; + uint32_t _nextFreeCommand; // pool of vertices - std::vector _vertices; - uint32_t _numVertices; + std::vector _vertices; + uint32_t _numVertices; // pool of indices - spUnsignedShortArray* _indices; + Vector _indices; }; } diff --git a/cocos/editor-support/spine/SkeletonBinary.cpp b/cocos/editor-support/spine/SkeletonBinary.cpp new file mode 100644 index 000000000000..e633b70d1c54 --- /dev/null +++ b/cocos/editor-support/spine/SkeletonBinary.cpp @@ -0,0 +1,1052 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace spine; + +const int SkeletonBinary::BONE_ROTATE = 0; +const int SkeletonBinary::BONE_TRANSLATE = 1; +const int SkeletonBinary::BONE_SCALE = 2; +const int SkeletonBinary::BONE_SHEAR = 3; + +const int SkeletonBinary::SLOT_ATTACHMENT = 0; +const int SkeletonBinary::SLOT_COLOR = 1; +const int SkeletonBinary::SLOT_TWO_COLOR = 2; + +const int SkeletonBinary::PATH_POSITION = 0; +const int SkeletonBinary::PATH_SPACING = 1; +const int SkeletonBinary::PATH_MIX = 2; + +const int SkeletonBinary::CURVE_LINEAR = 0; +const int SkeletonBinary::CURVE_STEPPED = 1; +const int SkeletonBinary::CURVE_BEZIER = 2; + +SkeletonBinary::SkeletonBinary(Atlas *atlasArray) : _attachmentLoader( + new(__FILE__, __LINE__) AtlasAttachmentLoader(atlasArray)), _error(), _scale(1), _ownsLoader(true) { + +} + +SkeletonBinary::SkeletonBinary(AttachmentLoader *attachmentLoader) : _attachmentLoader(attachmentLoader), _error(), + _scale(1), _ownsLoader(false) { + assert(_attachmentLoader != NULL); +} + +SkeletonBinary::~SkeletonBinary() { + ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); + _linkedMeshes.clear(); + + if (_ownsLoader) { + delete _attachmentLoader; + } +} + +SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, const int length) { + bool nonessential; + SkeletonData *skeletonData; + + DataInput *input = new(__FILE__, __LINE__) DataInput(); + input->cursor = binary; + input->end = binary + length; + + _linkedMeshes.clear(); + + skeletonData = new(__FILE__, __LINE__) SkeletonData(); + + char *skeletonData_hash = readString(input); + skeletonData->_hash.own(skeletonData_hash); + + char *skeletonData_version = readString(input); + skeletonData->_version.own(skeletonData_version); + + skeletonData->_width = readFloat(input); + skeletonData->_height = readFloat(input); + + nonessential = readBoolean(input); + + if (nonessential) { + /* Skip images path, audio path & fps */ + skeletonData->_fps = readFloat(input); + skeletonData->_imagesPath.own(readString(input)); + skeletonData->_audioPath.own(readString(input)); + } + + /* Bones. */ + int numBones = readVarint(input, true); + skeletonData->_bones.setSize(numBones, 0); + for (int i = 0; i < numBones; ++i) { + const char *name = readString(input); + BoneData *parent = i == 0 ? 0 : skeletonData->_bones[readVarint(input, true)]; + BoneData *data = new(__FILE__, __LINE__) BoneData(i, String(name, true), parent); + data->_rotation = readFloat(input); + data->_x = readFloat(input) * _scale; + data->_y = readFloat(input) * _scale; + data->_scaleX = readFloat(input); + data->_scaleY = readFloat(input); + data->_shearX = readFloat(input); + data->_shearY = readFloat(input); + data->_length = readFloat(input) * _scale; + data->_transformMode = static_cast(readVarint(input, true)); + if (nonessential) { + /* Skip bone color. */ + readInt(input); + } + skeletonData->_bones[i] = data; + } + + /* Slots. */ + int slotsCount = readVarint(input, true); + skeletonData->_slots.setSize(slotsCount, 0); + for (int i = 0; i < slotsCount; ++i) { + const char *slotName = readString(input); + BoneData *boneData = skeletonData->_bones[readVarint(input, true)]; + SlotData *slotData = new(__FILE__, __LINE__) SlotData(i, String(slotName, true), *boneData); + + readColor(input, slotData->getColor()); + unsigned char r = readByte(input); + unsigned char g = readByte(input); + unsigned char b = readByte(input); + unsigned char a = readByte(input); + if (!(r == 0xff && g == 0xff && b == 0xff && a == 0xff)) { + slotData->getDarkColor().set(r / 255.0f, g / 255.0f, b / 255.0f, 1); + slotData->setHasDarkColor(true); + } + slotData->_attachmentName.own(readString(input)); + slotData->_blendMode = static_cast(readVarint(input, true)); + skeletonData->_slots[i] = slotData; + } + + /* IK constraints. */ + int ikConstraintsCount = readVarint(input, true); + skeletonData->_ikConstraints.setSize(ikConstraintsCount, 0); + for (int i = 0; i < ikConstraintsCount; ++i) { + const char *name = readString(input); + IkConstraintData *data = new(__FILE__, __LINE__) IkConstraintData(String(name, true)); + data->_order = readVarint(input, true); + int bonesCount = readVarint(input, true); + data->_bones.setSize(bonesCount, 0); + for (int ii = 0; ii < bonesCount; ++ii) { + data->_bones[ii] = skeletonData->_bones[readVarint(input, true)]; + } + data->_target = skeletonData->_bones[readVarint(input, true)]; + data->_mix = readFloat(input); + data->_bendDirection = readSByte(input); + data->_compress = readBoolean(input); + data->_stretch = readBoolean(input); + data->_uniform = readBoolean(input); + skeletonData->_ikConstraints[i] = data; + } + + /* Transform constraints. */ + int transformConstraintsCount = readVarint(input, true); + skeletonData->_transformConstraints.setSize(transformConstraintsCount, 0); + for (int i = 0; i < transformConstraintsCount; ++i) { + const char *name = readString(input); + TransformConstraintData *data = new(__FILE__, __LINE__) TransformConstraintData(String(name, true)); + data->_order = readVarint(input, true); + int bonesCount = readVarint(input, true); + data->_bones.setSize(bonesCount, 0); + for (int ii = 0; ii < bonesCount; ++ii) { + data->_bones[ii] = skeletonData->_bones[readVarint(input, true)]; + } + data->_target = skeletonData->_bones[readVarint(input, true)]; + data->_local = readBoolean(input); + data->_relative = readBoolean(input); + data->_offsetRotation = readFloat(input); + data->_offsetX = readFloat(input) * _scale; + data->_offsetY = readFloat(input) * _scale; + data->_offsetScaleX = readFloat(input); + data->_offsetScaleY = readFloat(input); + data->_offsetShearY = readFloat(input); + data->_rotateMix = readFloat(input); + data->_translateMix = readFloat(input); + data->_scaleMix = readFloat(input); + data->_shearMix = readFloat(input); + skeletonData->_transformConstraints[i] = data; + } + + /* Path constraints */ + int pathConstraintsCount = readVarint(input, true); + skeletonData->_pathConstraints.setSize(pathConstraintsCount, 0); + for (int i = 0; i < pathConstraintsCount; ++i) { + const char *name = readString(input); + PathConstraintData *data = new(__FILE__, __LINE__) PathConstraintData(String(name, true)); + data->_order = readVarint(input, true); + int bonesCount = readVarint(input, true); + data->_bones.setSize(bonesCount, 0); + for (int ii = 0; ii < bonesCount; ++ii) { + data->_bones[ii] = skeletonData->_bones[readVarint(input, true)]; + } + data->_target = skeletonData->_slots[readVarint(input, true)]; + data->_positionMode = static_cast(readVarint(input, true)); + data->_spacingMode = static_cast(readVarint(input, true)); + data->_rotateMode = static_cast(readVarint(input, true)); + data->_offsetRotation = readFloat(input); + data->_position = readFloat(input); + if (data->_positionMode == PositionMode_Fixed) data->_position *= _scale; + data->_spacing = readFloat(input); + if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) + data->_spacing *= _scale; + data->_rotateMix = readFloat(input); + data->_translateMix = readFloat(input); + skeletonData->_pathConstraints[i] = data; + } + + /* Default skin. */ + skeletonData->_defaultSkin = readSkin(input, "default", skeletonData, nonessential); + int skinsCount = readVarint(input, true); + if (skeletonData->_defaultSkin) { + ++skinsCount; + } + skeletonData->_skins.setSize(skinsCount, 0); + if (skeletonData->_defaultSkin) { + skeletonData->_skins[0] = skeletonData->_defaultSkin; + } + + /* Skins. */ + for (size_t i = skeletonData->_defaultSkin ? 1 : 0; i < skeletonData->_skins.size(); ++i) { + String skinName(readString(input), true); + skeletonData->_skins[i] = readSkin(input, skinName, skeletonData, nonessential); + } + + /* Linked meshes. */ + for (int i = 0, n = _linkedMeshes.size(); i < n; ++i) { + LinkedMesh *linkedMesh = _linkedMeshes[i]; + Skin *skin = linkedMesh->_skin.length() == 0 ? skeletonData->getDefaultSkin() : skeletonData->findSkin( + linkedMesh->_skin); + if (skin == NULL) { + delete input; + delete skeletonData; + setError("Skin not found: ", linkedMesh->_skin.buffer()); + return NULL; + } + Attachment *parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent); + if (parent == NULL) { + delete input; + delete skeletonData; + setError("Parent mesh not found: ", linkedMesh->_parent.buffer()); + return NULL; + } + linkedMesh->_mesh->setParentMesh(static_cast(parent)); + linkedMesh->_mesh->updateUVs(); + _attachmentLoader->configureAttachment(linkedMesh->_mesh); + } + ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); + _linkedMeshes.clear(); + + /* Events. */ + int eventsCount = readVarint(input, true); + skeletonData->_events.setSize(eventsCount, 0); + for (int i = 0; i < eventsCount; ++i) { + const char *name = readString(input); + EventData *eventData = new(__FILE__, __LINE__) EventData(String(name, true)); + eventData->_intValue = readVarint(input, false); + eventData->_floatValue = readFloat(input); + eventData->_stringValue.own(readString(input)); + eventData->_audioPath.own(readString(input)); // skip audio path + if (!eventData->_audioPath.isEmpty()) { + eventData->_volume = readFloat(input); + eventData->_balance = readFloat(input); + } + skeletonData->_events[i] = eventData; + } + + /* Animations. */ + int animationsCount = readVarint(input, true); + skeletonData->_animations.setSize(animationsCount, 0); + for (int i = 0; i < animationsCount; ++i) { + String name(readString(input), true); + Animation *animation = readAnimation(name, input, skeletonData); + if (!animation) { + delete input; + delete skeletonData; + return NULL; + } + skeletonData->_animations[i] = animation; + } + + delete input; + return skeletonData; +} + +SkeletonData *SkeletonBinary::readSkeletonDataFile(const String &path) { + int length; + SkeletonData *skeletonData; + const char *binary = SpineExtension::readFile(path.buffer(), &length); + if (length == 0 || !binary) { + setError("Unable to read skeleton file: ", path.buffer()); + return NULL; + } + skeletonData = readSkeletonData((unsigned char *) binary, length); + SpineExtension::free(binary, __FILE__, __LINE__); + return skeletonData; +} + +void SkeletonBinary::setError(const char *value1, const char *value2) { + char message[256]; + int length; + strcpy(message, value1); + length = (int) strlen(value1); + if (value2) { + strncat(message + length, value2, 255 - length); + } + + _error = String(message); +} + +char *SkeletonBinary::readString(DataInput *input) { + int length = readVarint(input, true); + char *string; + if (length == 0) { + return NULL; + } + string = SpineExtension::alloc(length, __FILE__, __LINE__); + memcpy(string, input->cursor, length - 1); + input->cursor += length - 1; + string[length - 1] = '\0'; + return string; +} + +float SkeletonBinary::readFloat(DataInput *input) { + union { + int intValue; + float floatValue; + } intToFloat; + + intToFloat.intValue = readInt(input); + + return intToFloat.floatValue; +} + +unsigned char SkeletonBinary::readByte(DataInput *input) { + return *input->cursor++; +} + +signed char SkeletonBinary::readSByte(DataInput *input) { + return (signed char) readByte(input); +} + +bool SkeletonBinary::readBoolean(DataInput *input) { + return readByte(input) != 0; +} + +int SkeletonBinary::readInt(DataInput *input) { + int result = readByte(input); + result <<= 8; + result |= readByte(input); + result <<= 8; + result |= readByte(input); + result <<= 8; + result |= readByte(input); + return result; +} + +void SkeletonBinary::readColor(DataInput *input, Color &color) { + color.r = readByte(input) / 255.0f; + color.g = readByte(input) / 255.0f; + color.b = readByte(input) / 255.0f; + color.a = readByte(input) / 255.0f; +} + +int SkeletonBinary::readVarint(DataInput *input, bool optimizePositive) { + unsigned char b = readByte(input); + int value = b & 0x7F; + if (b & 0x80) { + b = readByte(input); + value |= (b & 0x7F) << 7; + if (b & 0x80) { + b = readByte(input); + value |= (b & 0x7F) << 14; + if (b & 0x80) { + b = readByte(input); + value |= (b & 0x7F) << 21; + if (b & 0x80) value |= (readByte(input) & 0x7F) << 28; + } + } + } + + if (!optimizePositive) { + value = (((unsigned int) value >> 1) ^ -(value & 1)); + } + + return value; +} + +Skin * +SkeletonBinary::readSkin(DataInput *input, const String &skinName, SkeletonData *skeletonData, bool nonessential) { + int slotCount = readVarint(input, true); + if (slotCount == 0) return NULL; + Skin *skin = new(__FILE__, __LINE__) Skin(skinName); + for (int i = 0; i < slotCount; ++i) { + int slotIndex = readVarint(input, true); + for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { + String name(readString(input), true); + Attachment *attachment = readAttachment(input, skin, slotIndex, name, skeletonData, nonessential); + if (attachment) skin->addAttachment(slotIndex, String(name), attachment); + } + } + return skin; +} + +Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slotIndex, const String &attachmentName, + SkeletonData *skeletonData, bool nonessential) { + String name(readString(input), true); + if (name.isEmpty()) name = attachmentName; + + AttachmentType type = static_cast(readByte(input)); + switch (type) { + case AttachmentType_Region: { + String path(readString(input), true); + if (path.isEmpty()) path = name; + RegionAttachment *region = _attachmentLoader->newRegionAttachment(*skin, String(name), String(path)); + region->_path = path; + region->_rotation = readFloat(input); + region->_x = readFloat(input) * _scale; + region->_y = readFloat(input) * _scale; + region->_scaleX = readFloat(input); + region->_scaleY = readFloat(input); + region->_width = readFloat(input) * _scale; + region->_height = readFloat(input) * _scale; + readColor(input, region->getColor()); + region->updateOffset(); + _attachmentLoader->configureAttachment(region); + return region; + } + case AttachmentType_Boundingbox: { + int vertexCount = readVarint(input, true); + BoundingBoxAttachment *box = _attachmentLoader->newBoundingBoxAttachment(*skin, String(name)); + readVertices(input, static_cast(box), vertexCount); + if (nonessential) { + /* Skip color. */ + readInt(input); + } + _attachmentLoader->configureAttachment(box); + return box; + } + case AttachmentType_Mesh: { + int vertexCount; + MeshAttachment *mesh; + String path(readString(input), true); + if (path.isEmpty()) path = name; + + mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path)); + mesh->_path = path; + readColor(input, mesh->getColor()); + vertexCount = readVarint(input, true); + readFloatArray(input, vertexCount << 1, 1, mesh->getRegionUVs()); + readShortArray(input, mesh->getTriangles()); + readVertices(input, static_cast(mesh), vertexCount); + mesh->updateUVs(); + mesh->_hullLength = readVarint(input, true) << 1; + if (nonessential) { + readShortArray(input, mesh->getEdges()); + mesh->_width = readFloat(input) * _scale; + mesh->_height = readFloat(input) * _scale; + } else { + mesh->_width = 0; + mesh->_height = 0; + } + _attachmentLoader->configureAttachment(mesh); + return mesh; + } + case AttachmentType_Linkedmesh: { + String path(readString(input), true); + if (path.isEmpty()) path = name; + + MeshAttachment *mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path)); + mesh->_path = path; + readColor(input, mesh->getColor()); + String skinName(readString(input), true); + String parent(readString(input), true); + mesh->_inheritDeform = readBoolean(input); + if (nonessential) { + mesh->_width = readFloat(input) * _scale; + mesh->_height = readFloat(input) * _scale; + } + + LinkedMesh *linkedMesh = new(__FILE__, __LINE__) LinkedMesh(mesh, String(skinName), slotIndex, + String(parent)); + _linkedMeshes.add(linkedMesh); + return mesh; + } + case AttachmentType_Path: { + PathAttachment *path = _attachmentLoader->newPathAttachment(*skin, String(name)); + path->_closed = readBoolean(input); + path->_constantSpeed = readBoolean(input); + int vertexCount = readVarint(input, true); + readVertices(input, static_cast(path), vertexCount); + int lengthsLength = vertexCount / 3; + path->_lengths.setSize(lengthsLength, 0); + for (int i = 0; i < lengthsLength; ++i) { + path->_lengths[i] = readFloat(input) * _scale; + } + if (nonessential) { + /* Skip color. */ + readInt(input); + } + _attachmentLoader->configureAttachment(path); + return path; + } + case AttachmentType_Point: { + PointAttachment *point = _attachmentLoader->newPointAttachment(*skin, String(name)); + point->_rotation = readFloat(input); + point->_x = readFloat(input) * _scale; + point->_y = readFloat(input) * _scale; + + if (nonessential) { + /* Skip color. */ + readInt(input); + } + _attachmentLoader->configureAttachment(point); + return point; + } + case AttachmentType_Clipping: { + int endSlotIndex = readVarint(input, true); + int vertexCount = readVarint(input, true); + ClippingAttachment *clip = _attachmentLoader->newClippingAttachment(*skin, name); + readVertices(input, static_cast(clip), vertexCount); + clip->_endSlot = skeletonData->_slots[endSlotIndex]; + if (nonessential) { + /* Skip color. */ + readInt(input); + } + _attachmentLoader->configureAttachment(clip); + return clip; + } + } + return NULL; +} + +void SkeletonBinary::readVertices(DataInput *input, VertexAttachment *attachment, int vertexCount) { + float scale = _scale; + int verticesLength = vertexCount << 1; + attachment->setWorldVerticesLength(vertexCount << 1); + + if (!readBoolean(input)) { + readFloatArray(input, verticesLength, scale, attachment->getVertices()); + return; + } + + Vector &vertices = attachment->getVertices(); + Vector &bones = attachment->getBones(); + vertices.ensureCapacity(verticesLength * 3 * 3); + bones.ensureCapacity(verticesLength * 3); + + for (int i = 0; i < vertexCount; ++i) { + int boneCount = readVarint(input, true); + bones.add(boneCount); + for (int ii = 0; ii < boneCount; ++ii) { + bones.add(readVarint(input, true)); + vertices.add(readFloat(input) * scale); + vertices.add(readFloat(input) * scale); + vertices.add(readFloat(input)); + } + } +} + +void SkeletonBinary::readFloatArray(DataInput *input, int n, float scale, Vector &array) { + array.setSize(n, 0); + + int i; + if (scale == 1) { + for (i = 0; i < n; ++i) { + array[i] = readFloat(input); + } + } else { + for (i = 0; i < n; ++i) { + array[i] = readFloat(input) * scale; + } + } +} + +void SkeletonBinary::readShortArray(DataInput *input, Vector &array) { + int n = readVarint(input, true); + array.setSize(n, 0); + + int i; + for (i = 0; i < n; ++i) { + array[i] = readByte(input) << 8; + array[i] |= readByte(input); + } +} + +Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, SkeletonData *skeletonData) { + Vector timelines; + float scale = _scale; + float duration = 0; + + // Slot timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int slotIndex = readVarint(input, true); + for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { + unsigned char timelineType = readByte(input); + int frameCount = readVarint(input, true); + switch (timelineType) { + case SLOT_ATTACHMENT: { + AttachmentTimeline *timeline = new(__FILE__, __LINE__) AttachmentTimeline(frameCount); + timeline->_slotIndex = slotIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + String attachmentName = String(readString(input), true); + timeline->setFrame(frameIndex, time, attachmentName); + } + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[frameCount - 1]); + break; + } + case SLOT_COLOR: { + ColorTimeline *timeline = new(__FILE__, __LINE__) ColorTimeline(frameCount); + timeline->_slotIndex = slotIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + int color = readInt(input); + float r = ((color & 0xff000000) >> 24) / 255.0f; + float g = ((color & 0x00ff0000) >> 16) / 255.0f; + float b = ((color & 0x0000ff00) >> 8) / 255.0f; + float a = ((color & 0x000000ff)) / 255.0f; + timeline->setFrame(frameIndex, time, r, g, b, a); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * ColorTimeline::ENTRIES]); + break; + } + case SLOT_TWO_COLOR: { + TwoColorTimeline *timeline = new(__FILE__, __LINE__) TwoColorTimeline(frameCount); + timeline->_slotIndex = slotIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + int color = readInt(input); + float r = ((color & 0xff000000) >> 24) / 255.0f; + float g = ((color & 0x00ff0000) >> 16) / 255.0f; + float b = ((color & 0x0000ff00) >> 8) / 255.0f; + float a = ((color & 0x000000ff)) / 255.0f; + int color2 = readInt(input); // 0x00rrggbb + float r2 = ((color2 & 0x00ff0000) >> 16) / 255.0f; + float g2 = ((color2 & 0x0000ff00) >> 8) / 255.0f; + float b2 = ((color2 & 0x000000ff)) / 255.0f; + + timeline->setFrame(frameIndex, time, r, g, b, a, r2, g2, b2); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * TwoColorTimeline::ENTRIES]); + break; + } + default: { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError("Invalid timeline type for a slot: ", skeletonData->_slots[slotIndex]->_name.buffer()); + return NULL; + } + } + } + } + + // Bone timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int boneIndex = readVarint(input, true); + for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { + unsigned char timelineType = readByte(input); + int frameCount = readVarint(input, true); + switch (timelineType) { + case BONE_ROTATE: { + RotateTimeline *timeline = new(__FILE__, __LINE__) RotateTimeline(frameCount); + timeline->_boneIndex = boneIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + float degrees = readFloat(input); + timeline->setFrame(frameIndex, time, degrees); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * RotateTimeline::ENTRIES]); + break; + } + case BONE_TRANSLATE: + case BONE_SCALE: + case BONE_SHEAR: { + TranslateTimeline *timeline; + float timelineScale = 1; + if (timelineType == BONE_SCALE) { + timeline = new(__FILE__, __LINE__) ScaleTimeline(frameCount); + } else if (timelineType == BONE_SHEAR) { + timeline = new(__FILE__, __LINE__) ShearTimeline(frameCount); + } else { + timeline = new(__FILE__, __LINE__) TranslateTimeline(frameCount); + timelineScale = scale; + } + timeline->_boneIndex = boneIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + float x = readFloat(input) * timelineScale; + float y = readFloat(input) * timelineScale; + timeline->setFrame(frameIndex, time, x, y); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.add(timeline); + duration = MathUtil::max(duration, + timeline->_frames[(frameCount - 1) * TranslateTimeline::ENTRIES]); + break; + } + default: { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError("Invalid timeline type for a bone: ", skeletonData->_bones[boneIndex]->_name.buffer()); + return NULL; + } + } + } + } + + // IK timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int index = readVarint(input, true); + int frameCount = readVarint(input, true); + IkConstraintTimeline *timeline = new(__FILE__, __LINE__) IkConstraintTimeline(frameCount); + timeline->_ikConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + float mix = readFloat(input); + signed char bendDirection = readSByte(input); + bool compress = readBoolean(input); + bool stretch = readBoolean(input); + timeline->setFrame(frameIndex, time, mix, bendDirection, compress, stretch); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * IkConstraintTimeline::ENTRIES]); + } + + // Transform constraint timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int index = readVarint(input, true); + int frameCount = readVarint(input, true); + TransformConstraintTimeline *timeline = new(__FILE__, __LINE__) TransformConstraintTimeline(frameCount); + timeline->_transformConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + float rotateMix = readFloat(input); + float translateMix = readFloat(input); + float scaleMix = readFloat(input); + float shearMix = readFloat(input); + timeline->setFrame(frameIndex, time, rotateMix, translateMix, scaleMix, shearMix); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * TransformConstraintTimeline::ENTRIES]); + } + + // Path constraint timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int index = readVarint(input, true); + PathConstraintData *data = skeletonData->_pathConstraints[index]; + for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { + int timelineType = readSByte(input); + int frameCount = readVarint(input, true); + switch (timelineType) { + case PATH_POSITION: + case PATH_SPACING: { + PathConstraintPositionTimeline *timeline; + float timelineScale = 1; + if (timelineType == PATH_SPACING) { + timeline = new(__FILE__, __LINE__) PathConstraintSpacingTimeline(frameCount); + + if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) { + timelineScale = scale; + } + } else { + timeline = new(__FILE__, __LINE__) PathConstraintPositionTimeline(frameCount); + + if (data->_positionMode == PositionMode_Fixed) { + timelineScale = scale; + } + } + timeline->_pathConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + float value = readFloat(input) * timelineScale; + timeline->setFrame(frameIndex, time, value); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * + PathConstraintPositionTimeline::ENTRIES]); + break; + } + case PATH_MIX: { + PathConstraintMixTimeline *timeline = new(__FILE__, __LINE__) PathConstraintMixTimeline(frameCount); + + timeline->_pathConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + float rotateMix = readFloat(input); + float translateMix = readFloat(input); + timeline->setFrame(frameIndex, time, rotateMix, translateMix); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.add(timeline); + duration = MathUtil::max(duration, + timeline->_frames[(frameCount - 1) * PathConstraintMixTimeline::ENTRIES]); + break; + } + } + } + } + + // Deform timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + Skin *skin = skeletonData->_skins[readVarint(input, true)]; + for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { + int slotIndex = readVarint(input, true); + for (int iii = 0, nnn = readVarint(input, true); iii < nnn; iii++) { + const char *attachmentName = readString(input); + Attachment *baseAttachment = skin->getAttachment(slotIndex, String(attachmentName, true)); + + if (!baseAttachment) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError("Attachment not found: ", attachmentName); + return NULL; + } + + VertexAttachment *attachment = static_cast(baseAttachment); + + bool weighted = attachment->_bones.size() > 0; + Vector &vertices = attachment->_vertices; + size_t deformLength = weighted ? vertices.size() / 3 * 2 + : vertices.size(); + + size_t frameCount = (size_t)readVarint(input, true); + + DeformTimeline *timeline = new(__FILE__, __LINE__) DeformTimeline(frameCount); + + timeline->_slotIndex = slotIndex; + timeline->_attachment = attachment; + + for (size_t frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + Vector deform; + size_t end = (size_t)readVarint(input, true); + if (end == 0) { + if (weighted) { + deform.setSize(deformLength, 0); + for (size_t iiii = 0; iiii < deformLength; ++iiii) { + deform[iiii] = 0; + } + } else { + deform.clearAndAddAll(vertices); + } + } else { + deform.setSize(deformLength, 0); + size_t start = (size_t)readVarint(input, true); + end += start; + if (scale == 1) { + for (size_t v = start; v < end; ++v) { + deform[v] = readFloat(input); + } + } else { + for (size_t v = start; v < end; ++v) { + deform[v] = readFloat(input) * scale; + } + } + + if (!weighted) { + for (size_t v = 0, vn = deform.size(); v < vn; ++v) { + deform[v] += vertices[v]; + } + } + } + + timeline->setFrame(frameIndex, time, deform); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[frameCount - 1]); + } + } + } + + // Draw order timeline. + size_t drawOrderCount = (size_t)readVarint(input, true); + if (drawOrderCount > 0) { + DrawOrderTimeline *timeline = new(__FILE__, __LINE__) DrawOrderTimeline(drawOrderCount); + + size_t slotCount = skeletonData->_slots.size(); + for (size_t i = 0; i < drawOrderCount; ++i) { + float time = readFloat(input); + size_t offsetCount = (size_t)readVarint(input, true); + + Vector drawOrder; + drawOrder.setSize(slotCount, 0); + for (int ii = (int)slotCount - 1; ii >= 0; --ii) { + drawOrder[ii] = -1; + } + + Vector unchanged; + unchanged.setSize(slotCount - offsetCount, 0); + size_t originalIndex = 0, unchangedIndex = 0; + for (size_t ii = 0; ii < offsetCount; ++ii) { + size_t slotIndex = (size_t)readVarint(input, true); + // Collect unchanged items. + while (originalIndex != slotIndex) { + unchanged[unchangedIndex++] = originalIndex++; + } + // Set changed items. + size_t index = originalIndex; + drawOrder[index + (size_t)readVarint(input, true)] = originalIndex++; + } + + // Collect remaining unchanged items. + while (originalIndex < slotCount) { + unchanged[unchangedIndex++] = originalIndex++; + } + + // Fill in unchanged items. + for (int ii = (int)slotCount - 1; ii >= 0; --ii) { + if (drawOrder[ii] == -1) { + drawOrder[ii] = unchanged[--unchangedIndex]; + } + } + timeline->setFrame(i, time, drawOrder); + } + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[drawOrderCount - 1]); + } + + // Event timeline. + int eventCount = readVarint(input, true); + if (eventCount > 0) { + EventTimeline *timeline = new(__FILE__, __LINE__) EventTimeline(eventCount); + + for (int i = 0; i < eventCount; ++i) { + float time = readFloat(input); + EventData *eventData = skeletonData->_events[readVarint(input, true)]; + Event *event = new(__FILE__, __LINE__) Event(time, *eventData); + + event->_intValue = readVarint(input, false); + event->_floatValue = readFloat(input); + bool freeString = readBoolean(input); + const char *event_stringValue = freeString ? readString(input) : eventData->_stringValue.buffer(); + event->_stringValue = String(event_stringValue); + if (freeString) { + SpineExtension::free(event_stringValue, __FILE__, __LINE__); + } + + if (!eventData->_audioPath.isEmpty()) { + event->_volume = readFloat(input); + event->_balance = readFloat(input); + } + timeline->setFrame(i, event); + } + + timelines.add(timeline); + duration = MathUtil::max(duration, timeline->_frames[eventCount - 1]); + } + + return new(__FILE__, __LINE__) Animation(String(name), timelines, duration); +} + +void SkeletonBinary::readCurve(DataInput *input, int frameIndex, CurveTimeline *timeline) { + switch (readByte(input)) { + case CURVE_STEPPED: { + timeline->setStepped(frameIndex); + break; + } + case CURVE_BEZIER: { + float cx1 = readFloat(input); + float cy1 = readFloat(input); + float cx2 = readFloat(input); + float cy2 = readFloat(input); + timeline->setCurve(frameIndex, cx1, cy1, cx2, cy2); + break; + } + } +} diff --git a/cocos/editor-support/spine/SkeletonBinary.h b/cocos/editor-support/spine/SkeletonBinary.h index 41f50d3171f9..5766960f45d9 100644 --- a/cocos/editor-support/spine/SkeletonBinary.h +++ b/cocos/editor-support/spine/SkeletonBinary.h @@ -1,72 +1,129 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_SKELETONBINARY_H_ -#define SPINE_SKELETONBINARY_H_ - -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif +#ifndef Spine_SkeletonBinary_h +#define Spine_SkeletonBinary_h -struct spAtlasAttachmentLoader; +#include +#include +#include +#include +#include -typedef struct spSkeletonBinary { - float scale; - spAttachmentLoader* attachmentLoader; - const char* const error; -} spSkeletonBinary; +namespace spine { + class SkeletonData; + class Atlas; + class AttachmentLoader; + class LinkedMesh; + class Skin; + class Attachment; + class VertexAttachment; + class Animation; + class CurveTimeline; + + class SP_API SkeletonBinary : public SpineObject { + public: + static const int BONE_ROTATE; + static const int BONE_TRANSLATE; + static const int BONE_SCALE; + static const int BONE_SHEAR; + + static const int SLOT_ATTACHMENT; + static const int SLOT_COLOR; + static const int SLOT_TWO_COLOR; + + static const int PATH_POSITION; + static const int PATH_SPACING; + static const int PATH_MIX; + + static const int CURVE_LINEAR; + static const int CURVE_STEPPED; + static const int CURVE_BEZIER; -SP_API spSkeletonBinary* spSkeletonBinary_createWithLoader (spAttachmentLoader* attachmentLoader); -SP_API spSkeletonBinary* spSkeletonBinary_create (spAtlas* atlas); -SP_API void spSkeletonBinary_dispose (spSkeletonBinary* self); + explicit SkeletonBinary(Atlas* atlasArray); -SP_API spSkeletonData* spSkeletonBinary_readSkeletonData (spSkeletonBinary* self, const unsigned char* binary, const int length); -SP_API spSkeletonData* spSkeletonBinary_readSkeletonDataFile (spSkeletonBinary* self, const char* path); + explicit SkeletonBinary(AttachmentLoader* attachmentLoader); + + ~SkeletonBinary(); + + SkeletonData* readSkeletonData(const unsigned char* binary, int length); + + SkeletonData* readSkeletonDataFile(const String& path); -#ifdef SPINE_SHORT_NAMES -typedef spSkeletonBinary SkeletonBinary; -#define SkeletonBinary_createWithLoader(...) spSkeletonBinary_createWithLoader(__VA_ARGS__) -#define SkeletonBinary_create(...) spSkeletonBinary_create(__VA_ARGS__) -#define SkeletonBinary_dispose(...) spSkeletonBinary_dispose(__VA_ARGS__) -#define SkeletonBinary_readSkeletonData(...) spSkeletonBinary_readSkeletonData(__VA_ARGS__) -#define SkeletonBinary_readSkeletonDataFile(...) spSkeletonBinary_readSkeletonDataFile(__VA_ARGS__) -#endif + void setScale(float scale) { _scale = scale; } -#ifdef __cplusplus + String& getError() { return _error; } + + private: + struct DataInput : public SpineObject { + const unsigned char* cursor; + const unsigned char* end; + }; + + AttachmentLoader* _attachmentLoader; + Vector _linkedMeshes; + String _error; + float _scale; + const bool _ownsLoader; + + void setError(const char* value1, const char* value2); + + char* readString(DataInput* input); + + float readFloat(DataInput* input); + + unsigned char readByte(DataInput* input); + + signed char readSByte(DataInput* input); + + bool readBoolean(DataInput* input); + + int readInt(DataInput* input); + + void readColor(DataInput* input, Color& color); + + int readVarint(DataInput* input, bool optimizePositive); + + Skin* readSkin(DataInput* input, const String& skinName, SkeletonData* skeletonData, bool nonessential); + + Attachment* readAttachment(DataInput* input, Skin* skin, int slotIndex, const String& attachmentName, SkeletonData* skeletonData, bool nonessential); + + void readVertices(DataInput* input, VertexAttachment* attachment, int vertexCount); + + void readFloatArray(DataInput *input, int n, float scale, Vector& array); + + void readShortArray(DataInput *input, Vector& array); + + Animation* readAnimation(const String& name, DataInput* input, SkeletonData *skeletonData); + + void readCurve(DataInput* input, int frameIndex, CurveTimeline* timeline); + }; } -#endif -#endif /* SPINE_SKELETONBINARY_H_ */ +#endif /* Spine_SkeletonBinary_h */ diff --git a/cocos/editor-support/spine/SkeletonBounds.cpp b/cocos/editor-support/spine/SkeletonBounds.cpp new file mode 100644 index 000000000000..443740fa0c76 --- /dev/null +++ b/cocos/editor-support/spine/SkeletonBounds.cpp @@ -0,0 +1,237 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include + +using namespace spine; + +SkeletonBounds::SkeletonBounds() : _minX(0), _minY(0), _maxX(0), _maxY(0) { +} + +void SkeletonBounds::update(Skeleton &skeleton, bool updateAabb) { + Vector &slots = skeleton._slots; + size_t slotCount = slots.size(); + + _boundingBoxes.clear(); + for (size_t i = 0, n = _polygons.size(); i < n; ++i) { + _polygonPool.add(_polygons[i]); + } + + _polygons.clear(); + + for (size_t i = 0; i < slotCount; i++) { + Slot *slot = slots[i]; + Attachment *attachment = slot->getAttachment(); + if (attachment == NULL || !attachment->getRTTI().instanceOf(BoundingBoxAttachment::rtti)) { + continue; + } + BoundingBoxAttachment *boundingBox = static_cast(attachment); + _boundingBoxes.add(boundingBox); + + Polygon *polygonP = NULL; + size_t poolCount = _polygonPool.size(); + if (poolCount > 0) { + polygonP = _polygonPool[poolCount - 1]; + _polygonPool.removeAt(poolCount - 1); + } else { + polygonP = new(__FILE__, __LINE__) Polygon(); + } + + _polygons.add(polygonP); + + Polygon &polygon = *polygonP; + + size_t count = boundingBox->getWorldVerticesLength(); + polygon._count = count; + if (polygon._vertices.size() < count) { + polygon._vertices.setSize(count, 0); + } + boundingBox->computeWorldVertices(*slot, polygon._vertices); + } + + if (updateAabb) { + aabbCompute(); + } else { + _minX = std::numeric_limits::min(); + _minY = std::numeric_limits::min(); + _maxX = std::numeric_limits::max(); + _maxY = std::numeric_limits::max(); + } +} + +bool SkeletonBounds::aabbcontainsPoint(float x, float y) { + return x >= _minX && x <= _maxX && y >= _minY && y <= _maxY; +} + +bool SkeletonBounds::aabbintersectsSegment(float x1, float y1, float x2, float y2) { + float minX = _minX; + float minY = _minY; + float maxX = _maxX; + float maxY = _maxY; + + if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || + (y1 >= maxY && y2 >= maxY)) { + return false; + } + + float m = (y2 - y1) / (x2 - x1); + float y = m * (minX - x1) + y1; + if (y > minY && y < maxY) { + return true; + } + y = m * (maxX - x1) + y1; + if (y > minY && y < maxY) { + return true; + } + float x = (minY - y1) / m + x1; + if (x > minX && x < maxX) { + return true; + } + x = (maxY - y1) / m + x1; + if (x > minX && x < maxX) { + return true; + } + return false; +} + +bool SkeletonBounds::aabbIntersectsSkeleton(SkeletonBounds bounds) { + return _minX < bounds._maxX && _maxX > bounds._minX && _minY < bounds._maxY && _maxY > bounds._minY; +} + +bool SkeletonBounds::containsPoint(spine::Polygon *polygon, float x, float y) { + Vector &vertices = polygon->_vertices; + int nn = polygon->_count; + + int prevIndex = nn - 2; + bool inside = false; + for (int ii = 0; ii < nn; ii += 2) { + float vertexY = vertices[ii + 1]; + float prevY = vertices[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { + float vertexX = vertices[ii]; + if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) { + inside = !inside; + } + } + prevIndex = ii; + } + return inside; +} + +BoundingBoxAttachment *SkeletonBounds::containsPoint(float x, float y) { + for (size_t i = 0, n = _polygons.size(); i < n; ++i) { + if (containsPoint(_polygons[i], x, y)) { + return _boundingBoxes[i]; + } + } + + return NULL; +} + +BoundingBoxAttachment *SkeletonBounds::intersectsSegment(float x1, float y1, float x2, float y2) { + for (size_t i = 0, n = _polygons.size(); i < n; ++i) { + if (intersectsSegment(_polygons[i], x1, y1, x2, y2)) { + return _boundingBoxes[i]; + } + } + return NULL; +} + +bool SkeletonBounds::intersectsSegment(spine::Polygon *polygon, float x1, float y1, float x2, float y2) { + Vector &vertices = polygon->_vertices; + size_t nn = polygon->_count; + + float width12 = x1 - x2, height12 = y1 - y2; + float det1 = x1 * y2 - y1 * x2; + float x3 = vertices[nn - 2], y3 = vertices[nn - 1]; + for (size_t ii = 0; ii < nn; ii += 2) { + float x4 = vertices[ii], y4 = vertices[ii + 1]; + float det2 = x3 * y4 - y3 * x4; + float width34 = x3 - x4, height34 = y3 - y4; + float det3 = width12 * height34 - height12 * width34; + float x = (det1 * width34 - width12 * det2) / det3; + if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { + float y = (det1 * height34 - height12 * det2) / det3; + if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) { + return true; + } + } + x3 = x4; + y3 = y4; + } + + return false; +} + +spine::Polygon *SkeletonBounds::getPolygon(BoundingBoxAttachment *attachment) { + int index = _boundingBoxes.indexOf(attachment); + + return index == -1 ? NULL : _polygons[index]; +} + +float SkeletonBounds::getWidth() { + return _maxX - _minX; +} + +float SkeletonBounds::getHeight() { + return _maxY - _minY; +} + +void SkeletonBounds::aabbCompute() { + float minX = std::numeric_limits::min(); + float minY = std::numeric_limits::min(); + float maxX = std::numeric_limits::max(); + float maxY = std::numeric_limits::max(); + + for (size_t i = 0, n = _polygons.size(); i < n; ++i) { + Polygon *polygon = _polygons[i]; + Vector &vertices = polygon->_vertices; + for (int ii = 0, nn = polygon->_count; ii < nn; ii += 2) { + float x = vertices[ii]; + float y = vertices[ii + 1]; + minX = MathUtil::min(minX, x); + minY = MathUtil::min(minY, y); + maxX = MathUtil::max(maxX, x); + maxY = MathUtil::max(maxY, y); + } + } + _minX = minX; + _minY = minY; + _maxX = maxX; + _maxY = maxY; +} diff --git a/cocos/editor-support/spine/SkeletonBounds.h b/cocos/editor-support/spine/SkeletonBounds.h index 077dca5c8971..5b1068304251 100644 --- a/cocos/editor-support/spine/SkeletonBounds.h +++ b/cocos/editor-support/spine/SkeletonBounds.h @@ -1,113 +1,107 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_SKELETONBOUNDS_H_ -#define SPINE_SKELETONBOUNDS_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spPolygon { - float* const vertices; - int count; - int capacity; -} spPolygon; - -SP_API spPolygon* spPolygon_create (int capacity); -SP_API void spPolygon_dispose (spPolygon* self); - -SP_API int/*bool*/spPolygon_containsPoint (spPolygon* polygon, float x, float y); -SP_API int/*bool*/spPolygon_intersectsSegment (spPolygon* polygon, float x1, float y1, float x2, float y2); - -#ifdef SPINE_SHORT_NAMES -typedef spPolygon Polygon; -#define Polygon_create(...) spPolygon_create(__VA_ARGS__) -#define Polygon_dispose(...) spPolygon_dispose(__VA_ARGS__) -#define Polygon_containsPoint(...) spPolygon_containsPoint(__VA_ARGS__) -#define Polygon_intersectsSegment(...) spPolygon_intersectsSegment(__VA_ARGS__) -#endif - -/**/ - -typedef struct spSkeletonBounds { - int count; - spBoundingBoxAttachment** boundingBoxes; - spPolygon** polygons; - - float minX, minY, maxX, maxY; -} spSkeletonBounds; - -SP_API spSkeletonBounds* spSkeletonBounds_create (); -SP_API void spSkeletonBounds_dispose (spSkeletonBounds* self); -SP_API void spSkeletonBounds_update (spSkeletonBounds* self, spSkeleton* skeleton, int/*bool*/updateAabb); - -/** Returns true if the axis aligned bounding box contains the point. */ -SP_API int/*bool*/spSkeletonBounds_aabbContainsPoint (spSkeletonBounds* self, float x, float y); - -/** Returns true if the axis aligned bounding box intersects the line segment. */ -SP_API int/*bool*/spSkeletonBounds_aabbIntersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2); - -/** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ -SP_API int/*bool*/spSkeletonBounds_aabbIntersectsSkeleton (spSkeletonBounds* self, spSkeletonBounds* bounds); - -/** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more - * efficient to only call this method if spSkeletonBounds_aabbContainsPoint returns true. */ -SP_API spBoundingBoxAttachment* spSkeletonBounds_containsPoint (spSkeletonBounds* self, float x, float y); - -/** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually - * more efficient to only call this method if spSkeletonBounds_aabbIntersectsSegment returns true. */ -SP_API spBoundingBoxAttachment* spSkeletonBounds_intersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2); - -/** Returns the polygon for the specified bounding box, or null. */ -SP_API spPolygon* spSkeletonBounds_getPolygon (spSkeletonBounds* self, spBoundingBoxAttachment* boundingBox); - -#ifdef SPINE_SHORT_NAMES -typedef spSkeletonBounds SkeletonBounds; -#define SkeletonBounds_create(...) spSkeletonBounds_create(__VA_ARGS__) -#define SkeletonBounds_dispose(...) spSkeletonBounds_dispose(__VA_ARGS__) -#define SkeletonBounds_update(...) spSkeletonBounds_update(__VA_ARGS__) -#define SkeletonBounds_aabbContainsPoint(...) spSkeletonBounds_aabbContainsPoint(__VA_ARGS__) -#define SkeletonBounds_aabbIntersectsSegment(...) spSkeletonBounds_aabbIntersectsSegment(__VA_ARGS__) -#define SkeletonBounds_aabbIntersectsSkeleton(...) spSkeletonBounds_aabbIntersectsSkeleton(__VA_ARGS__) -#define SkeletonBounds_containsPoint(...) spSkeletonBounds_containsPoint(__VA_ARGS__) -#define SkeletonBounds_intersectsSegment(...) spSkeletonBounds_intersectsSegment(__VA_ARGS__) -#define SkeletonBounds_getPolygon(...) spSkeletonBounds_getPolygon(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#ifndef Spine_SkeletonBounds_h +#define Spine_SkeletonBounds_h + +#include +#include + +namespace spine { + class Skeleton; + class BoundingBoxAttachment; + class Polygon; + + /// + /// Collects each BoundingBoxAttachment that is visible and computes the world vertices for its polygon. + /// The polygon vertices are provided along with convenience methods for doing hit detection. + /// + class SP_API SkeletonBounds : public SpineObject { + public: + SkeletonBounds(); + + /// + /// Clears any previous polygons, finds all visible bounding box attachments, + /// and computes the world vertices for each bounding box's polygon. + /// @param skeleton The skeleton. + /// @param updateAabb + /// If true, the axis aligned bounding box containing all the polygons is computed. + /// If false, the SkeletonBounds AABB methods will always return true. + /// + void update(Skeleton& skeleton, bool updateAabb); + + /// Returns true if the axis aligned bounding box contains the point. + bool aabbcontainsPoint(float x, float y); + + /// Returns true if the axis aligned bounding box intersects the line segment. + bool aabbintersectsSegment(float x1, float y1, float x2, float y2); + + /// Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. + bool aabbIntersectsSkeleton(SkeletonBounds bounds); + + /// Returns true if the polygon contains the point. + bool containsPoint(Polygon* polygon, float x, float y); + + /// Returns the first bounding box attachment that contains the point, or NULL. When doing many checks, it is usually more + /// efficient to only call this method if {@link #aabbcontainsPoint(float, float)} returns true. + BoundingBoxAttachment* containsPoint(float x, float y); + + /// Returns the first bounding box attachment that contains the line segment, or NULL. When doing many checks, it is usually + /// more efficient to only call this method if {@link #aabbintersectsSegment(float, float, float, float)} returns true. + BoundingBoxAttachment* intersectsSegment(float x1, float y1, float x2, float y2); + + /// Returns true if the polygon contains the line segment. + bool intersectsSegment(Polygon* polygon, float x1, float y1, float x2, float y2); + + Polygon* getPolygon(BoundingBoxAttachment* attachment); + + float getWidth(); + float getHeight(); + + private: + Vector _polygonPool; + Vector _boundingBoxes; + Vector _polygons; + float _minX, _minY, _maxX, _maxY; + + void aabbCompute(); + }; + + class Polygon : public SpineObject { + public: + Vector _vertices; + int _count; + + Polygon() : _count(0) { + _vertices.ensureCapacity(16); + } + }; } -#endif -#endif /* SPINE_SKELETONBOUNDS_H_ */ +#endif /* Spine_SkeletonBounds_h */ diff --git a/cocos/editor-support/spine/SkeletonClipping.cpp b/cocos/editor-support/spine/SkeletonClipping.cpp new file mode 100644 index 000000000000..d6cd1ad2cdbb --- /dev/null +++ b/cocos/editor-support/spine/SkeletonClipping.cpp @@ -0,0 +1,335 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +using namespace spine; + +SkeletonClipping::SkeletonClipping() : _clipAttachment(NULL) { + _clipOutput.ensureCapacity(128); + _clippedVertices.ensureCapacity(128); + _clippedTriangles.ensureCapacity(128); + _clippedUVs.ensureCapacity(128); +} + +size_t SkeletonClipping::clipStart(Slot &slot, ClippingAttachment *clip) { + if (_clipAttachment != NULL) { + return 0; + } + + _clipAttachment = clip; + + int n = clip->getWorldVerticesLength(); + _clippingPolygon.setSize(n, 0); + clip->computeWorldVertices(slot, 0, n, _clippingPolygon, 0, 2); + makeClockwise(_clippingPolygon); + _clippingPolygons = &_triangulator.decompose(_clippingPolygon, _triangulator.triangulate(_clippingPolygon)); + + for (size_t i = 0; i < _clippingPolygons->size(); ++i) { + Vector *polygonP = (*_clippingPolygons)[i]; + Vector &polygon = *polygonP; + makeClockwise(polygon); + polygon.add(polygon[0]); + polygon.add(polygon[1]); + } + + return (*_clippingPolygons).size(); +} + +void SkeletonClipping::clipEnd(Slot &slot) { + if (_clipAttachment != NULL && _clipAttachment->_endSlot == &slot._data) { + clipEnd(); + } +} + +void SkeletonClipping::clipEnd() { + if (_clipAttachment == NULL) { + return; + } + + _clipAttachment = NULL; + _clippingPolygons = NULL; + _clippedVertices.clear(); + _clippedUVs.clear(); + _clippedTriangles.clear(); + _clippingPolygon.clear(); +} + +void SkeletonClipping::clipTriangles(Vector &vertices, Vector &triangles, Vector &uvs, size_t stride) { + clipTriangles(vertices.buffer(), triangles.buffer(), triangles.size(), uvs.buffer(), stride); +} + +void SkeletonClipping::clipTriangles(float *vertices, unsigned short *triangles, + size_t trianglesLength, float *uvs, size_t stride) { + Vector &clipOutput = _clipOutput; + Vector &clippedVertices = _clippedVertices; + Vector &clippedTriangles = _clippedTriangles; + Vector *> &polygons = *_clippingPolygons; + size_t polygonsCount = (*_clippingPolygons).size(); + + size_t index = 0; + clippedVertices.clear(); + _clippedUVs.clear(); + clippedTriangles.clear(); + + size_t i = 0; + continue_outer: + for (; i < trianglesLength; i += 3) { + int vertexOffset = triangles[i] * stride; + float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; + float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; + + vertexOffset = triangles[i + 1] * stride; + float x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; + float u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; + + vertexOffset = triangles[i + 2] * stride; + float x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; + float u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; + + for (size_t p = 0; p < polygonsCount; p++) { + size_t s = clippedVertices.size(); + if (clip(x1, y1, x2, y2, x3, y3, &(*polygons[p]), &clipOutput)) { + size_t clipOutputLength = clipOutput.size(); + if (clipOutputLength == 0) { + continue; + } + float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; + float d = 1 / (d0 * d2 + d1 * (y1 - y3)); + + size_t clipOutputCount = clipOutputLength >> 1; + clippedVertices.setSize(s + clipOutputCount * 2, 0); + _clippedUVs.setSize(s + clipOutputCount * 2, 0); + for (size_t ii = 0; ii < clipOutputLength; ii += 2) { + float x = clipOutput[ii], y = clipOutput[ii + 1]; + clippedVertices[s] = x; + clippedVertices[s + 1] = y; + float c0 = x - x3, c1 = y - y3; + float a = (d0 * c0 + d1 * c1) * d; + float b = (d4 * c0 + d2 * c1) * d; + float c = 1 - a - b; + _clippedUVs[s] = u1 * a + u2 * b + u3 * c; + _clippedUVs[s + 1] = v1 * a + v2 * b + v3 * c; + s += 2; + } + + s = clippedTriangles.size(); + clippedTriangles.setSize(s + 3 * (clipOutputCount - 2), 0); + clipOutputCount--; + for (size_t ii = 1; ii < clipOutputCount; ii++) { + clippedTriangles[s] = (unsigned short)(index); + clippedTriangles[s + 1] = (unsigned short)(index + ii); + clippedTriangles[s + 2] = (unsigned short)(index + ii + 1); + s += 3; + } + index += clipOutputCount + 1; + } else { + clippedVertices.setSize(s + 3 * 2, 0); + _clippedUVs.setSize(s + 3 * 2, 0); + clippedVertices[s] = x1; + clippedVertices[s + 1] = y1; + clippedVertices[s + 2] = x2; + clippedVertices[s + 3] = y2; + clippedVertices[s + 4] = x3; + clippedVertices[s + 5] = y3; + + _clippedUVs[s] = u1; + _clippedUVs[s + 1] = v1; + _clippedUVs[s + 2] = u2; + _clippedUVs[s + 3] = v2; + _clippedUVs[s + 4] = u3; + _clippedUVs[s + 5] = v3; + + s = clippedTriangles.size(); + clippedTriangles.setSize(s + 3, 0); + clippedTriangles[s] = (unsigned short)index; + clippedTriangles[s + 1] = (unsigned short)(index + 1); + clippedTriangles[s + 2] = (unsigned short)(index + 2); + index += 3; + i += 3; + goto continue_outer; + } + } + } +} + +bool SkeletonClipping::isClipping() { + return _clipAttachment != NULL; +} + +Vector &SkeletonClipping::getClippedVertices() { + return _clippedVertices; +} + +Vector &SkeletonClipping::getClippedTriangles() { + return _clippedTriangles; +} + +Vector &SkeletonClipping::getClippedUVs() { + return _clippedUVs; +} + +bool SkeletonClipping::clip(float x1, float y1, float x2, float y2, float x3, float y3, Vector *clippingArea, + Vector *output) { + Vector *originalOutput = output; + bool clipped = false; + + // Avoid copy at the end. + Vector *input; + if (clippingArea->size() % 4 >= 2) { + input = output; + output = &_scratch; + } else { + input = &_scratch; + } + + input->clear(); + input->add(x1); + input->add(y1); + input->add(x2); + input->add(y2); + input->add(x3); + input->add(y3); + input->add(x1); + input->add(y1); + output->clear(); + + Vector &clippingVertices = *clippingArea; + size_t clippingVerticesLast = clippingArea->size() - 4; + for (size_t i = 0;; i += 2) { + float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; + float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; + float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; + + Vector &inputVertices = *input; + size_t inputVerticesLength = input->size() - 2, outputStart = output->size(); + for (size_t ii = 0; ii < inputVerticesLength; ii += 2) { + float inputX = inputVertices[ii], inputY = inputVertices[ii + 1]; + float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3]; + bool side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; + if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { + if (side2) { + // v1 inside, v2 inside + output->add(inputX2); + output->add(inputY2); + continue; + } + // v1 inside, v2 outside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY); + if (MathUtil::abs(s) > 0.000001f) { + float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s; + output->add(edgeX + (edgeX2 - edgeX) * ua); + output->add(edgeY + (edgeY2 - edgeY) * ua); + } else { + output->add(edgeX); + output->add(edgeY); + } + } else if (side2) { + // v1 outside, v2 inside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY); + if (MathUtil::abs(s) > 0.000001f) { + float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s; + output->add(edgeX + (edgeX2 - edgeX) * ua); + output->add(edgeY + (edgeY2 - edgeY) * ua); + } else { + output->add(edgeX); + output->add(edgeY); + } + output->add(inputX2); + output->add(inputY2); + } + clipped = true; + } + + if (outputStart == output->size()) { + // All edges outside. + originalOutput->clear(); + return true; + } + + output->add((*output)[0]); + output->add((*output)[1]); + + if (i == clippingVerticesLast) { + break; + } + Vector *temp = output; + output = input; + output->clear(); + input = temp; + } + + if (originalOutput != output) { + originalOutput->clear(); + for (size_t i = 0, n = output->size() - 2; i < n; ++i) { + originalOutput->add((*output)[i]); + } + } else { + originalOutput->setSize(originalOutput->size() - 2, 0); + } + + return clipped; +} + +void SkeletonClipping::makeClockwise(Vector &polygon) { + size_t verticeslength = polygon.size(); + + float area = + polygon[verticeslength - 2] * polygon[1] - polygon[0] * polygon[verticeslength - 1], p1x, p1y, p2x, p2y; + + for (size_t i = 0, n = verticeslength - 3; i < n; i += 2) { + p1x = polygon[i]; + p1y = polygon[i + 1]; + p2x = polygon[i + 2]; + p2y = polygon[i + 3]; + area += p1x * p2y - p2x * p1y; + } + + if (area < 0) { + return; + } + + for (size_t i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) { + float x = polygon[i], y = polygon[i + 1]; + int other = lastX - i; + polygon[i] = polygon[other]; + polygon[i + 1] = polygon[other + 1]; + polygon[other] = x; + polygon[other + 1] = y; + } +} diff --git a/cocos/editor-support/spine/SkeletonClipping.h b/cocos/editor-support/spine/SkeletonClipping.h index 74f21fccb796..3ed6138eea9d 100644 --- a/cocos/editor-support/spine/SkeletonClipping.h +++ b/cocos/editor-support/spine/SkeletonClipping.h @@ -1,68 +1,79 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_SKELETONCLIPPING_H -#define SPINE_SKELETONCLIPPING_H +#ifndef Spine_SkeletonClipping_h +#define Spine_SkeletonClipping_h -#include -#include -#include -#include +#include #include -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spSkeletonClipping { - spTriangulator* triangulator; - spFloatArray* clippingPolygon; - spFloatArray* clipOutput; - spFloatArray* clippedVertices; - spFloatArray* clippedUVs; - spUnsignedShortArray* clippedTriangles; - spFloatArray* scratch; - spClippingAttachment* clipAttachment; - spArrayFloatArray* clippingPolygons; -} spSkeletonClipping; - -SP_API spSkeletonClipping* spSkeletonClipping_create(); -SP_API int spSkeletonClipping_clipStart(spSkeletonClipping* self, spSlot* slot, spClippingAttachment* clip); -SP_API void spSkeletonClipping_clipEnd(spSkeletonClipping* self, spSlot* slot); -SP_API void spSkeletonClipping_clipEnd2(spSkeletonClipping* self); -SP_API int /*boolean*/ spSkeletonClipping_isClipping(spSkeletonClipping* self); -SP_API void spSkeletonClipping_clipTriangles(spSkeletonClipping* self, float* vertices, int verticesLength, unsigned short* triangles, int trianglesLength, float* uvs, int stride); -SP_API void spSkeletonClipping_dispose(spSkeletonClipping* self); +namespace spine { + class Slot; + class ClippingAttachment; + + class SP_API SkeletonClipping : public SpineObject { + public: + SkeletonClipping(); -#ifdef __cplusplus + size_t clipStart(Slot& slot, ClippingAttachment* clip); + + void clipEnd(Slot& slot); + + void clipEnd(); + + void clipTriangles(float* vertices, unsigned short* triangles, size_t trianglesLength, float* uvs, size_t stride); + + void clipTriangles(Vector& vertices, Vector& triangles, Vector& uvs, size_t stride); + + bool isClipping(); + + Vector& getClippedVertices(); + Vector& getClippedTriangles(); + Vector& getClippedUVs(); + + private: + Triangulator _triangulator; + Vector _clippingPolygon; + Vector _clipOutput; + Vector _clippedVertices; + Vector _clippedTriangles; + Vector _clippedUVs; + Vector _scratch; + ClippingAttachment* _clipAttachment; + Vector< Vector* > *_clippingPolygons; + + /** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping + * area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */ + bool clip(float x1, float y1, float x2, float y2, float x3, float y3, Vector* clippingArea, Vector* output); + + static void makeClockwise(Vector& polygon); + }; } -#endif -#endif /* SPINE_SKELETONCLIPPING_H */ +#endif /* Spine_SkeletonClipping_h */ diff --git a/cocos/editor-support/spine/SkeletonData.cpp b/cocos/editor-support/spine/SkeletonData.cpp new file mode 100644 index 000000000000..05b072ff025c --- /dev/null +++ b/cocos/editor-support/spine/SkeletonData.cpp @@ -0,0 +1,221 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace spine; + +SkeletonData::SkeletonData() : + _name(), + _defaultSkin(NULL), + _width(0), + _height(0), + _version(), + _hash(), + _fps(0), + _imagesPath() { +} + +SkeletonData::~SkeletonData() { + ContainerUtil::cleanUpVectorOfPointers(_bones); + ContainerUtil::cleanUpVectorOfPointers(_slots); + ContainerUtil::cleanUpVectorOfPointers(_skins); + + _defaultSkin = NULL; + + ContainerUtil::cleanUpVectorOfPointers(_events); + ContainerUtil::cleanUpVectorOfPointers(_animations); + ContainerUtil::cleanUpVectorOfPointers(_ikConstraints); + ContainerUtil::cleanUpVectorOfPointers(_transformConstraints); + ContainerUtil::cleanUpVectorOfPointers(_pathConstraints); +} + +BoneData *SkeletonData::findBone(const String &boneName) { + return ContainerUtil::findWithName(_bones, boneName); +} + +int SkeletonData::findBoneIndex(const String &boneName) { + return ContainerUtil::findIndexWithName(_bones, boneName); +} + +SlotData *SkeletonData::findSlot(const String &slotName) { + return ContainerUtil::findWithName(_slots, slotName); +} + +int SkeletonData::findSlotIndex(const String &slotName) { + return ContainerUtil::findIndexWithName(_slots, slotName); +} + +Skin *SkeletonData::findSkin(const String &skinName) { + return ContainerUtil::findWithName(_skins, skinName); +} + +spine::EventData *SkeletonData::findEvent(const String &eventDataName) { + return ContainerUtil::findWithName(_events, eventDataName); +} + +Animation *SkeletonData::findAnimation(const String &animationName) { + return ContainerUtil::findWithName(_animations, animationName); +} + +IkConstraintData *SkeletonData::findIkConstraint(const String &constraintName) { + return ContainerUtil::findWithName(_ikConstraints, constraintName); +} + +TransformConstraintData *SkeletonData::findTransformConstraint(const String &constraintName) { + return ContainerUtil::findWithName(_transformConstraints, constraintName); +} + +PathConstraintData *SkeletonData::findPathConstraint(const String &constraintName) { + return ContainerUtil::findWithName(_pathConstraints, constraintName); +} + +int SkeletonData::findPathConstraintIndex(const String &pathConstraintName) { + return ContainerUtil::findIndexWithName(_pathConstraints, pathConstraintName); +} + +const String &SkeletonData::getName() { + return _name; +} + +void SkeletonData::setName(const String &inValue) { + _name = inValue; +} + +Vector &SkeletonData::getBones() { + return _bones; +} + +Vector &SkeletonData::getSlots() { + return _slots; +} + +Vector &SkeletonData::getSkins() { + return _skins; +} + +Skin *SkeletonData::getDefaultSkin() { + return _defaultSkin; +} + +void SkeletonData::setDefaultSkin(Skin *inValue) { + _defaultSkin = inValue; +} + +Vector &SkeletonData::getEvents() { + return _events; +} + +Vector &SkeletonData::getAnimations() { + return _animations; +} + +Vector &SkeletonData::getIkConstraints() { + return _ikConstraints; +} + +Vector &SkeletonData::getTransformConstraints() { + return _transformConstraints; +} + +Vector &SkeletonData::getPathConstraints() { + return _pathConstraints; +} + +float SkeletonData::getWidth() { + return _width; +} + +void SkeletonData::setWidth(float inValue) { + _width = inValue; +} + +float SkeletonData::getHeight() { + return _height; +} + +void SkeletonData::setHeight(float inValue) { + _height = inValue; +} + +const String &SkeletonData::getVersion() { + return _version; +} + +void SkeletonData::setVersion(const String &inValue) { + _version = inValue; +} + +const String &SkeletonData::getHash() { + return _hash; +} + +void SkeletonData::setHash(const String &inValue) { + _hash = inValue; +} + +const String &SkeletonData::getImagesPath() { + return _imagesPath; +} + +void SkeletonData::setImagesPath(const String &inValue) { + _imagesPath = inValue; +} + + +const String &SkeletonData::getAudioPath() { + return _audioPath; +} + +void SkeletonData::setAudioPath(const String &inValue) { + _audioPath = inValue; +} + +float SkeletonData::getFps() { + return _fps; +} + +void SkeletonData::setFps(float inValue) { + _fps = inValue; +} diff --git a/cocos/editor-support/spine/SkeletonData.h b/cocos/editor-support/spine/SkeletonData.h index 5383d7d144f7..47e4729e0650 100644 --- a/cocos/editor-support/spine/SkeletonData.h +++ b/cocos/editor-support/spine/SkeletonData.h @@ -1,117 +1,183 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_SKELETONDATA_H_ -#define SPINE_SKELETONDATA_H_ +#ifndef Spine_SkeletonData_h +#define Spine_SkeletonData_h + +#include +#include + +namespace spine { +class BoneData; + +class SlotData; + +class Skin; + +class EventData; + +class Animation; + +class IkConstraintData; + +class TransformConstraintData; + +class PathConstraintData; + +/// Stores the setup pose and all of the stateless data for a skeleton. +class SP_API SkeletonData : public SpineObject { + friend class SkeletonBinary; + + friend class SkeletonJson; + + friend class Skeleton; + +public: + SkeletonData(); + + ~SkeletonData(); + + /// Finds a bone by comparing each bone's name. + /// It is more efficient to cache the results of this method than to call it multiple times. + /// @return May be NULL. + BoneData *findBone(const String &boneName); + + /// @return -1 if the bone was not found. + int findBoneIndex(const String &boneName); + + /// @return May be NULL. + SlotData *findSlot(const String &slotName); + + /// @return -1 if the slot was not found. + int findSlotIndex(const String &slotName); + + /// @return May be NULL. + Skin *findSkin(const String &skinName); + + /// @return May be NULL. + spine::EventData *findEvent(const String &eventDataName); + + /// @return May be NULL. + Animation *findAnimation(const String &animationName); + + /// @return May be NULL. + IkConstraintData *findIkConstraint(const String &constraintName); + + /// @return May be NULL. + TransformConstraintData *findTransformConstraint(const String &constraintName); + + /// @return May be NULL. + PathConstraintData *findPathConstraint(const String &constraintName); + + /// @return -1 if the path constraint was not found. + int findPathConstraintIndex(const String &pathConstraintName); + + const String &getName(); + + void setName(const String &inValue); + + /// The skeleton's bones, sorted parent first. The root bone is always the first bone. + Vector &getBones(); + + Vector &getSlots(); + + /// All skins, including the default skin. + Vector &getSkins(); + + /// The skeleton's default skin. + /// By default this skin contains all attachments that were not in a skin in Spine. + /// + /// @return May be NULL. + Skin *getDefaultSkin(); -#include -#include -#include -#include -#include -#include -#include -#include -#include + void setDefaultSkin(Skin *inValue); -#ifdef __cplusplus -extern "C" { -#endif + Vector &getEvents(); -typedef struct spSkeletonData { - const char* version; - const char* hash; - float width, height; + Vector &getAnimations(); - int bonesCount; - spBoneData** bones; + Vector &getIkConstraints(); - int slotsCount; - spSlotData** slots; + Vector &getTransformConstraints(); - int skinsCount; - spSkin** skins; - spSkin* defaultSkin; + Vector &getPathConstraints(); - int eventsCount; - spEventData** events; + float getWidth(); - int animationsCount; - spAnimation** animations; + void setWidth(float inValue); - int ikConstraintsCount; - spIkConstraintData** ikConstraints; + float getHeight(); - int transformConstraintsCount; - spTransformConstraintData** transformConstraints; + void setHeight(float inValue); - int pathConstraintsCount; - spPathConstraintData** pathConstraints; -} spSkeletonData; + /// The Spine version used to export this data, or NULL. + const String &getVersion(); -SP_API spSkeletonData* spSkeletonData_create (); -SP_API void spSkeletonData_dispose (spSkeletonData* self); + void setVersion(const String &inValue); -SP_API spBoneData* spSkeletonData_findBone (const spSkeletonData* self, const char* boneName); -SP_API int spSkeletonData_findBoneIndex (const spSkeletonData* self, const char* boneName); + const String &getHash(); -SP_API spSlotData* spSkeletonData_findSlot (const spSkeletonData* self, const char* slotName); -SP_API int spSkeletonData_findSlotIndex (const spSkeletonData* self, const char* slotName); + void setHash(const String &inValue); -SP_API spSkin* spSkeletonData_findSkin (const spSkeletonData* self, const char* skinName); + const String &getImagesPath(); -SP_API spEventData* spSkeletonData_findEvent (const spSkeletonData* self, const char* eventName); + void setImagesPath(const String &inValue); -SP_API spAnimation* spSkeletonData_findAnimation (const spSkeletonData* self, const char* animationName); + const String &getAudioPath(); -SP_API spIkConstraintData* spSkeletonData_findIkConstraint (const spSkeletonData* self, const char* constraintName); + void setAudioPath(const String &inValue); -SP_API spTransformConstraintData* spSkeletonData_findTransformConstraint (const spSkeletonData* self, const char* constraintName); + /// The dopesheet FPS in Spine. Available only when nonessential data was exported. + float getFps(); -SP_API spPathConstraintData* spSkeletonData_findPathConstraint (const spSkeletonData* self, const char* constraintName); + void setFps(float inValue); -#ifdef SPINE_SHORT_NAMES -typedef spSkeletonData SkeletonData; -#define SkeletonData_create(...) spSkeletonData_create(__VA_ARGS__) -#define SkeletonData_dispose(...) spSkeletonData_dispose(__VA_ARGS__) -#define SkeletonData_findBone(...) spSkeletonData_findBone(__VA_ARGS__) -#define SkeletonData_findBoneIndex(...) spSkeletonData_findBoneIndex(__VA_ARGS__) -#define SkeletonData_findSlot(...) spSkeletonData_findSlot(__VA_ARGS__) -#define SkeletonData_findSlotIndex(...) spSkeletonData_findSlotIndex(__VA_ARGS__) -#define SkeletonData_findSkin(...) spSkeletonData_findSkin(__VA_ARGS__) -#define SkeletonData_findEvent(...) spSkeletonData_findEvent(__VA_ARGS__) -#define SkeletonData_findAnimation(...) spSkeletonData_findAnimation(__VA_ARGS__) -#endif +private: + String _name; + Vector _bones; // Ordered parents first + Vector _slots; // Setup pose draw order. + Vector _skins; + Skin *_defaultSkin; + Vector _events; + Vector _animations; + Vector _ikConstraints; + Vector _transformConstraints; + Vector _pathConstraints; + float _width, _height; + String _version; + String _hash; -#ifdef __cplusplus + // Nonessential. + float _fps; + String _imagesPath; + String _audioPath; +}; } -#endif -#endif /* SPINE_SKELETONDATA_H_ */ +#endif /* Spine_SkeletonData_h */ diff --git a/cocos/editor-support/spine/SkeletonJson.cpp b/cocos/editor-support/spine/SkeletonJson.cpp new file mode 100644 index 000000000000..f8007e673254 --- /dev/null +++ b/cocos/editor-support/spine/SkeletonJson.cpp @@ -0,0 +1,1250 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) +#define strdup _strdup +#endif + +using namespace spine; + +SkeletonJson::SkeletonJson(Atlas *atlas) : _attachmentLoader(new(__FILE__, __LINE__) AtlasAttachmentLoader(atlas)), + _scale(1), _ownsLoader(true) { +} + +SkeletonJson::SkeletonJson(AttachmentLoader *attachmentLoader) : _attachmentLoader(attachmentLoader), _scale(1), + _ownsLoader(false) { + assert(_attachmentLoader != NULL); +} + +SkeletonJson::~SkeletonJson() { + ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); + + if (_ownsLoader) { + delete _attachmentLoader; + } +} + +SkeletonData *SkeletonJson::readSkeletonDataFile(const String &path) { + int length; + SkeletonData *skeletonData; + const char *json = SpineExtension::readFile(path, &length); + if (length == 0 || !json) { + setError(NULL, "Unable to read skeleton file: ", path); + return NULL; + } + + skeletonData = readSkeletonData(json); + + SpineExtension::free(json, __FILE__, __LINE__); + + return skeletonData; +} + +SkeletonData *SkeletonJson::readSkeletonData(const char *json) { + int i, ii; + SkeletonData *skeletonData; + Json *root, *skeleton, *bones, *boneMap, *ik, *transform, *path, *slots, *skins, *animations, *events; + + _error = ""; + _linkedMeshes.clear(); + + root = new(__FILE__, __LINE__) Json(json); + + if (!root) { + setError(NULL, "Invalid skeleton JSON: ", Json::getError()); + return NULL; + } + + skeletonData = new(__FILE__, __LINE__) SkeletonData(); + + skeleton = Json::getItem(root, "skeleton"); + if (skeleton) { + skeletonData->_hash = Json::getString(skeleton, "hash", 0); + skeletonData->_version = Json::getString(skeleton, "spine", 0); + skeletonData->_width = Json::getFloat(skeleton, "width", 0); + skeletonData->_height = Json::getFloat(skeleton, "height", 0); + skeletonData->_fps = Json::getFloat(skeleton, "fps", 30); + skeletonData->_audioPath = Json::getString(skeleton, "audio", 0); + skeletonData->_imagesPath = Json::getString(skeleton, "images", 0); + } + + /* Bones. */ + bones = Json::getItem(root, "bones"); + skeletonData->_bones.setSize(bones->_size, 0); + int bonesCount = 0; + for (boneMap = bones->_child, i = 0; boneMap; boneMap = boneMap->_next, ++i) { + BoneData *data; + const char *transformMode; + + BoneData *parent = 0; + const char *parentName = Json::getString(boneMap, "parent", 0); + if (parentName) { + parent = skeletonData->findBone(parentName); + if (!parent) { + delete skeletonData; + setError(root, "Parent bone not found: ", parentName); + return NULL; + } + } + + data = new(__FILE__, __LINE__) BoneData(bonesCount, Json::getString(boneMap, "name", 0), parent); + + data->_length = Json::getFloat(boneMap, "length", 0) * _scale; + data->_x = Json::getFloat(boneMap, "x", 0) * _scale; + data->_y = Json::getFloat(boneMap, "y", 0) * _scale; + data->_rotation = Json::getFloat(boneMap, "rotation", 0); + data->_scaleX = Json::getFloat(boneMap, "scaleX", 1); + data->_scaleY = Json::getFloat(boneMap, "scaleY", 1); + data->_shearX = Json::getFloat(boneMap, "shearX", 0); + data->_shearY = Json::getFloat(boneMap, "shearY", 0); + transformMode = Json::getString(boneMap, "transform", "normal"); + data->_transformMode = TransformMode_Normal; + if (strcmp(transformMode, "normal") == 0) { + data->_transformMode = TransformMode_Normal; + } + if (strcmp(transformMode, "onlyTranslation") == 0) { + data->_transformMode = TransformMode_OnlyTranslation; + } + if (strcmp(transformMode, "noRotationOrReflection") == 0) { + data->_transformMode = TransformMode_NoRotationOrReflection; + } + if (strcmp(transformMode, "noScale") == 0) { + data->_transformMode = TransformMode_NoScale; + } + if (strcmp(transformMode, "noScaleOrReflection") == 0) { + data->_transformMode = TransformMode_NoScaleOrReflection; + } + + skeletonData->_bones[i] = data; + bonesCount++; + } + + /* Slots. */ + slots = Json::getItem(root, "slots"); + if (slots) { + Json *slotMap; + skeletonData->_slots.ensureCapacity(slots->_size); + skeletonData->_slots.setSize(slots->_size, 0); + for (slotMap = slots->_child, i = 0; slotMap; slotMap = slotMap->_next, ++i) { + SlotData *data; + const char *color; + const char *dark; + Json *item; + + const char *boneName = Json::getString(slotMap, "bone", 0); + BoneData *boneData = skeletonData->findBone(boneName); + if (!boneData) { + delete skeletonData; + setError(root, "Slot bone not found: ", boneName); + return NULL; + } + + data = new(__FILE__, __LINE__) SlotData(i, Json::getString(slotMap, "name", 0), *boneData); + + color = Json::getString(slotMap, "color", 0); + if (color) { + Color &c = data->getColor(); + c.r = toColor(color, 0); + c.g = toColor(color, 1); + c.b = toColor(color, 2); + c.a = toColor(color, 3); + } + + dark = Json::getString(slotMap, "dark", 0); + if (dark) { + Color &darkColor = data->getDarkColor(); + darkColor.r = toColor(dark, 0); + darkColor.g = toColor(dark, 1); + darkColor.b = toColor(dark, 2); + darkColor.a = 1; + data->setHasDarkColor(true); + } + + item = Json::getItem(slotMap, "attachment"); + if (item) { + data->setAttachmentName(item->_valueString); + } + + item = Json::getItem(slotMap, "blend"); + if (item) { + if (strcmp(item->_valueString, "additive") == 0) { + data->_blendMode = BlendMode_Additive; + } else if (strcmp(item->_valueString, "multiply") == 0) { + data->_blendMode = BlendMode_Multiply; + } else if (strcmp(item->_valueString, "screen") == 0) { + data->_blendMode = BlendMode_Screen; + } + } + + skeletonData->_slots[i] = data; + } + } + + /* IK constraints. */ + ik = Json::getItem(root, "ik"); + if (ik) { + Json *constraintMap; + skeletonData->_ikConstraints.ensureCapacity(ik->_size); + skeletonData->_ikConstraints.setSize(ik->_size, 0); + for (constraintMap = ik->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { + const char *targetName; + + IkConstraintData *data = new(__FILE__, __LINE__) IkConstraintData( + Json::getString(constraintMap, "name", 0)); + + data->_order = Json::getInt(constraintMap, "order", 0); + + boneMap = Json::getItem(constraintMap, "bones"); + data->_bones.ensureCapacity(boneMap->_size); + data->_bones.setSize(boneMap->_size, 0); + for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) { + data->_bones[ii] = skeletonData->findBone(boneMap->_valueString); + if (!data->_bones[ii]) { + delete skeletonData; + setError(root, "IK bone not found: ", boneMap->_valueString); + return NULL; + } + } + + targetName = Json::getString(constraintMap, "target", 0); + data->_target = skeletonData->findBone(targetName); + if (!data->_target) { + delete skeletonData; + setError(root, "Target bone not found: ", targetName); + return NULL; + } + + data->_mix = Json::getFloat(constraintMap, "mix", 1); + data->_bendDirection = Json::getInt(constraintMap, "bendPositive", 1) ? 1 : -1; + data->_compress = Json::getInt(constraintMap, "compress", 0) ? true: false; + data->_stretch = Json::getInt(constraintMap, "stretch", 0) ? true: false; + data->_uniform = Json::getInt(constraintMap, "uniform", 0) ? true: false; + + skeletonData->_ikConstraints[i] = data; + } + } + + /* Transform constraints. */ + transform = Json::getItem(root, "transform"); + if (transform) { + Json *constraintMap; + skeletonData->_transformConstraints.ensureCapacity(transform->_size); + skeletonData->_transformConstraints.setSize(transform->_size, 0); + for (constraintMap = transform->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { + const char *name; + + TransformConstraintData *data = new(__FILE__, __LINE__) TransformConstraintData( + Json::getString(constraintMap, "name", 0)); + + data->_order = Json::getInt(constraintMap, "order", 0); + + boneMap = Json::getItem(constraintMap, "bones"); + data->_bones.ensureCapacity(boneMap->_size); + data->_bones.setSize(boneMap->_size, 0); + for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) { + data->_bones[ii] = skeletonData->findBone(boneMap->_valueString); + if (!data->_bones[ii]) { + delete skeletonData; + setError(root, "Transform bone not found: ", boneMap->_valueString); + return NULL; + } + } + + name = Json::getString(constraintMap, "target", 0); + data->_target = skeletonData->findBone(name); + if (!data->_target) { + delete skeletonData; + setError(root, "Target bone not found: ", name); + return NULL; + } + + data->_local = Json::getInt(constraintMap, "local", 0) ? true : false; + data->_relative = Json::getInt(constraintMap, "relative", 0) ? true : false; + data->_offsetRotation = Json::getFloat(constraintMap, "rotation", 0); + data->_offsetX = Json::getFloat(constraintMap, "x", 0) * _scale; + data->_offsetY = Json::getFloat(constraintMap, "y", 0) * _scale; + data->_offsetScaleX = Json::getFloat(constraintMap, "scaleX", 0); + data->_offsetScaleY = Json::getFloat(constraintMap, "scaleY", 0); + data->_offsetShearY = Json::getFloat(constraintMap, "shearY", 0); + + data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1); + data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1); + data->_scaleMix = Json::getFloat(constraintMap, "scaleMix", 1); + data->_shearMix = Json::getFloat(constraintMap, "shearMix", 1); + + skeletonData->_transformConstraints[i] = data; + } + } + + /* Path constraints */ + path = Json::getItem(root, "path"); + if (path) { + Json *constraintMap; + skeletonData->_pathConstraints.ensureCapacity(path->_size); + skeletonData->_pathConstraints.setSize(path->_size, 0); + for (constraintMap = path->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { + const char *name; + const char *item; + + PathConstraintData *data = new(__FILE__, __LINE__) PathConstraintData( + Json::getString(constraintMap, "name", 0)); + + data->_order = Json::getInt(constraintMap, "order", 0); + + boneMap = Json::getItem(constraintMap, "bones"); + data->_bones.ensureCapacity(boneMap->_size); + data->_bones.setSize(boneMap->_size, 0); + for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) { + data->_bones[ii] = skeletonData->findBone(boneMap->_valueString); + if (!data->_bones[ii]) { + delete skeletonData; + setError(root, "Path bone not found: ", boneMap->_valueString); + return NULL; + } + } + + name = Json::getString(constraintMap, "target", 0); + data->_target = skeletonData->findSlot(name); + if (!data->_target) { + delete skeletonData; + setError(root, "Target slot not found: ", name); + return NULL; + } + + item = Json::getString(constraintMap, "positionMode", "percent"); + if (strcmp(item, "fixed") == 0) { + data->_positionMode = PositionMode_Fixed; + } else if (strcmp(item, "percent") == 0) { + data->_positionMode = PositionMode_Percent; + } + + item = Json::getString(constraintMap, "spacingMode", "length"); + if (strcmp(item, "length") == 0) { + data->_spacingMode = SpacingMode_Length; + } else if (strcmp(item, "fixed") == 0) { + data->_spacingMode = SpacingMode_Fixed; + } else if (strcmp(item, "percent") == 0) { + data->_spacingMode = SpacingMode_Percent; + } + + item = Json::getString(constraintMap, "rotateMode", "tangent"); + if (strcmp(item, "tangent") == 0) { + data->_rotateMode = RotateMode_Tangent; + } else if (strcmp(item, "chain") == 0) { + data->_rotateMode = RotateMode_Chain; + } else if (strcmp(item, "chainScale") == 0) { + data->_rotateMode = RotateMode_ChainScale; + } + + data->_offsetRotation = Json::getFloat(constraintMap, "rotation", 0); + data->_position = Json::getFloat(constraintMap, "position", 0); + if (data->_positionMode == PositionMode_Fixed) { + data->_position *= _scale; + } + data->_spacing = Json::getFloat(constraintMap, "spacing", 0); + if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) { + data->_spacing *= _scale; + } + data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1); + data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1); + + skeletonData->_pathConstraints[i] = data; + } + } + + /* Skins. */ + skins = Json::getItem(root, "skins"); + if (skins) { + Json *skinMap; + skeletonData->_skins.ensureCapacity(skins->_size); + skeletonData->_skins.setSize(skins->_size, 0); + int skinsIndex = 0; + for (skinMap = skins->_child, i = 0; skinMap; skinMap = skinMap->_next, ++i) { + Json *attachmentsMap; + Json *curves; + + Skin *skin = new(__FILE__, __LINE__) Skin(skinMap->_name); + + skeletonData->_skins[skinsIndex++] = skin; + if (strcmp(skinMap->_name, "default") == 0) { + skeletonData->_defaultSkin = skin; + } + + for (attachmentsMap = skinMap->_child; attachmentsMap; attachmentsMap = attachmentsMap->_next) { + int slotIndex = skeletonData->findSlotIndex(attachmentsMap->_name); + Json *attachmentMap; + + for (attachmentMap = attachmentsMap->_child; attachmentMap; attachmentMap = attachmentMap->_next) { + Attachment *attachment = NULL; + const char *skinAttachmentName = attachmentMap->_name; + const char *attachmentName = Json::getString(attachmentMap, "name", skinAttachmentName); + const char *attachmentPath = Json::getString(attachmentMap, "path", attachmentName); + const char *color; + Json *entry; + + const char *typeString = Json::getString(attachmentMap, "type", "region"); + AttachmentType type; + if (strcmp(typeString, "region") == 0) { + type = AttachmentType_Region; + } else if (strcmp(typeString, "mesh") == 0) { + type = AttachmentType_Mesh; + } else if (strcmp(typeString, "linkedmesh") == 0) { + type = AttachmentType_Linkedmesh; + } else if (strcmp(typeString, "boundingbox") == 0) { + type = AttachmentType_Boundingbox; + } else if (strcmp(typeString, "path") == 0) { + type = AttachmentType_Path; + } else if (strcmp(typeString, "clipping") == 0) { + type = AttachmentType_Clipping; + } else if (strcmp(typeString, "point") == 0) { + type = AttachmentType_Point; + }else { + delete skeletonData; + setError(root, "Unknown attachment type: ", typeString); + return NULL; + } + + switch (type) { + case AttachmentType_Region: { + attachment = _attachmentLoader->newRegionAttachment(*skin, attachmentName, attachmentPath); + if (!attachment) { + delete skeletonData; + setError(root, "Error reading attachment: ", skinAttachmentName); + return NULL; + } + + RegionAttachment *region = static_cast(attachment); + region->_path = attachmentPath; + + region->_x = Json::getFloat(attachmentMap, "x", 0) * _scale; + region->_y = Json::getFloat(attachmentMap, "y", 0) * _scale; + region->_scaleX = Json::getFloat(attachmentMap, "scaleX", 1); + region->_scaleY = Json::getFloat(attachmentMap, "scaleY", 1); + region->_rotation = Json::getFloat(attachmentMap, "rotation", 0); + region->_width = Json::getFloat(attachmentMap, "width", 32) * _scale; + region->_height = Json::getFloat(attachmentMap, "height", 32) * _scale; + + color = Json::getString(attachmentMap, "color", 0); + if (color) { + region->getColor().r = toColor(color, 0); + region->getColor().g = toColor(color, 1); + region->getColor().b = toColor(color, 2); + region->getColor().a = toColor(color, 3); + } + + region->updateOffset(); + _attachmentLoader->configureAttachment(region); + break; + } + case AttachmentType_Mesh: + case AttachmentType_Linkedmesh: { + attachment = _attachmentLoader->newMeshAttachment(*skin, attachmentName, attachmentPath); + + if (!attachment) { + delete skeletonData; + setError(root, "Error reading attachment: ", skinAttachmentName); + return NULL; + } + + MeshAttachment *mesh = static_cast(attachment); + mesh->_path = attachmentPath; + + color = Json::getString(attachmentMap, "color", 0); + if (color) { + mesh->getColor().r = toColor(color, 0); + mesh->getColor().g = toColor(color, 1); + mesh->getColor().b = toColor(color, 2); + mesh->getColor().a = toColor(color, 3); + } + + mesh->_width = Json::getFloat(attachmentMap, "width", 32) * _scale; + mesh->_height = Json::getFloat(attachmentMap, "height", 32) * _scale; + + entry = Json::getItem(attachmentMap, "parent"); + if (!entry) { + int verticesLength; + entry = Json::getItem(attachmentMap, "triangles"); + mesh->_triangles.ensureCapacity(entry->_size); + mesh->_triangles.setSize(entry->_size, 0); + for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii) { + mesh->_triangles[ii] = (unsigned short) entry->_valueInt; + } + + entry = Json::getItem(attachmentMap, "uvs"); + verticesLength = entry->_size; + mesh->_regionUVs.ensureCapacity(verticesLength); + mesh->_regionUVs.setSize(verticesLength, 0); + for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii) { + mesh->_regionUVs[ii] = entry->_valueFloat; + } + + readVertices(attachmentMap, mesh, verticesLength); + + mesh->updateUVs(); + + mesh->_hullLength = Json::getInt(attachmentMap, "hull", 0); + + entry = Json::getItem(attachmentMap, "edges"); + if (entry) { + mesh->_edges.ensureCapacity(entry->_size); + mesh->_edges.setSize(entry->_size, 0); + for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii) { + mesh->_edges[ii] = entry->_valueInt; + } + } + _attachmentLoader->configureAttachment(mesh); + } else { + mesh->_inheritDeform = Json::getInt(attachmentMap, "deform", 1) ? true : false; + LinkedMesh *linkedMesh = new(__FILE__, __LINE__) LinkedMesh(mesh, + String(Json::getString( + attachmentMap, + "skin", 0)), + slotIndex, + String(entry->_valueString)); + _linkedMeshes.add(linkedMesh); + } + break; + } + case AttachmentType_Boundingbox: { + attachment = _attachmentLoader->newBoundingBoxAttachment(*skin, attachmentName); + + BoundingBoxAttachment *box = static_cast(attachment); + + int vertexCount = Json::getInt(attachmentMap, "vertexCount", 0) << 1; + readVertices(attachmentMap, box, vertexCount); + _attachmentLoader->configureAttachment(attachment); + break; + } + case AttachmentType_Path: { + attachment = _attachmentLoader->newPathAttachment(*skin, attachmentName); + + PathAttachment *pathAttatchment = static_cast(attachment); + + int vertexCount = 0; + pathAttatchment->_closed = Json::getInt(attachmentMap, "closed", 0) ? true : false; + pathAttatchment->_constantSpeed = Json::getInt(attachmentMap, "constantSpeed", 1) ? true : false; + vertexCount = Json::getInt(attachmentMap, "vertexCount", 0); + readVertices(attachmentMap, pathAttatchment, vertexCount << 1); + + pathAttatchment->_lengths.ensureCapacity(vertexCount / 3); + pathAttatchment->_lengths.setSize(vertexCount / 3, 0); + + curves = Json::getItem(attachmentMap, "lengths"); + for (curves = curves->_child, ii = 0; curves; curves = curves->_next, ++ii) { + pathAttatchment->_lengths[ii] = curves->_valueFloat * _scale; + } + _attachmentLoader->configureAttachment(attachment); + break; + } + case AttachmentType_Point: { + attachment = _attachmentLoader->newPointAttachment(*skin, attachmentName); + + PointAttachment *point = static_cast(attachment); + + point->_x = Json::getFloat(attachmentMap, "x", 0) * _scale; + point->_y = Json::getFloat(attachmentMap, "y", 0) * _scale; + point->_rotation = Json::getFloat(attachmentMap, "rotation", 0); + _attachmentLoader->configureAttachment(attachment); + break; + } + case AttachmentType_Clipping: { + attachment = _attachmentLoader->newClippingAttachment(*skin, attachmentName); + + ClippingAttachment *clip = static_cast(attachment); + + int vertexCount = 0; + const char *end = Json::getString(attachmentMap, "end", 0); + if (end) { + SlotData *slot = skeletonData->findSlot(end); + clip->_endSlot = slot; + } + vertexCount = Json::getInt(attachmentMap, "vertexCount", 0) << 1; + readVertices(attachmentMap, clip, vertexCount); + _attachmentLoader->configureAttachment(attachment); + break; + } + } + + skin->addAttachment(slotIndex, skinAttachmentName, attachment); + } + } + } + } + + /* Linked meshes. */ + int n = _linkedMeshes.size(); + for (i = 0; i < n; ++i) { + LinkedMesh *linkedMesh = _linkedMeshes[i]; + Skin *skin = linkedMesh->_skin.length() == 0 ? skeletonData->getDefaultSkin() : skeletonData->findSkin( + linkedMesh->_skin); + if (skin == NULL) { + delete skeletonData; + setError(root, "Skin not found: ", linkedMesh->_skin.buffer()); + return NULL; + } + Attachment *parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent); + if (parent == NULL) { + delete skeletonData; + setError(root, "Parent mesh not found: ", linkedMesh->_parent.buffer()); + return NULL; + } + linkedMesh->_mesh->setParentMesh(static_cast(parent)); + linkedMesh->_mesh->updateUVs(); + _attachmentLoader->configureAttachment(linkedMesh->_mesh); + } + ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); + _linkedMeshes.clear(); + + /* Events. */ + events = Json::getItem(root, "events"); + if (events) { + Json *eventMap; + skeletonData->_events.ensureCapacity(events->_size); + skeletonData->_events.setSize(events->_size, 0); + for (eventMap = events->_child, i = 0; eventMap; eventMap = eventMap->_next, ++i) { + EventData *eventData = new(__FILE__, __LINE__) EventData(String(eventMap->_name)); + + eventData->_intValue = Json::getInt(eventMap, "int", 0); + eventData->_floatValue = Json::getFloat(eventMap, "float", 0); + const char *stringValue = Json::getString(eventMap, "string", 0); + eventData->_stringValue = stringValue; + const char *audioPath = Json::getString(eventMap, "audio", 0); + eventData->_audioPath = audioPath; + if (audioPath) { + eventData->_volume = Json::getFloat(eventMap, "volume", 1); + eventData->_balance = Json::getFloat(eventMap, "balance", 0); + } + skeletonData->_events[i] = eventData; + } + } + + /* Animations. */ + animations = Json::getItem(root, "animations"); + if (animations) { + Json *animationMap; + skeletonData->_animations.ensureCapacity(animations->_size); + skeletonData->_animations.setSize(animations->_size, 0); + int animationsIndex = 0; + for (animationMap = animations->_child; animationMap; animationMap = animationMap->_next) { + Animation *animation = readAnimation(animationMap, skeletonData); + if (!animation) { + delete skeletonData; + delete root; + return NULL; + } + skeletonData->_animations[animationsIndex++] = animation; + } + } + + delete root; + + return skeletonData; +} + +float SkeletonJson::toColor(const char *value, size_t index) { + char digits[3]; + char *error; + int color; + + if (index >= strlen(value) / 2) { + return -1; + } + + value += index * 2; + + digits[0] = *value; + digits[1] = *(value + 1); + digits[2] = '\0'; + color = (int) strtoul(digits, &error, 16); + if (*error != 0) { + return -1; + } + + return color / (float) 255; +} + +void SkeletonJson::readCurve(Json *frame, CurveTimeline *timeline, size_t frameIndex) { + Json *curve = Json::getItem(frame, "curve"); + if (!curve) { + return; + } + if (curve->_type == Json::JSON_STRING && strcmp(curve->_valueString, "stepped") == 0) { + timeline->setStepped(frameIndex); + } else if (curve->_type == Json::JSON_ARRAY) { + Json *child0 = curve->_child; + Json *child1 = child0->_next; + Json *child2 = child1->_next; + Json *child3 = child2->_next; + timeline->setCurve(frameIndex, child0->_valueFloat, child1->_valueFloat, child2->_valueFloat, + child3->_valueFloat); + } +} + +Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { + Vector timelines; + float duration = 0; + + size_t frameIndex; + Json *valueMap; + int timelinesCount = 0; + + Json *bones = Json::getItem(root, "bones"); + Json *slots = Json::getItem(root, "slots"); + Json *ik = Json::getItem(root, "ik"); + Json *transform = Json::getItem(root, "transform"); + Json *paths = Json::getItem(root, "paths"); + Json *deform = Json::getItem(root, "deform"); + Json *drawOrder = Json::getItem(root, "drawOrder"); + Json *events = Json::getItem(root, "events"); + Json *boneMap, *slotMap, *constraintMap; + if (!drawOrder) { + drawOrder = Json::getItem(root, "draworder"); + } + + for (boneMap = bones ? bones->_child : NULL; boneMap; boneMap = boneMap->_next) { + timelinesCount += boneMap->_size; + } + + for (slotMap = slots ? slots->_child : NULL; slotMap; slotMap = slotMap->_next) { + timelinesCount += slotMap->_size; + } + + timelinesCount += ik ? ik->_size : 0; + timelinesCount += transform ? transform->_size : 0; + + for (constraintMap = paths ? paths->_child : NULL; constraintMap; constraintMap = constraintMap->_next) { + timelinesCount += constraintMap->_size; + } + + for (constraintMap = deform ? deform->_child : NULL; constraintMap; constraintMap = constraintMap->_next) { + for (slotMap = constraintMap->_child; slotMap; slotMap = slotMap->_next) { + timelinesCount += slotMap->_size; + } + } + + if (drawOrder) { + ++timelinesCount; + } + + if (events) { + ++timelinesCount; + } + + /** Slot timelines. */ + for (slotMap = slots ? slots->_child : 0; slotMap; slotMap = slotMap->_next) { + Json *timelineMap; + + int slotIndex = skeletonData->findSlotIndex(slotMap->_name); + if (slotIndex == -1) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Slot not found: ", slotMap->_name); + return NULL; + } + + for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) { + if (strcmp(timelineMap->_name, "attachment") == 0) { + AttachmentTimeline *timeline = new(__FILE__, __LINE__) AttachmentTimeline(timelineMap->_size); + + timeline->_slotIndex = slotIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + Json *name = Json::getItem(valueMap, "name"); + String attachmentName = name->_type == Json::JSON_NULL ? "" : name->_valueString; + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), attachmentName); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, timeline->_frames[timelineMap->_size - 1]); + + } else if (strcmp(timelineMap->_name, "color") == 0) { + ColorTimeline *timeline = new(__FILE__, __LINE__) ColorTimeline(timelineMap->_size); + + timeline->_slotIndex = slotIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + const char *s = Json::getString(valueMap, "color", 0); + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1), + toColor(s, 2), toColor(s, 3)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, + timeline->_frames[(timelineMap->_size - 1) * ColorTimeline::ENTRIES]); + + } else if (strcmp(timelineMap->_name, "twoColor") == 0) { + TwoColorTimeline *timeline = new(__FILE__, __LINE__) TwoColorTimeline(timelineMap->_size); + + timeline->_slotIndex = slotIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + const char *s = Json::getString(valueMap, "light", 0); + const char *ds = Json::getString(valueMap, "dark", 0); + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1), + toColor(s, 2), + toColor(s, 3), toColor(ds, 0), toColor(ds, 1), toColor(ds, 2)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, + timeline->_frames[(timelineMap->_size - 1) * TwoColorTimeline::ENTRIES]); + } else { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Invalid timeline type for a slot: ", timelineMap->_name); + return NULL; + } + } + } + + /** Bone timelines. */ + for (boneMap = bones ? bones->_child : 0; boneMap; boneMap = boneMap->_next) { + Json *timelineMap; + + int boneIndex = skeletonData->findBoneIndex(boneMap->_name); + if (boneIndex == -1) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Bone not found: ", boneMap->_name); + return NULL; + } + + for (timelineMap = boneMap->_child; timelineMap; timelineMap = timelineMap->_next) { + if (strcmp(timelineMap->_name, "rotate") == 0) { + RotateTimeline *timeline = new(__FILE__, __LINE__) RotateTimeline(timelineMap->_size); + + timeline->_boneIndex = boneIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), + Json::getFloat(valueMap, "angle", 0)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, + timeline->_frames[(timelineMap->_size - 1) * RotateTimeline::ENTRIES]); + } else { + int isScale = strcmp(timelineMap->_name, "scale") == 0; + int isTranslate = strcmp(timelineMap->_name, "translate") == 0; + int isShear = strcmp(timelineMap->_name, "shear") == 0; + if (isScale || isTranslate || isShear) { + float timelineScale = isTranslate ? _scale : 1; + TranslateTimeline *timeline = 0; + if (isScale) { + timeline = new(__FILE__, __LINE__) ScaleTimeline(timelineMap->_size); + } else if (isTranslate) { + timeline = new(__FILE__, __LINE__) TranslateTimeline(timelineMap->_size); + } else if (isShear) { + timeline = new(__FILE__, __LINE__) ShearTimeline(timelineMap->_size); + } + timeline->_boneIndex = boneIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), + Json::getFloat(valueMap, "x", 0) * timelineScale, + Json::getFloat(valueMap, "y", 0) * timelineScale); + readCurve(valueMap, timeline, frameIndex); + } + + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, + timeline->_frames[(timelineMap->_size - 1) * TranslateTimeline::ENTRIES]); + } else { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Invalid timeline type for a bone: ", timelineMap->_name); + return NULL; + } + } + } + } + + /** IK constraint timelines. */ + for (constraintMap = ik ? ik->_child : 0; constraintMap; constraintMap = constraintMap->_next) { + IkConstraintData *constraint = skeletonData->findIkConstraint(constraintMap->_name); + IkConstraintTimeline *timeline = new(__FILE__, __LINE__) IkConstraintTimeline(constraintMap->_size); + + for (frameIndex = 0; frameIndex < skeletonData->_ikConstraints.size(); ++frameIndex) { + if (constraint == skeletonData->_ikConstraints[frameIndex]) { + timeline->_ikConstraintIndex = frameIndex; + break; + } + } + for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "mix", 1), + Json::getInt(valueMap, "bendPositive", 1) ? 1 : -1, Json::getInt(valueMap, "compress", 0) ? true : false, Json::getInt(valueMap, "stretch", 0) ? true : false); + readCurve(valueMap, timeline, frameIndex); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, + timeline->_frames[(constraintMap->_size - 1) * IkConstraintTimeline::ENTRIES]); + } + + /** Transform constraint timelines. */ + for (constraintMap = transform ? transform->_child : 0; constraintMap; constraintMap = constraintMap->_next) { + TransformConstraintData *constraint = skeletonData->findTransformConstraint(constraintMap->_name); + TransformConstraintTimeline *timeline = new(__FILE__, __LINE__) TransformConstraintTimeline( + constraintMap->_size); + + for (frameIndex = 0; frameIndex < skeletonData->_transformConstraints.size(); ++frameIndex) { + if (constraint == skeletonData->_transformConstraints[frameIndex]) { + timeline->_transformConstraintIndex = frameIndex; + break; + } + } + for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), + Json::getFloat(valueMap, "rotateMix", 1), Json::getFloat(valueMap, "translateMix", 1), + Json::getFloat(valueMap, "scaleMix", 1), Json::getFloat(valueMap, "shearMix", 1)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, + timeline->_frames[(constraintMap->_size - 1) * TransformConstraintTimeline::ENTRIES]); + } + + /** Path constraint timelines. */ + for (constraintMap = paths ? paths->_child : 0; constraintMap; constraintMap = constraintMap->_next) { + size_t constraintIndex = 0, i; + Json *timelineMap; + + PathConstraintData *data = skeletonData->findPathConstraint(constraintMap->_name); + if (!data) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Path constraint not found: ", constraintMap->_name); + return NULL; + } + + for (i = 0; i < skeletonData->_pathConstraints.size(); i++) { + if (skeletonData->_pathConstraints[i] == data) { + constraintIndex = i; + break; + } + } + + for (timelineMap = constraintMap->_child; timelineMap; timelineMap = timelineMap->_next) { + const char *timelineName = timelineMap->_name; + if (strcmp(timelineName, "position") == 0 || strcmp(timelineName, "spacing") == 0) { + PathConstraintPositionTimeline *timeline; + float timelineScale = 1; + if (strcmp(timelineName, "spacing") == 0) { + timeline = new(__FILE__, __LINE__) PathConstraintSpacingTimeline(timelineMap->_size); + + if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) { + timelineScale = _scale; + } + } else { + timeline = new(__FILE__, __LINE__) PathConstraintPositionTimeline(timelineMap->_size); + + if (data->_positionMode == PositionMode_Fixed) { + timelineScale = _scale; + } + } + + timeline->_pathConstraintIndex = constraintIndex; + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), + Json::getFloat(valueMap, timelineName, 0) * timelineScale); + readCurve(valueMap, timeline, frameIndex); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * + PathConstraintPositionTimeline::ENTRIES]); + } else if (strcmp(timelineName, "mix") == 0) { + PathConstraintMixTimeline *timeline = new(__FILE__, __LINE__) PathConstraintMixTimeline( + timelineMap->_size); + timeline->_pathConstraintIndex = constraintIndex; + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), + Json::getFloat(valueMap, "rotateMix", 1), + Json::getFloat(valueMap, "translateMix", 1)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * + PathConstraintMixTimeline::ENTRIES]); + } + } + } + + /** Deform timelines. */ + for (constraintMap = deform ? deform->_child : NULL; constraintMap; constraintMap = constraintMap->_next) { + Skin *skin = skeletonData->findSkin(constraintMap->_name); + for (slotMap = constraintMap->_child; slotMap; slotMap = slotMap->_next) { + int slotIndex = skeletonData->findSlotIndex(slotMap->_name); + Json *timelineMap; + for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) { + DeformTimeline *timeline; + int weighted, deformLength; + + Attachment *baseAttachment = skin->getAttachment(slotIndex, timelineMap->_name); + + if (!baseAttachment) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Attachment not found: ", timelineMap->_name); + return NULL; + } + + VertexAttachment *attachment = static_cast(baseAttachment); + + weighted = attachment->_bones.size() != 0; + Vector &verts = attachment->_vertices; + deformLength = weighted ? verts.size() / 3 * 2 : verts.size(); + + timeline = new(__FILE__, __LINE__) DeformTimeline(timelineMap->_size); + + timeline->_slotIndex = slotIndex; + timeline->_attachment = attachment; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + Json *vertices = Json::getItem(valueMap, "vertices"); + Vector deformed; + if (!vertices) { + if (weighted) { + deformed.setSize(deformLength, 0); + } else { + deformed.clearAndAddAll(attachment->_vertices); + } + } else { + int v, start = Json::getInt(valueMap, "offset", 0); + Json *vertex; + deformed.setSize(deformLength, 0); + if (_scale == 1) { + for (vertex = vertices->_child, v = start; vertex; vertex = vertex->_next, ++v) { + deformed[v] = vertex->_valueFloat; + } + } else { + for (vertex = vertices->_child, v = start; vertex; vertex = vertex->_next, ++v) { + deformed[v] = vertex->_valueFloat * _scale; + } + } + if (!weighted) { + Vector &verticesAttachment = attachment->_vertices; + for (v = 0; v < deformLength; ++v) { + deformed[v] += verticesAttachment[v]; + } + } + } + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), deformed); + readCurve(valueMap, timeline, frameIndex); + } + + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, timeline->_frames[timelineMap->_size - 1]); + } + } + } + + /** Draw order timeline. */ + if (drawOrder) { + DrawOrderTimeline *timeline = new(__FILE__, __LINE__) DrawOrderTimeline(drawOrder->_size); + + for (valueMap = drawOrder->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + int ii; + Vector drawOrder2; + Json *offsets = Json::getItem(valueMap, "offsets"); + if (offsets) { + Json *offsetMap; + Vector unchanged; + unchanged.ensureCapacity(skeletonData->_slots.size() - offsets->_size); + unchanged.setSize(skeletonData->_slots.size() - offsets->_size, 0); + size_t originalIndex = 0, unchangedIndex = 0; + + drawOrder2.ensureCapacity(skeletonData->_slots.size()); + drawOrder2.setSize(skeletonData->_slots.size(), 0); + for (ii = (int)skeletonData->_slots.size() - 1; ii >= 0; --ii) { + drawOrder2[ii] = -1; + } + + for (offsetMap = offsets->_child; offsetMap; offsetMap = offsetMap->_next) { + int slotIndex = skeletonData->findSlotIndex(Json::getString(offsetMap, "slot", 0)); + if (slotIndex == -1) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Slot not found: ", Json::getString(offsetMap, "slot", 0)); + return NULL; + } + /* Collect unchanged items. */ + while (originalIndex != (size_t)slotIndex) { + unchanged[unchangedIndex++] = originalIndex++; + } + /* Set changed items. */ + drawOrder2[originalIndex + Json::getInt(offsetMap, "offset", 0)] = originalIndex; + originalIndex++; + } + /* Collect remaining unchanged items. */ + while (originalIndex < skeletonData->_slots.size()) { + unchanged[unchangedIndex++] = originalIndex++; + } + /* Fill in unchanged items. */ + for (ii = (int)skeletonData->_slots.size() - 1; ii >= 0; ii--) { + if (drawOrder2[ii] == -1) { + drawOrder2[ii] = unchanged[--unchangedIndex]; + } + } + } + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), drawOrder2); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, timeline->_frames[drawOrder->_size - 1]); + } + + /** Event timeline. */ + if (events) { + EventTimeline *timeline = new(__FILE__, __LINE__) EventTimeline(events->_size); + + for (valueMap = events->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + Event *event; + EventData *eventData = skeletonData->findEvent(Json::getString(valueMap, "name", 0)); + if (!eventData) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Event not found: ", Json::getString(valueMap, "name", 0)); + return NULL; + } + + event = new(__FILE__, __LINE__) Event(Json::getFloat(valueMap, "time", 0), *eventData); + event->_intValue = Json::getInt(valueMap, "int", eventData->_intValue); + event->_floatValue = Json::getFloat(valueMap, "float", eventData->_floatValue); + event->_stringValue = Json::getString(valueMap, "string", eventData->_stringValue.buffer()); + if (!eventData->_audioPath.isEmpty()) { + event->_volume = Json::getFloat(valueMap, "volume", 1); + event->_balance = Json::getFloat(valueMap, "balance", 0); + } + timeline->setFrame(frameIndex, event); + } + timelines.add(timeline); + timelinesCount++; + duration = MathUtil::max(duration, timeline->_frames[events->_size - 1]); + } + + return new(__FILE__, __LINE__) Animation(String(root->_name), timelines, duration); +} + +void SkeletonJson::readVertices(Json *attachmentMap, VertexAttachment *attachment, size_t verticesLength) { + Json *entry; + size_t i, n, nn, entrySize; + Vector vertices; + + attachment->setWorldVerticesLength(verticesLength); + + entry = Json::getItem(attachmentMap, "vertices"); + entrySize = entry->_size; + vertices.ensureCapacity(entrySize); + vertices.setSize(entrySize, 0); + for (entry = entry->_child, i = 0; entry; entry = entry->_next, ++i) { + vertices[i] = entry->_valueFloat; + } + + if (verticesLength == entrySize) { + if (_scale != 1) { + for (i = 0; i < entrySize; ++i) { + vertices[i] *= _scale; + } + } + + attachment->getVertices().clearAndAddAll(vertices); + return; + } + + Vertices bonesAndWeights; + bonesAndWeights._bones.ensureCapacity(verticesLength * 3); + bonesAndWeights._vertices.ensureCapacity(verticesLength * 3 * 3); + + for (i = 0, n = entrySize; i < n;) { + int boneCount = (int) vertices[i++]; + bonesAndWeights._bones.add(boneCount); + for (nn = i + boneCount * 4; i < nn; i += 4) { + bonesAndWeights._bones.add((int) vertices[i]); + bonesAndWeights._vertices.add(vertices[i + 1] * _scale); + bonesAndWeights._vertices.add(vertices[i + 2] * _scale); + bonesAndWeights._vertices.add(vertices[i + 3]); + } + } + + attachment->getVertices().clearAndAddAll(bonesAndWeights._vertices); + attachment->getBones().clearAndAddAll(bonesAndWeights._bones); +} + +void SkeletonJson::setError(Json *root, const String &value1, const String &value2) { + _error = String(value1).append(value2); + + delete root; +} diff --git a/cocos/editor-support/spine/SkeletonJson.h b/cocos/editor-support/spine/SkeletonJson.h index 58eacdd8b2b1..06b6a0bf771a 100644 --- a/cocos/editor-support/spine/SkeletonJson.h +++ b/cocos/editor-support/spine/SkeletonJson.h @@ -1,73 +1,91 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_SKELETONJSON_H_ -#define SPINE_SKELETONJSON_H_ - -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct spAtlasAttachmentLoader; - -typedef struct spSkeletonJson { - float scale; - spAttachmentLoader* attachmentLoader; - const char* const error; -} spSkeletonJson; - -SP_API spSkeletonJson* spSkeletonJson_createWithLoader (spAttachmentLoader* attachmentLoader); -SP_API spSkeletonJson* spSkeletonJson_create (spAtlas* atlas); -SP_API void spSkeletonJson_dispose (spSkeletonJson* self); - -SP_API spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const char* json); -SP_API spSkeletonData* spSkeletonJson_readSkeletonDataFile (spSkeletonJson* self, const char* path); - -#ifdef SPINE_SHORT_NAMES -typedef spSkeletonJson SkeletonJson; -#define SkeletonJson_createWithLoader(...) spSkeletonJson_createWithLoader(__VA_ARGS__) -#define SkeletonJson_create(...) spSkeletonJson_create(__VA_ARGS__) -#define SkeletonJson_dispose(...) spSkeletonJson_dispose(__VA_ARGS__) -#define SkeletonJson_readSkeletonData(...) spSkeletonJson_readSkeletonData(__VA_ARGS__) -#define SkeletonJson_readSkeletonDataFile(...) spSkeletonJson_readSkeletonDataFile(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#ifndef Spine_SkeletonJson_h +#define Spine_SkeletonJson_h + +#include +#include +#include + +namespace spine { +class CurveTimeline; + +class VertexAttachment; + +class Animation; + +class Json; + +class SkeletonData; + +class Atlas; + +class AttachmentLoader; + +class LinkedMesh; + +class String; + +class SP_API SkeletonJson : public SpineObject { +public: + explicit SkeletonJson(Atlas *atlas); + + explicit SkeletonJson(AttachmentLoader *attachmentLoader); + + ~SkeletonJson(); + + SkeletonData *readSkeletonDataFile(const String &path); + + SkeletonData *readSkeletonData(const char *json); + + void setScale(float scale) { _scale = scale; } + + String &getError() { return _error; } + +private: + AttachmentLoader *_attachmentLoader; + Vector _linkedMeshes; + float _scale; + const bool _ownsLoader; + String _error; + + static float toColor(const char *value, size_t index); + + static void readCurve(Json *frame, CurveTimeline *timeline, size_t frameIndex); + + Animation *readAnimation(Json *root, SkeletonData *skeletonData); + + void readVertices(Json *attachmentMap, VertexAttachment *attachment, size_t verticesLength); + + void setError(Json *root, const String &value1, const String &value2); +}; } -#endif -#endif /* SPINE_SKELETONJSON_H_ */ +#endif /* Spine_SkeletonJson_h */ diff --git a/cocos/editor-support/spine/SkeletonRenderer.cpp b/cocos/editor-support/spine/SkeletonRenderer.cpp index 9c3c0ba40a9f..bb6556a50ab6 100644 --- a/cocos/editor-support/spine/SkeletonRenderer.cpp +++ b/cocos/editor-support/spine/SkeletonRenderer.cpp @@ -1,791 +1,909 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +#include #include -#include +#include #include #include #include -#include #include -USING_NS_CC; -using std::min; -using std::max; - -namespace spine { +#include "renderer/backend/Types.h" -SkeletonRenderer* SkeletonRenderer::createWithData (spSkeletonData* skeletonData, bool ownsSkeletonData) { - SkeletonRenderer* node = new SkeletonRenderer(skeletonData, ownsSkeletonData); - node->autorelease(); - return node; -} +#define INITIAL_WORLD_VERTICES_LENGTH 1000 +// Used for transforming attachments for bounding boxes & debug rendering +static float* worldVertices = nullptr; +static size_t worldVerticesLength = 0; -SkeletonRenderer* SkeletonRenderer::createWithFile (const std::string& skeletonDataFile, spAtlas* atlas, float scale) { - SkeletonRenderer* node = new SkeletonRenderer(skeletonDataFile, atlas, scale); - node->autorelease(); - return node; +void ensureWorldVerticesCapacity(size_t capacity) { + if (worldVerticesLength < capacity) { + float* newWorldVertices = new float[capacity]; + memcpy(newWorldVertices, worldVertices, capacity * sizeof(float)); + delete[] worldVertices; + worldVertices = newWorldVertices; + worldVerticesLength = capacity; + } } -SkeletonRenderer* SkeletonRenderer::createWithFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { - SkeletonRenderer* node = new SkeletonRenderer(skeletonDataFile, atlasFile, scale); - node->autorelease(); - return node; -} +USING_NS_CC; +using std::min; +using std::max; -void SkeletonRenderer::initialize () { - _worldVertices = new float[1000]; // Max number of vertices per mesh. +namespace spine { - _clipper = spSkeletonClipping_create(); - - _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED; - setOpacityModifyRGB(true); - - setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP)); -} - -void SkeletonRenderer::setSkeletonData (spSkeletonData *skeletonData, bool ownsSkeletonData) { - _skeleton = spSkeleton_create(skeletonData); - _ownsSkeletonData = ownsSkeletonData; -} - -SkeletonRenderer::SkeletonRenderer () - : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr) { -} - -SkeletonRenderer::SkeletonRenderer (spSkeletonData *skeletonData, bool ownsSkeletonData) - : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr) { - initWithData(skeletonData, ownsSkeletonData); -} - -SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, spAtlas* atlas, float scale) - : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr) { - initWithJsonFile(skeletonDataFile, atlas, scale); -} - -SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) - : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr) { - initWithJsonFile(skeletonDataFile, atlasFile, scale); -} - -SkeletonRenderer::~SkeletonRenderer () { - if (_ownsSkeletonData) spSkeletonData_dispose(_skeleton->data); - spSkeleton_dispose(_skeleton); - if (_atlas) spAtlas_dispose(_atlas); - if (_attachmentLoader) spAttachmentLoader_dispose(_attachmentLoader); - delete [] _worldVertices; - spSkeletonClipping_dispose(_clipper); -} - -void SkeletonRenderer::initWithData (spSkeletonData* skeletonData, bool ownsSkeletonData) { - setSkeletonData(skeletonData, ownsSkeletonData); - - initialize(); -} - -void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, spAtlas* atlas, float scale) { - _atlas = atlas; - _attachmentLoader = SUPER(Cocos2dAttachmentLoader_create(_atlas)); - - spSkeletonJson* json = spSkeletonJson_createWithLoader(_attachmentLoader); - json->scale = scale; - spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, skeletonDataFile.c_str()); - CCASSERT(skeletonData, json->error ? json->error : "Error reading skeleton data."); - spSkeletonJson_dispose(json); - - setSkeletonData(skeletonData, true); - - initialize(); -} - -void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { - _atlas = spAtlas_createFromFile(atlasFile.c_str(), 0); - CCASSERT(_atlas, "Error reading atlas file."); - - _attachmentLoader = SUPER(Cocos2dAttachmentLoader_create(_atlas)); - - spSkeletonJson* json = spSkeletonJson_createWithLoader(_attachmentLoader); - json->scale = scale; - spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, skeletonDataFile.c_str()); - CCASSERT(skeletonData, json->error ? json->error : "Error reading skeleton data file."); - spSkeletonJson_dispose(json); - - setSkeletonData(skeletonData, true); - - initialize(); -} - -void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, spAtlas* atlas, float scale) { - _atlas = atlas; - _attachmentLoader = SUPER(Cocos2dAttachmentLoader_create(_atlas)); - - spSkeletonBinary* binary = spSkeletonBinary_createWithLoader(_attachmentLoader); - binary->scale = scale; - spSkeletonData* skeletonData = spSkeletonBinary_readSkeletonDataFile(binary, skeletonDataFile.c_str()); - CCASSERT(skeletonData, binary->error ? binary->error : "Error reading skeleton data file."); - spSkeletonBinary_dispose(binary); - - setSkeletonData(skeletonData, true); - - initialize(); -} - -void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { - _atlas = spAtlas_createFromFile(atlasFile.c_str(), 0); - CCASSERT(_atlas, "Error reading atlas file."); - - _attachmentLoader = SUPER(Cocos2dAttachmentLoader_create(_atlas)); - - spSkeletonBinary* binary = spSkeletonBinary_createWithLoader(_attachmentLoader); - binary->scale = scale; - spSkeletonData* skeletonData = spSkeletonBinary_readSkeletonDataFile(binary, skeletonDataFile.c_str()); - CCASSERT(skeletonData, binary->error ? binary->error : "Error reading skeleton data file."); - spSkeletonBinary_dispose(binary); - - setSkeletonData(skeletonData, true); - - initialize(); -} - - -void SkeletonRenderer::update (float deltaTime) { - Node::update(deltaTime); - spSkeleton_update(_skeleton, deltaTime * _timeScale); -} - -void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t transformFlags) { - SkeletonBatch* batch = SkeletonBatch::getInstance(); - SkeletonTwoColorBatch* twoColorBatch = SkeletonTwoColorBatch::getInstance(); - bool isTwoColorTint = this->isTwoColorTint(); + static Cocos2dTextureLoader textureLoader; - if (_effect) _effect->begin(_effect, _skeleton); - - Color4F nodeColor; - nodeColor.r = getDisplayedColor().r / (float)255; - nodeColor.g = getDisplayedColor().g / (float)255; - nodeColor.b = getDisplayedColor().b / (float)255; - nodeColor.a = getDisplayedOpacity() / (float)255; - - Color4F color; - Color4F darkColor; - AttachmentVertices* attachmentVertices = nullptr; - TwoColorTrianglesCommand* lastTwoColorTrianglesCommand = nullptr; - for (int i = 0, n = _skeleton->slotsCount; i < n; ++i) { - spSlot* slot = _skeleton->drawOrder[i]; - if (!slot->attachment) { - spSkeletonClipping_clipEnd(_clipper, slot); - continue; + void SkeletonRenderer::destroyScratchBuffers() { + if (worldVertices) { + delete[] worldVertices; + worldVertices = nullptr; + worldVerticesLength = 0; + } + } + + SkeletonRenderer* SkeletonRenderer::createWithSkeleton(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData) { + SkeletonRenderer* node = new SkeletonRenderer(skeleton, ownsSkeleton, ownsSkeletonData); + node->autorelease(); + return node; + } + + SkeletonRenderer* SkeletonRenderer::createWithData (SkeletonData* skeletonData, bool ownsSkeletonData) { + SkeletonRenderer* node = new SkeletonRenderer(skeletonData, ownsSkeletonData); + node->autorelease(); + return node; + } + + SkeletonRenderer* SkeletonRenderer::createWithFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) { + SkeletonRenderer* node = new SkeletonRenderer(skeletonDataFile, atlas, scale); + node->autorelease(); + return node; + } + + SkeletonRenderer* SkeletonRenderer::createWithFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { + SkeletonRenderer* node = new SkeletonRenderer(skeletonDataFile, atlasFile, scale); + node->autorelease(); + return node; + } + + void SkeletonRenderer::initialize () { + if (!worldVertices) { + worldVertices = new float[INITIAL_WORLD_VERTICES_LENGTH]; + worldVerticesLength = INITIAL_WORLD_VERTICES_LENGTH; } - cocos2d::TrianglesCommand::Triangles triangles; - TwoColorTriangles trianglesTwoColor; + _clipper = new (__FILE__, __LINE__) SkeletonClipping(); - switch (slot->attachment->type) { - case SP_ATTACHMENT_REGION: { - spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment; - attachmentVertices = getAttachmentVertices(attachment); - - if (!isTwoColorTint) { - triangles.indices = attachmentVertices->_triangles->indices; - triangles.indexCount = attachmentVertices->_triangles->indexCount; - triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount); - triangles.vertCount = attachmentVertices->_triangles->vertCount; - memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount); - spRegionAttachment_computeWorldVertices(attachment, slot->bone, (float*)triangles.verts, 0, 6); - } else { - trianglesTwoColor.indices = attachmentVertices->_triangles->indices; - trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount; - trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount); - trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount; - for (int ii = 0; ii < trianglesTwoColor.vertCount; ii++) { - trianglesTwoColor.verts[ii].texCoords = attachmentVertices->_triangles->verts[ii].texCoords; - } - spRegionAttachment_computeWorldVertices(attachment, slot->bone, (float*)trianglesTwoColor.verts, 0, 7); - } - - color.r = attachment->color.r; - color.g = attachment->color.g; - color.b = attachment->color.b; - color.a = attachment->color.a; - - break; + _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED; + setOpacityModifyRGB(true); + + setupGLProgramState(false); + + _skeleton->setToSetupPose(); + _skeleton->updateWorldTransform(); + } + + void SkeletonRenderer::setupGLProgramState (bool twoColorTintEnabled) { + + _twoColorTintEnabled = twoColorTintEnabled; + if (twoColorTintEnabled) { + return; } - case SP_ATTACHMENT_MESH: { - spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment; - attachmentVertices = getAttachmentVertices(attachment); - - if (!isTwoColorTint) { - triangles.indices = attachmentVertices->_triangles->indices; - triangles.indexCount = attachmentVertices->_triangles->indexCount; - triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount); - triangles.vertCount = attachmentVertices->_triangles->vertCount; - memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount); - spVertexAttachment_computeWorldVertices(SUPER(attachment), slot, 0, triangles.vertCount * sizeof(cocos2d::V3F_C4B_T2F) / 4, (float*)triangles.verts, 0, 6); + + Texture2D *texture = nullptr; + for (int i = 0, n = _skeleton->getSlots().size(); i < n; i++) { + Slot* slot = _skeleton->getDrawOrder()[i]; + if (!slot->getAttachment()) continue; + if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) { + RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment(); + texture = static_cast(attachment->getRendererObject())->_texture; + } else if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) { + MeshAttachment* attachment = (MeshAttachment*)slot->getAttachment(); + texture = static_cast(attachment->getRendererObject())->_texture; } else { - trianglesTwoColor.indices = attachmentVertices->_triangles->indices; - trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount; - trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount); - trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount; - for (int ii = 0; ii < trianglesTwoColor.vertCount; ii++) { - trianglesTwoColor.verts[ii].texCoords = attachmentVertices->_triangles->verts[ii].texCoords; - } - spVertexAttachment_computeWorldVertices(SUPER(attachment), slot, 0, trianglesTwoColor.vertCount * sizeof(V3F_C4B_C4B_T2F) / 4, (float*)trianglesTwoColor.verts, 0, 7); + continue; } - color.r = attachment->color.r; - color.g = attachment->color.g; - color.b = attachment->color.b; - color.a = attachment->color.a; - - break; - } - case SP_ATTACHMENT_CLIPPING: { - spClippingAttachment* clip = (spClippingAttachment*)slot->attachment; - spSkeletonClipping_clipStart(_clipper, slot, clip); - } - default: - spSkeletonClipping_clipEnd(_clipper, slot); - continue; + if (texture != nullptr) { + break; + } } + //setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, texture)); + } + + void SkeletonRenderer::setSkeletonData (SkeletonData *skeletonData, bool ownsSkeletonData) { + _skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData); + _ownsSkeletonData = ownsSkeletonData; + } + + SkeletonRenderer::SkeletonRenderer () + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) { + } + + SkeletonRenderer::SkeletonRenderer(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas) + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) { + initWithSkeleton(skeleton, ownsSkeleton, ownsSkeletonData, ownsAtlas); + } + + SkeletonRenderer::SkeletonRenderer (SkeletonData *skeletonData, bool ownsSkeletonData) + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) { + initWithData(skeletonData, ownsSkeletonData); + } + + SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, Atlas* atlas, float scale) + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) { + initWithJsonFile(skeletonDataFile, atlas, scale); + } + + SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) { + initWithJsonFile(skeletonDataFile, atlasFile, scale); + } + + SkeletonRenderer::~SkeletonRenderer () { + if (_ownsSkeletonData) delete _skeleton->getData(); + if (_ownsSkeleton) delete _skeleton; + if (_ownsAtlas && _atlas) delete _atlas; + if (_attachmentLoader) delete _attachmentLoader; + delete _clipper; + } + + void SkeletonRenderer::initWithSkeleton(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas) { + _skeleton = skeleton; + _ownsSkeleton = ownsSkeleton; + _ownsSkeletonData = ownsSkeletonData; + _ownsAtlas = ownsAtlas; - if (slot->darkColor) { - darkColor.r = slot->darkColor->r * 255; - darkColor.g = slot->darkColor->g * 255; - darkColor.b = slot->darkColor->b * 255; - } else { - darkColor.r = 0; - darkColor.g = 0; - darkColor.b = 0; - } + initialize(); + } + + void SkeletonRenderer::initWithData (SkeletonData* skeletonData, bool ownsSkeletonData) { + _ownsSkeleton = true; + setSkeletonData(skeletonData, ownsSkeletonData); + initialize(); + } + + void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) { + _atlas = atlas; + _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); - color.a *= nodeColor.a * _skeleton->color.a * slot->color.a * 255; - // skip rendering if the color of this attachment is 0 - if (color.a == 0){ - spSkeletonClipping_clipEnd(_clipper, slot); - continue; - } - float multiplier = _premultipliedAlpha ? color.a : 255; - color.r *= nodeColor.r * _skeleton->color.r * slot->color.r * multiplier; - color.g *= nodeColor.g * _skeleton->color.g * slot->color.g * multiplier; - color.b *= nodeColor.b * _skeleton->color.b * slot->color.b * multiplier; + SkeletonJson* json = new (__FILE__, __LINE__) SkeletonJson(_attachmentLoader); + json->setScale(scale); + SkeletonData* skeletonData = json->readSkeletonDataFile(skeletonDataFile.c_str()); + CCASSERT(skeletonData, !json->getError().isEmpty() ? json->getError().buffer() : "Error reading skeleton data."); + delete json; - BlendFunc blendFunc; - switch (slot->data->blendMode) { - case SP_BLEND_MODE_ADDITIVE: - blendFunc.src = _premultipliedAlpha ? backend::BlendFactor::ONE : backend::BlendFactor::SRC_ALPHA; - blendFunc.dst = backend::BlendFactor::ONE; - break; - case SP_BLEND_MODE_MULTIPLY: - blendFunc.src = backend::BlendFactor::DST_COLOR; - blendFunc.dst = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; - break; - case SP_BLEND_MODE_SCREEN: - blendFunc.src = backend::BlendFactor::ONE; - blendFunc.dst = backend::BlendFactor::ONE_MINUS_SRC_COLOR; - break; - default: - blendFunc.src = _premultipliedAlpha ? backend::BlendFactor::ONE : backend::BlendFactor::SRC_ALPHA; - blendFunc.dst = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; + _ownsSkeleton = true; + setSkeletonData(skeletonData, true); + + initialize(); + } + + void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { + _atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader); + CCASSERT(_atlas, "Error reading atlas file."); + + _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); + + SkeletonJson* json = new (__FILE__, __LINE__) SkeletonJson(_attachmentLoader); + json->setScale(scale); + SkeletonData* skeletonData = json->readSkeletonDataFile(skeletonDataFile.c_str()); + CCASSERT(skeletonData, !json->getError().isEmpty() ? json->getError().buffer() : "Error reading skeleton data."); + delete json; + + _ownsSkeleton = true; + _ownsAtlas = true; + setSkeletonData(skeletonData, true); + + initialize(); + } + + void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) { + _atlas = atlas; + _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); + + SkeletonBinary* binary = new (__FILE__, __LINE__) SkeletonBinary(_attachmentLoader); + binary->setScale(scale); + SkeletonData* skeletonData = binary->readSkeletonDataFile(skeletonDataFile.c_str()); + CCASSERT(skeletonData, !binary->getError().isEmpty() ? binary->getError().buffer() : "Error reading skeleton data."); + delete binary; + _ownsSkeleton = true; + setSkeletonData(skeletonData, true); + + initialize(); + } + + void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { + _atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader); + CCASSERT(_atlas, "Error reading atlas file."); + + _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); + + SkeletonBinary* binary = new (__FILE__, __LINE__) SkeletonBinary(_attachmentLoader); + binary->setScale(scale); + SkeletonData* skeletonData = binary->readSkeletonDataFile(skeletonDataFile.c_str()); + CCASSERT(skeletonData, !binary->getError().isEmpty() ? binary->getError().buffer() : "Error reading skeleton data."); + delete binary; + _ownsSkeleton = true; + _ownsAtlas = true; + setSkeletonData(skeletonData, true); + + initialize(); + } + + void SkeletonRenderer::update (float deltaTime) { + Node::update(deltaTime); + if (_ownsSkeleton) _skeleton->update(deltaTime * _timeScale); + } + + void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t transformFlags) { + SkeletonBatch* batch = SkeletonBatch::getInstance(); + SkeletonTwoColorBatch* twoColorBatch = SkeletonTwoColorBatch::getInstance(); + bool isTwoColorTint = this->isTwoColorTint(); + + // Early exit if the skeleton is invisible + if (getDisplayedOpacity() == 0 || _skeleton->getColor().a == 0){ + return; } - if (!isTwoColorTint) { - if (spSkeletonClipping_isClipping(_clipper)) { - spSkeletonClipping_clipTriangles(_clipper, (float*)&triangles.verts[0].vertices, triangles.vertCount * sizeof(cocos2d::V3F_C4B_T2F) / 4, triangles.indices, triangles.indexCount, (float*)&triangles.verts[0].texCoords, 6); - batch->deallocateVertices(triangles.vertCount); + if (_effect) _effect->begin(*_skeleton); + + Color4F nodeColor; + nodeColor.r = getDisplayedColor().r / (float)255; + nodeColor.g = getDisplayedColor().g / (float)255; + nodeColor.b = getDisplayedColor().b / (float)255; + nodeColor.a = getDisplayedOpacity() / (float)255; + + Color4F color; + Color4F darkColor; + float darkPremultipliedAlpha = _premultipliedAlpha ? 255 : 0; + AttachmentVertices* attachmentVertices = nullptr; + TwoColorTrianglesCommand* lastTwoColorTrianglesCommand = nullptr; + bool inRange = _startSlotIndex != -1 || _endSlotIndex != -1 ? false : true; + for (int i = 0, n = _skeleton->getSlots().size(); i < n; ++i) { + Slot* slot = _skeleton->getDrawOrder()[i]; + + if (_startSlotIndex >= 0 && _startSlotIndex == slot->getData().getIndex()) { + inRange = true; + } + + if (!inRange) { + _clipper->clipEnd(*slot); + continue; + } + + if (_endSlotIndex >= 0 && _endSlotIndex == slot->getData().getIndex()) { + inRange = false; + } + + if (!slot->getAttachment()) { + _clipper->clipEnd(*slot); + continue; + } + + // Early exit if slot is invisible + if (slot->getColor().a == 0) { + _clipper->clipEnd(*slot); + continue; + } + + cocos2d::TrianglesCommand::Triangles triangles; + TwoColorTriangles trianglesTwoColor; + + if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) { + RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment(); + attachmentVertices = (AttachmentVertices*)attachment->getRendererObject(); - if (_clipper->clippedTriangles->size == 0){ - spSkeletonClipping_clipEnd(_clipper, slot); + // Early exit if attachment is invisible + if (attachment->getColor().a == 0) { + _clipper->clipEnd(*slot); continue; } - triangles.vertCount = _clipper->clippedVertices->size >> 1; - triangles.verts = batch->allocateVertices(triangles.vertCount); - triangles.indexCount = _clipper->clippedTriangles->size; - triangles.indices = batch->allocateIndices(triangles.indexCount); - memcpy(triangles.indices, _clipper->clippedTriangles->items, sizeof(unsigned short) * _clipper->clippedTriangles->size); - - cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags); - - float* verts = _clipper->clippedVertices->items; - float* uvs = _clipper->clippedUVs->items; - if (_effect) { - spColor light; - spColor dark; - light.r = color.r / 255.0f; - light.g = color.g / 255.0f; - light.b = color.b / 255.0f; - light.a = color.a / 255.0f; - dark.r = dark.g = dark.b = dark.a = 0; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2) { - V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; - spColor lightCopy = light; - spColor darkCopy = dark; - vertex->vertices.x = verts[vv]; - vertex->vertices.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - _effect->transform(_effect, &vertex->vertices.x, &vertex->vertices.y, &vertex->texCoords.u, &vertex->texCoords.v, &lightCopy, &darkCopy); - vertex->colors.r = (GLubyte)(lightCopy.r * 255); - vertex->colors.g = (GLubyte)(lightCopy.g * 255); - vertex->colors.b = (GLubyte)(lightCopy.b * 255); - vertex->colors.a = (GLubyte)(lightCopy.a * 255); - } + if (!isTwoColorTint) { + triangles.indices = attachmentVertices->_triangles->indices; + triangles.indexCount = attachmentVertices->_triangles->indexCount; + triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount); + triangles.vertCount = attachmentVertices->_triangles->vertCount; + memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount); + attachment->computeWorldVertices(slot->getBone(), (float*)triangles.verts, 0, 6); } else { - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2) { - V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; - vertex->vertices.x = verts[vv]; - vertex->vertices.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - vertex->colors.r = (GLubyte)color.r; - vertex->colors.g = (GLubyte)color.g; - vertex->colors.b = (GLubyte)color.b; - vertex->colors.a = (GLubyte)color.a; + trianglesTwoColor.indices = attachmentVertices->_triangles->indices; + trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount; + trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount); + trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount; + for (int i = 0; i < trianglesTwoColor.vertCount; i++) { + trianglesTwoColor.verts[i].texCoords = attachmentVertices->_triangles->verts[i].texCoords; } + attachment->computeWorldVertices(slot->getBone(), (float*)trianglesTwoColor.verts, 0, 7); } - } else { - cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags); - if (_effect) { - spColor light; - spColor dark; - light.r = color.r / 255.0f; - light.g = color.g / 255.0f; - light.b = color.b / 255.0f; - light.a = color.a / 255.0f; - dark.r = dark.g = dark.b = dark.a = 0; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) { - V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; - spColor lightCopy = light; - spColor darkCopy = dark; - _effect->transform(_effect, &vertex->vertices.x, &vertex->vertices.y, &vertex->texCoords.u, &vertex->texCoords.v, &lightCopy, &darkCopy); - vertex->colors.r = (GLubyte)(lightCopy.r * 255); - vertex->colors.g = (GLubyte)(lightCopy.g * 255); - vertex->colors.b = (GLubyte)(lightCopy.b * 255); - vertex->colors.a = (GLubyte)(lightCopy.a * 255); - } - } else { - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) { - V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; - vertex->colors.r = (GLubyte)color.r; - vertex->colors.g = (GLubyte)color.g; - vertex->colors.b = (GLubyte)color.b; - vertex->colors.a = (GLubyte)color.a; - } - } + color.r = attachment->getColor().r; + color.g = attachment->getColor().g; + color.b = attachment->getColor().b; + color.a = attachment->getColor().a; } - } else { - if (spSkeletonClipping_isClipping(_clipper)) { - spSkeletonClipping_clipTriangles(_clipper, (float*)&trianglesTwoColor.verts[0].position, trianglesTwoColor.vertCount * sizeof(V3F_C4B_C4B_T2F) / 4, trianglesTwoColor.indices, trianglesTwoColor.indexCount, (float*)&trianglesTwoColor.verts[0].texCoords, 7); - twoColorBatch->deallocateVertices(trianglesTwoColor.vertCount); + else if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) { + MeshAttachment* attachment = (MeshAttachment*)slot->getAttachment(); + attachmentVertices = (AttachmentVertices*)attachment->getRendererObject(); - if (_clipper->clippedTriangles->size == 0){ - spSkeletonClipping_clipEnd(_clipper, slot); + // Early exit if attachment is invisible + if (attachment->getColor().a == 0) { + _clipper->clipEnd(*slot); continue; } - trianglesTwoColor.vertCount = _clipper->clippedVertices->size >> 1; - trianglesTwoColor.verts = twoColorBatch->allocateVertices(trianglesTwoColor.vertCount); - trianglesTwoColor.indexCount = _clipper->clippedTriangles->size; - trianglesTwoColor.indices = twoColorBatch->allocateIndices(trianglesTwoColor.indexCount); - memcpy(trianglesTwoColor.indices, _clipper->clippedTriangles->items, sizeof(unsigned short) * _clipper->clippedTriangles->size); - - TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags); - - float* verts = _clipper->clippedVertices->items; - float* uvs = _clipper->clippedUVs->items; + if (!isTwoColorTint) { + triangles.indices = attachmentVertices->_triangles->indices; + triangles.indexCount = attachmentVertices->_triangles->indexCount; + triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount); + triangles.vertCount = attachmentVertices->_triangles->vertCount; + memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount); + attachment->computeWorldVertices(*slot, 0, attachment->getWorldVerticesLength(), (float*)triangles.verts, 0, 6); + } else { + trianglesTwoColor.indices = attachmentVertices->_triangles->indices; + trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount; + trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount); + trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount; + for (int i = 0; i < trianglesTwoColor.vertCount; i++) { + trianglesTwoColor.verts[i].texCoords = attachmentVertices->_triangles->verts[i].texCoords; + } + attachment->computeWorldVertices(*slot, 0, attachment->getWorldVerticesLength(), (float*)trianglesTwoColor.verts, 0, 7); + } - if (_effect) { - spColor light; - spColor dark; - light.r = color.r / 255.0f; - light.g = color.g / 255.0f; - light.b = color.b / 255.0f; - light.a = color.a / 255.0f; - dark.r = darkColor.r / 255.0f; - dark.g = darkColor.g / 255.0f; - dark.b = darkColor.b / 255.0f; - dark.a = darkColor.a / 255.0f; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2) { - V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; - spColor lightCopy = light; - spColor darkCopy = dark; - vertex->position.x = verts[vv]; - vertex->position.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - _effect->transform(_effect, &vertex->position.x, &vertex->position.y, &vertex->texCoords.u, &vertex->texCoords.v, &lightCopy, &darkCopy); - vertex->color.r = (GLubyte)(lightCopy.r * 255); - vertex->color.g = (GLubyte)(lightCopy.g * 255); - vertex->color.b = (GLubyte)(lightCopy.b * 255); - vertex->color.a = (GLubyte)(lightCopy.a * 255); - vertex->color2.r = (GLubyte)(darkCopy.r * 255); - vertex->color2.g = (GLubyte)(darkCopy.g * 255); - vertex->color2.b = (GLubyte)(darkCopy.b * 255); - vertex->color2.a = 1; + color.r = attachment->getColor().r; + color.g = attachment->getColor().g; + color.b = attachment->getColor().b; + color.a = attachment->getColor().a; + } + else if (slot->getAttachment()->getRTTI().isExactly(ClippingAttachment::rtti)) { + ClippingAttachment* clip = (ClippingAttachment*)slot->getAttachment(); + _clipper->clipStart(*slot, clip); + continue; + } else { + _clipper->clipEnd(*slot); + continue; + } + + float alpha = nodeColor.a * _skeleton->getColor().a * slot->getColor().a * color.a * 255; + // skip rendering if the color of this attachment is 0 + if (alpha == 0){ + _clipper->clipEnd(*slot); + continue; + } + float multiplier = _premultipliedAlpha ? alpha : 255; + float red = nodeColor.r * _skeleton->getColor().r * color.r * multiplier; + float green = nodeColor.g * _skeleton->getColor().g * color.g * multiplier; + float blue = nodeColor.b * _skeleton->getColor().b * color.b * multiplier; + + color.r = red * slot->getColor().r; + color.g = green * slot->getColor().g; + color.b = blue * slot->getColor().b; + color.a = alpha; + + if (slot->hasDarkColor()) { + darkColor.r = red * slot->getDarkColor().r; + darkColor.g = green * slot->getDarkColor().g; + darkColor.b = blue * slot->getDarkColor().b; + } else { + darkColor.r = 0; + darkColor.g = 0; + darkColor.b = 0; + } + darkColor.a = darkPremultipliedAlpha; + + BlendFunc blendFunc; + switch (slot->getData().getBlendMode()) { + case BlendMode_Additive: + blendFunc.src = _premultipliedAlpha ? backend::BlendFactor::ONE : backend::BlendFactor::SRC_ALPHA; + blendFunc.dst = backend::BlendFactor::ONE; + break; + case BlendMode_Multiply: + blendFunc.src = backend::BlendFactor::DST_COLOR; + blendFunc.dst = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; + break; + case BlendMode_Screen: + blendFunc.src = backend::BlendFactor::ONE; + blendFunc.dst = backend::BlendFactor::ONE_MINUS_SRC_COLOR; + break; + default: + blendFunc.src = _premultipliedAlpha ? backend::BlendFactor::ONE : backend::BlendFactor::SRC_ALPHA; + blendFunc.dst = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; + } + + if (!isTwoColorTint) { + if (_clipper->isClipping()) { + _clipper->clipTriangles((float*)&triangles.verts[0].vertices, triangles.indices, triangles.indexCount, (float*)&triangles.verts[0].texCoords, sizeof(cocos2d::V3F_C4B_T2F) / 4); + batch->deallocateVertices(triangles.vertCount); + + if (_clipper->getClippedTriangles().size() == 0){ + _clipper->clipEnd(*slot); + continue; + } + + triangles.vertCount = _clipper->getClippedVertices().size() >> 1; + triangles.verts = batch->allocateVertices(triangles.vertCount); + triangles.indexCount = _clipper->getClippedTriangles().size(); + triangles.indices = batch->allocateIndices(triangles.indexCount); + memcpy(triangles.indices, _clipper->getClippedTriangles().buffer(), sizeof(unsigned short) * _clipper->getClippedTriangles().size()); + + cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, blendFunc, triangles, transform, transformFlags); + + float* verts = _clipper->getClippedVertices().buffer(); + float* uvs = _clipper->getClippedUVs().buffer(); + if (_effect) { + Color light; + Color dark; + light.r = color.r / 255.0f; + light.g = color.g / 255.0f; + light.b = color.b / 255.0f; + light.a = color.a / 255.0f; + dark.r = dark.g = dark.b = dark.a = 0; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2) { + V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; + Color lightCopy = light; + Color darkCopy = dark; + vertex->vertices.x = verts[vv]; + vertex->vertices.y = verts[vv + 1]; + vertex->texCoords.u = uvs[vv]; + vertex->texCoords.v = uvs[vv + 1]; + _effect->transform(vertex->vertices.x, vertex->vertices.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkCopy); + vertex->colors.r = (GLubyte)(lightCopy.r * 255); + vertex->colors.g = (GLubyte)(lightCopy.g * 255); + vertex->colors.b = (GLubyte)(lightCopy.b * 255); + vertex->colors.a = (GLubyte)(lightCopy.a * 255); + } + } else { + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2) { + V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; + vertex->vertices.x = verts[vv]; + vertex->vertices.y = verts[vv + 1]; + vertex->texCoords.u = uvs[vv]; + vertex->texCoords.v = uvs[vv + 1]; + vertex->colors.r = (GLubyte)color.r; + vertex->colors.g = (GLubyte)color.g; + vertex->colors.b = (GLubyte)color.b; + vertex->colors.a = (GLubyte)color.a; + } } } else { - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2) { - V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; - vertex->position.x = verts[vv]; - vertex->position.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - vertex->color.r = (GLubyte)color.r; - vertex->color.g = (GLubyte)color.g; - vertex->color.b = (GLubyte)color.b; - vertex->color.a = (GLubyte)color.a; - vertex->color2.r = (GLubyte)darkColor.r; - vertex->color2.g = (GLubyte)darkColor.g; - vertex->color2.b = (GLubyte)darkColor.b; - vertex->color2.a = 1; + cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, blendFunc, triangles, transform, transformFlags); + + if (_effect) { + Color light; + Color dark; + light.r = color.r / 255.0f; + light.g = color.g / 255.0f; + light.b = color.b / 255.0f; + light.a = color.a / 255.0f; + dark.r = dark.g = dark.b = dark.a = 0; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) { + V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; + Color lightCopy = light; + Color darkCopy = dark; + _effect->transform(vertex->vertices.x, vertex->vertices.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkCopy); + vertex->colors.r = (GLubyte)(lightCopy.r * 255); + vertex->colors.g = (GLubyte)(lightCopy.g * 255); + vertex->colors.b = (GLubyte)(lightCopy.b * 255); + vertex->colors.a = (GLubyte)(lightCopy.a * 255); + } + } else { + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) { + V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; + vertex->colors.r = (GLubyte)color.r; + vertex->colors.g = (GLubyte)color.g; + vertex->colors.b = (GLubyte)color.b; + vertex->colors.a = (GLubyte)color.a; + } } } } else { - TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags); - - if (_effect) { - spColor light; - spColor dark; - light.r = color.r / 255.0f; - light.g = color.g / 255.0f; - light.b = color.b / 255.0f; - light.a = color.a / 255.0f; - dark.r = darkColor.r / 255.0f; - dark.g = darkColor.g / 255.0f; - dark.b = darkColor.b / 255.0f; - dark.a = darkColor.a / 255.0f; + if (_clipper->isClipping()) { + _clipper->clipTriangles((float*)&trianglesTwoColor.verts[0].position, trianglesTwoColor.indices, trianglesTwoColor.indexCount, (float*)&trianglesTwoColor.verts[0].texCoords, sizeof(V3F_C4B_C4B_T2F) / 4); + twoColorBatch->deallocateVertices(trianglesTwoColor.vertCount); + + if (_clipper->getClippedTriangles().size() == 0){ + _clipper->clipEnd(*slot); + continue; + } + + trianglesTwoColor.vertCount = _clipper->getClippedVertices().size() >> 1; + trianglesTwoColor.verts = twoColorBatch->allocateVertices(trianglesTwoColor.vertCount); + trianglesTwoColor.indexCount = _clipper->getClippedTriangles().size(); + trianglesTwoColor.indices = twoColorBatch->allocateIndices(trianglesTwoColor.indexCount); + memcpy(trianglesTwoColor.indices, _clipper->getClippedTriangles().buffer(), sizeof(unsigned short) * _clipper->getClippedTriangles().size()); + + TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, blendFunc, trianglesTwoColor, transform, transformFlags); + + float* verts = _clipper->getClippedVertices().buffer(); + float* uvs = _clipper->getClippedUVs().buffer(); - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) { - V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; - spColor lightCopy = light; - spColor darkCopy = dark; - _effect->transform(_effect, &vertex->position.x, &vertex->position.y, &vertex->texCoords.u, &vertex->texCoords.v, &lightCopy, &darkCopy); - vertex->color.r = (GLubyte)(lightCopy.r * 255); - vertex->color.g = (GLubyte)(lightCopy.g * 255); - vertex->color.b = (GLubyte)(lightCopy.b * 255); - vertex->color.a = (GLubyte)(lightCopy.a * 255); - vertex->color2.r = (GLubyte)(darkCopy.r * 255); - vertex->color2.g = (GLubyte)(darkCopy.g * 255); - vertex->color2.b = (GLubyte)(darkCopy.b * 255); - vertex->color2.a = 1; + if (_effect) { + Color light; + Color dark; + light.r = color.r / 255.0f; + light.g = color.g / 255.0f; + light.b = color.b / 255.0f; + light.a = color.a / 255.0f; + dark.r = darkColor.r / 255.0f; + dark.g = darkColor.g / 255.0f; + dark.b = darkColor.b / 255.0f; + dark.a = darkColor.a / 255.0f; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2) { + V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; + Color lightCopy = light; + Color darkCopy = dark; + vertex->position.x = verts[vv]; + vertex->position.y = verts[vv + 1]; + vertex->texCoords.u = uvs[vv]; + vertex->texCoords.v = uvs[vv + 1]; + _effect->transform(vertex->position.x, vertex->position.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkCopy); + vertex->color.r = (GLubyte)(lightCopy.r * 255); + vertex->color.g = (GLubyte)(lightCopy.g * 255); + vertex->color.b = (GLubyte)(lightCopy.b * 255); + vertex->color.a = (GLubyte)(lightCopy.a * 255); + vertex->color2.r = (GLubyte)(darkCopy.r * 255); + vertex->color2.g = (GLubyte)(darkCopy.g * 255); + vertex->color2.b = (GLubyte)(darkCopy.b * 255); + vertex->color2.a = (GLubyte)darkColor.a; + } + } else { + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2) { + V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; + vertex->position.x = verts[vv]; + vertex->position.y = verts[vv + 1]; + vertex->texCoords.u = uvs[vv]; + vertex->texCoords.v = uvs[vv + 1]; + vertex->color.r = (GLubyte)color.r; + vertex->color.g = (GLubyte)color.g; + vertex->color.b = (GLubyte)color.b; + vertex->color.a = (GLubyte)color.a; + vertex->color2.r = (GLubyte)darkColor.r; + vertex->color2.g = (GLubyte)darkColor.g; + vertex->color2.b = (GLubyte)darkColor.b; + vertex->color2.a = (GLubyte)darkColor.a; + } } } else { - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) { - V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; - vertex->color.r = (GLubyte)color.r; - vertex->color.g = (GLubyte)color.g; - vertex->color.b = (GLubyte)color.b; - vertex->color.a = (GLubyte)color.a; - vertex->color2.r = (GLubyte)darkColor.r; - vertex->color2.g = (GLubyte)darkColor.g; - vertex->color2.b = (GLubyte)darkColor.b; - vertex->color2.a = 1; + TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, blendFunc, trianglesTwoColor, transform, transformFlags); + + if (_effect) { + Color light; + Color dark; + light.r = color.r / 255.0f; + light.g = color.g / 255.0f; + light.b = color.b / 255.0f; + light.a = color.a / 255.0f; + dark.r = darkColor.r / 255.0f; + dark.g = darkColor.g / 255.0f; + dark.b = darkColor.b / 255.0f; + dark.a = darkColor.a / 255.0f; + + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) { + V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; + Color lightCopy = light; + Color darkCopy = dark; + _effect->transform(vertex->position.x, vertex->position.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkCopy); + vertex->color.r = (GLubyte)(lightCopy.r * 255); + vertex->color.g = (GLubyte)(lightCopy.g * 255); + vertex->color.b = (GLubyte)(lightCopy.b * 255); + vertex->color.a = (GLubyte)(lightCopy.a * 255); + vertex->color2.r = (GLubyte)(darkCopy.r * 255); + vertex->color2.g = (GLubyte)(darkCopy.g * 255); + vertex->color2.b = (GLubyte)(darkCopy.b * 255); + vertex->color2.a = (GLubyte)darkColor.a; + } + } else { + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) { + V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v; + vertex->color.r = (GLubyte)color.r; + vertex->color.g = (GLubyte)color.g; + vertex->color.b = (GLubyte)color.b; + vertex->color.a = (GLubyte)color.a; + vertex->color2.r = (GLubyte)darkColor.r; + vertex->color2.g = (GLubyte)darkColor.g; + vertex->color2.b = (GLubyte)darkColor.b; + vertex->color2.a = (GLubyte)darkColor.a; + } } } } + _clipper->clipEnd(*slot); } - spSkeletonClipping_clipEnd(_clipper, slot); - } - spSkeletonClipping_clipEnd2(_clipper); - - if (lastTwoColorTrianglesCommand) { - Node* parent = this->getParent(); + _clipper->clipEnd(); - // We need to decide if we can postpone flushing the current - // batch. We can postpone if the next sibling node is a - // two color tinted skeleton with the same global-z. - // The parent->getChildrenCount() > 100 check is a hack - // as checking for a sibling is an O(n) operation, and if - // all children of this nodes parent are skeletons, we - // are in O(n2) territory. - if (!parent || parent->getChildrenCount() > 100 || getChildrenCount() != 0) { - lastTwoColorTrianglesCommand->setForceFlush(true); - } else { - Vector& children = parent->getChildren(); - Node* sibling = nullptr; - for (ssize_t i = 0; i < children.size(); i++) { - if (children.at(i) == this) { - if (i < children.size() - 1) { - sibling = children.at(i+1); - break; - } - } - } - if (!sibling) { + if (lastTwoColorTrianglesCommand) { + Node* parent = this->getParent(); + + // We need to decide if we can postpone flushing the current + // batch. We can postpone if the next sibling node is a + // two color tinted skeleton with the same global-z. + // The parent->getChildrenCount() > 100 check is a hack + // as checking for a sibling is an O(n) operation, and if + // all children of this nodes parent are skeletons, we + // are in O(n2) territory. + if (!parent || parent->getChildrenCount() > 100 || getChildrenCount() != 0) { lastTwoColorTrianglesCommand->setForceFlush(true); } else { - SkeletonRenderer* siblingSkeleton = dynamic_cast(sibling); - if (!siblingSkeleton || // flush is next sibling isn't a SkeletonRenderer - !siblingSkeleton->isTwoColorTint() || // flush if next sibling isn't two color tinted - !siblingSkeleton->isVisible() || // flush if next sibling is two color tinted but not visible - (siblingSkeleton->getGlobalZOrder() != this->getGlobalZOrder())) { // flush if next sibling is two color tinted but z-order differs + cocos2d::Vector& children = parent->getChildren(); + Node* sibling = nullptr; + for (ssize_t i = 0; i < children.size(); i++) { + if (children.at(i) == this) { + if (i < children.size() - 1) { + sibling = children.at(i+1); + break; + } + } + } + if (!sibling) { lastTwoColorTrianglesCommand->setForceFlush(true); + } else { + SkeletonRenderer* siblingSkeleton = dynamic_cast(sibling); + if (!siblingSkeleton || // flush is next sibling isn't a SkeletonRenderer + !siblingSkeleton->isTwoColorTint() || // flush if next sibling isn't two color tinted + !siblingSkeleton->isVisible() || // flush if next sibling is two color tinted but not visible + (siblingSkeleton->getGlobalZOrder() != this->getGlobalZOrder())) { // flush if next sibling is two color tinted but z-order differs + lastTwoColorTrianglesCommand->setForceFlush(true); + } } } } + + if (_effect) _effect->end(); + + if (_debugSlots || _debugBones || _debugMeshes) { + drawDebug(renderer, transform, transformFlags); + } } - if (_effect) _effect->end(_effect); - - if (_debugSlots || _debugBones || _debugMeshes) { - drawDebug(renderer, transform, transformFlags); - } -} - -void SkeletonRenderer::drawDebug (Renderer* renderer, const Mat4 &transform, uint32_t transformFlags) { - - Director* director = Director::getInstance(); - director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); - director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform); - - DrawNode* drawNode = DrawNode::create(); - - if (_debugSlots) { - // Slots. - // DrawPrimitives::setDrawColor4B(0, 0, 255, 255); - glLineWidth(1); - Vec2 points[4]; - V3F_C4B_T2F_Quad quad; - for (int i = 0, n = _skeleton->slotsCount; i < n; i++) { - spSlot* slot = _skeleton->drawOrder[i]; - if (!slot->attachment || slot->attachment->type != SP_ATTACHMENT_REGION) continue; - spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment; - spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices, 0, 2); - points[0] = Vec2(_worldVertices[0], _worldVertices[1]); - points[1] = Vec2(_worldVertices[2], _worldVertices[3]); - points[2] = Vec2(_worldVertices[4], _worldVertices[5]); - points[3] = Vec2(_worldVertices[6], _worldVertices[7]); - drawNode->drawPoly(points, 4, true, Color4F::BLUE); - } - } - if (_debugBones) { - // Bone lengths. - glLineWidth(2); - for (int i = 0, n = _skeleton->bonesCount; i < n; i++) { - spBone *bone = _skeleton->bones[i]; - float x = bone->data->length * bone->a + bone->worldX; - float y = bone->data->length * bone->c + bone->worldY; - drawNode->drawLine(Vec2(bone->worldX, bone->worldY), Vec2(x, y), Color4F::RED); - } - // Bone origins. - auto color = Color4F::BLUE; // Root bone is blue. - for (int i = 0, n = _skeleton->bonesCount; i < n; i++) { - spBone *bone = _skeleton->bones[i]; - drawNode->drawPoint(Vec2(bone->worldX, bone->worldY), 4, color); - if (i == 0) color = Color4F::GREEN; - } - } - - if (_debugMeshes) { - // Meshes. - glLineWidth(1); - for (int i = 0, n = _skeleton->slotsCount; i < n; ++i) { - spSlot* slot = _skeleton->drawOrder[i]; - if (!slot->attachment || slot->attachment->type != SP_ATTACHMENT_MESH) continue; - spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment; - spVertexAttachment_computeWorldVertices(SUPER(attachment), slot, 0, attachment->super.worldVerticesLength, _worldVertices, 0, 2); - for (int ii = 0; ii < attachment->trianglesCount;) { - Vec2 v1(_worldVertices + (attachment->triangles[ii++] * 2)); - Vec2 v2(_worldVertices + (attachment->triangles[ii++] * 2)); - Vec2 v3(_worldVertices + (attachment->triangles[ii++] * 2)); - drawNode->drawLine(v1, v2, Color4F::YELLOW); - drawNode->drawLine(v2, v3, Color4F::YELLOW); - drawNode->drawLine(v3, v1, Color4F::YELLOW); + void SkeletonRenderer::drawDebug (Renderer* renderer, const Mat4 &transform, uint32_t transformFlags) { + + Director* director = Director::getInstance(); + director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); + director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform); + + DrawNode* drawNode = DrawNode::create(); + + if (_debugSlots) { + // Slots. + // DrawPrimitives::setDrawColor4B(0, 0, 255, 255); + drawNode->setLineWidth(1.0f); + Vec2 points[4]; + V3F_C4B_T2F_Quad quad; + for (int i = 0, n = _skeleton->getSlots().size(); i < n; i++) { + Slot* slot = _skeleton->getDrawOrder()[i]; + if (!slot->getAttachment() || !slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) continue; + RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment(); + attachment->computeWorldVertices(slot->getBone(), worldVertices, 0, 2); + points[0] = Vec2(worldVertices[0], worldVertices[1]); + points[1] = Vec2(worldVertices[2], worldVertices[3]); + points[2] = Vec2(worldVertices[4], worldVertices[5]); + points[3] = Vec2(worldVertices[6], worldVertices[7]); + drawNode->drawPoly(points, 4, true, Color4F::BLUE); + } + } + if (_debugBones) { + // Bone lengths. + drawNode->setLineWidth(2.0f); + for (int i = 0, n = _skeleton->getBones().size(); i < n; i++) { + Bone *bone = _skeleton->getBones()[i]; + float x = bone->getData().getLength() * bone->getA() + bone->getWorldX(); + float y = bone->getData().getLength() * bone->getC() + bone->getWorldY(); + drawNode->drawLine(Vec2(bone->getWorldX(), bone->getWorldY()), Vec2(x, y), Color4F::RED); + } + // Bone origins. + auto color = Color4F::BLUE; // Root bone is blue. + for (int i = 0, n = _skeleton->getBones().size(); i < n; i++) { + Bone *bone = _skeleton->getBones()[i]; + drawNode->drawPoint(Vec2(bone->getWorldX(), bone->getWorldY()), 4, color); + if (i == 0) color = Color4F::GREEN; } } + if (_debugMeshes) { + // Meshes. + drawNode->setLineWidth(1.0f); + for (int i = 0, n = _skeleton->getSlots().size(); i < n; ++i) { + Slot* slot = _skeleton->getDrawOrder()[i]; + if (!slot->getAttachment() || !slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) continue; + MeshAttachment* attachment = (MeshAttachment*)slot->getAttachment(); + ensureWorldVerticesCapacity(attachment->getWorldVerticesLength()); + attachment->computeWorldVertices(*slot, 0, attachment->getWorldVerticesLength(), worldVertices, 0, 2); + for (int ii = 0; ii < attachment->getTriangles().size();) { + Vec2 v1(worldVertices + (attachment->getTriangles()[ii++] * 2)); + Vec2 v2(worldVertices + (attachment->getTriangles()[ii++] * 2)); + Vec2 v3(worldVertices + (attachment->getTriangles()[ii++] * 2)); + drawNode->drawLine(v1, v2, Color4F::YELLOW); + drawNode->drawLine(v2, v3, Color4F::YELLOW); + drawNode->drawLine(v3, v1, Color4F::YELLOW); + } + } + + } + + drawNode->draw(renderer, transform, transformFlags); + director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); } - - drawNode->draw(renderer, transform, transformFlags); - director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); -} - -AttachmentVertices* SkeletonRenderer::getAttachmentVertices (spRegionAttachment* attachment) const { - return (AttachmentVertices*)attachment->rendererObject; -} - -AttachmentVertices* SkeletonRenderer::getAttachmentVertices (spMeshAttachment* attachment) const { - return (AttachmentVertices*)attachment->rendererObject; -} - -Rect SkeletonRenderer::getBoundingBox () const { - float minX = FLT_MAX, minY = FLT_MAX, maxX = -FLT_MAX, maxY = -FLT_MAX; - float scaleX = getScaleX(), scaleY = getScaleY(); - for (int i = 0; i < _skeleton->slotsCount; ++i) { - spSlot* slot = _skeleton->slots[i]; - if (!slot->attachment) continue; - int verticesCount; - if (slot->attachment->type == SP_ATTACHMENT_REGION) { - spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment; - spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices, 0, 2); - verticesCount = 8; - } else if (slot->attachment->type == SP_ATTACHMENT_MESH) { - spMeshAttachment* mesh = (spMeshAttachment*)slot->attachment; - spVertexAttachment_computeWorldVertices(SUPER(mesh), slot, 0, mesh->super.worldVerticesLength, _worldVertices, 0, 2); - verticesCount = mesh->super.worldVerticesLength; - } else - continue; - for (int ii = 0; ii < verticesCount; ii += 2) { - float x = _worldVertices[ii] * scaleX, y = _worldVertices[ii + 1] * scaleY; - minX = min(minX, x); - minY = min(minY, y); - maxX = max(maxX, x); - maxY = max(maxY, y); + + Rect SkeletonRenderer::getBoundingBox () const { + float minX = FLT_MAX, minY = FLT_MAX, maxX = -FLT_MAX, maxY = -FLT_MAX; + float scaleX = getScaleX(), scaleY = getScaleY(); + for (int i = 0; i < _skeleton->getSlots().size(); ++i) { + Slot* slot = _skeleton->getSlots()[i]; + if (!slot->getAttachment()) continue; + int verticesCount; + if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) { + RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment(); + attachment->computeWorldVertices(slot->getBone(), worldVertices, 0, 2); + verticesCount = 8; + } else if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) { + MeshAttachment* mesh = (MeshAttachment*)slot->getAttachment(); + ensureWorldVerticesCapacity(mesh->getWorldVerticesLength()); + mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), worldVertices, 0, 2); + verticesCount = mesh->getWorldVerticesLength(); + } else + continue; + for (int ii = 0; ii < verticesCount; ii += 2) { + float x = worldVertices[ii] * scaleX, y = worldVertices[ii + 1] * scaleY; + minX = min(minX, x); + minY = min(minY, y); + maxX = max(maxX, x); + maxY = max(maxY, y); + } } + Vec2 position = getPosition(); + if (minX == FLT_MAX) minX = minY = maxX = maxY = 0; + return Rect(position.x + minX, position.y + minY, maxX - minX, maxY - minY); } - Vec2 position = getPosition(); - if (minX == FLT_MAX) minX = minY = maxX = maxY = 0; - return Rect(position.x + minX, position.y + minY, maxX - minX, maxY - minY); -} - -// --- Convenience methods for Skeleton_* functions. - -void SkeletonRenderer::updateWorldTransform () { - spSkeleton_updateWorldTransform(_skeleton); -} - -void SkeletonRenderer::setToSetupPose () { - spSkeleton_setToSetupPose(_skeleton); -} -void SkeletonRenderer::setBonesToSetupPose () { - spSkeleton_setBonesToSetupPose(_skeleton); -} -void SkeletonRenderer::setSlotsToSetupPose () { - spSkeleton_setSlotsToSetupPose(_skeleton); -} - -spBone* SkeletonRenderer::findBone (const std::string& boneName) const { - return spSkeleton_findBone(_skeleton, boneName.c_str()); -} - -spSlot* SkeletonRenderer::findSlot (const std::string& slotName) const { - return spSkeleton_findSlot(_skeleton, slotName.c_str()); -} - -bool SkeletonRenderer::setSkin (const std::string& skinName) { - return spSkeleton_setSkinByName(_skeleton, skinName.empty() ? 0 : skinName.c_str()) ? true : false; -} -bool SkeletonRenderer::setSkin (const char* skinName) { - return spSkeleton_setSkinByName(_skeleton, skinName) ? true : false; -} - -spAttachment* SkeletonRenderer::getAttachment (const std::string& slotName, const std::string& attachmentName) const { - return spSkeleton_getAttachmentForSlotName(_skeleton, slotName.c_str(), attachmentName.c_str()); -} -bool SkeletonRenderer::setAttachment (const std::string& slotName, const std::string& attachmentName) { - return spSkeleton_setAttachment(_skeleton, slotName.c_str(), attachmentName.empty() ? 0 : attachmentName.c_str()) ? true : false; -} -bool SkeletonRenderer::setAttachment (const std::string& slotName, const char* attachmentName) { - return spSkeleton_setAttachment(_skeleton, slotName.c_str(), attachmentName) ? true : false; -} -void SkeletonRenderer::setTwoColorTint(bool enabled) { - if (enabled) - setGLProgramState(SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState()); - else - setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP)); -} - -bool SkeletonRenderer::isTwoColorTint() { - return getGLProgramState() == SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState(); -} + // --- Convenience methods for Skeleton_* functions. -void SkeletonRenderer::setVertexEffect(spVertexEffect *effect) { - this->_effect = effect; -} - -spSkeleton* SkeletonRenderer::getSkeleton () { - return _skeleton; -} - -void SkeletonRenderer::setTimeScale (float scale) { - _timeScale = scale; -} -float SkeletonRenderer::getTimeScale () const { - return _timeScale; -} - -void SkeletonRenderer::setDebugSlotsEnabled (bool enabled) { - _debugSlots = enabled; -} -bool SkeletonRenderer::getDebugSlotsEnabled () const { - return _debugSlots; -} - -void SkeletonRenderer::setDebugBonesEnabled (bool enabled) { - _debugBones = enabled; -} -bool SkeletonRenderer::getDebugBonesEnabled () const { - return _debugBones; -} + void SkeletonRenderer::updateWorldTransform () { + _skeleton->updateWorldTransform(); + } -void SkeletonRenderer::setDebugMeshesEnabled (bool enabled) { - _debugMeshes = enabled; -} -bool SkeletonRenderer::getDebugMeshesEnabled () const { - return _debugMeshes; -} - -void SkeletonRenderer::onEnter () { + void SkeletonRenderer::setToSetupPose () { + _skeleton->setToSetupPose(); + } + void SkeletonRenderer::setBonesToSetupPose () { + _skeleton->setBonesToSetupPose(); + } + void SkeletonRenderer::setSlotsToSetupPose () { + _skeleton->setSlotsToSetupPose(); + } + + Bone* SkeletonRenderer::findBone (const std::string& boneName) const { + return _skeleton->findBone(boneName.c_str()); + } + + Slot* SkeletonRenderer::findSlot (const std::string& slotName) const { + return _skeleton->findSlot(slotName.c_str()); + } + + void SkeletonRenderer::setSkin (const std::string& skinName) { + _skeleton->setSkin(skinName.empty() ? 0 : skinName.c_str()); + } + void SkeletonRenderer::setSkin (const char* skinName) { + _skeleton->setSkin(skinName); + } + + Attachment* SkeletonRenderer::getAttachment (const std::string& slotName, const std::string& attachmentName) const { + return _skeleton->getAttachment(slotName.c_str(), attachmentName.c_str()); + } + bool SkeletonRenderer::setAttachment (const std::string& slotName, const std::string& attachmentName) { + return _skeleton->getAttachment(slotName.c_str(), attachmentName.empty() ? 0 : attachmentName.c_str()) ? true : false; + } + bool SkeletonRenderer::setAttachment (const std::string& slotName, const char* attachmentName) { + return _skeleton->getAttachment(slotName.c_str(), attachmentName) ? true : false; + } + + void SkeletonRenderer::setTwoColorTint(bool enabled) { + setupGLProgramState(enabled); + } + + bool SkeletonRenderer::isTwoColorTint() { + return _twoColorTintEnabled; + } + + void SkeletonRenderer::setVertexEffect(VertexEffect *effect) { + this->_effect = effect; + } + + void SkeletonRenderer::setSlotsRange(int startSlotIndex, int endSlotIndex) { + this->_startSlotIndex = startSlotIndex; + this->_endSlotIndex = endSlotIndex; + } + + Skeleton* SkeletonRenderer::getSkeleton () { + return _skeleton; + } + + void SkeletonRenderer::setTimeScale (float scale) { + _timeScale = scale; + } + float SkeletonRenderer::getTimeScale () const { + return _timeScale; + } + + void SkeletonRenderer::setDebugSlotsEnabled (bool enabled) { + _debugSlots = enabled; + } + bool SkeletonRenderer::getDebugSlotsEnabled () const { + return _debugSlots; + } + + void SkeletonRenderer::setDebugBonesEnabled (bool enabled) { + _debugBones = enabled; + } + bool SkeletonRenderer::getDebugBonesEnabled () const { + return _debugBones; + } + + void SkeletonRenderer::setDebugMeshesEnabled (bool enabled) { + _debugMeshes = enabled; + } + bool SkeletonRenderer::getDebugMeshesEnabled () const { + return _debugMeshes; + } + + void SkeletonRenderer::onEnter () { #if CC_ENABLE_SCRIPT_BINDING - if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnter)) return; + if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnter)) return; #endif - Node::onEnter(); - scheduleUpdate(); -} - -void SkeletonRenderer::onExit () { + Node::onEnter(); + scheduleUpdate(); + } + + void SkeletonRenderer::onExit () { #if CC_ENABLE_SCRIPT_BINDING - if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit)) return; + if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit)) return; #endif - Node::onExit(); - unscheduleUpdate(); -} - -// --- CCBlendProtocol - -const BlendFunc& SkeletonRenderer::getBlendFunc () const { - return _blendFunc; -} - -void SkeletonRenderer::setBlendFunc (const BlendFunc &blendFunc) { - _blendFunc = blendFunc; -} - -void SkeletonRenderer::setOpacityModifyRGB (bool value) { - _premultipliedAlpha = value; -} - -bool SkeletonRenderer::isOpacityModifyRGB () const { - return _premultipliedAlpha; -} - + Node::onExit(); + unscheduleUpdate(); + } + + // --- CCBlendProtocol + + const BlendFunc& SkeletonRenderer::getBlendFunc () const { + return _blendFunc; + } + + void SkeletonRenderer::setBlendFunc (const BlendFunc &blendFunc) { + _blendFunc = blendFunc; + } + + void SkeletonRenderer::setOpacityModifyRGB (bool value) { + _premultipliedAlpha = value; + } + + bool SkeletonRenderer::isOpacityModifyRGB () const { + return _premultipliedAlpha; + } + } diff --git a/cocos/editor-support/spine/SkeletonRenderer.h b/cocos/editor-support/spine/SkeletonRenderer.h index adf34bd70bfe..5eefbe5bc395 100644 --- a/cocos/editor-support/spine/SkeletonRenderer.h +++ b/cocos/editor-support/spine/SkeletonRenderer.h @@ -1,31 +1,30 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef SPINE_SKELETONRENDERER_H_ @@ -35,117 +34,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* createWithData (spSkeletonData* skeletonData, bool ownsSkeletonData = false); - static SkeletonRenderer* createWithFile (const std::string& skeletonDataFile, spAtlas* 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; - - spSkeleton* 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. */ - spBone* findBone (const std::string& boneName) const; - /* Returns 0 if the slot was not found. */ - spSlot* 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. Returns false if the skin was not found. - * @param skin May be empty string ("") for no skin.*/ - bool setSkin (const std::string& skinName); - /** @param skin May be 0 for no skin.*/ - bool 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::BlendFunc _blendFunc; + bool _premultipliedAlpha; + Skeleton* _skeleton; + float _timeScale; + bool _debugSlots; + bool _debugBones; + bool _debugMeshes; + SkeletonClipping* _clipper; + VertexEffect* _effect; + bool _twoColorTintEnabled = false; + int _startSlotIndex; + int _endSlotIndex; + }; - /* Returns 0 if the slot or attachment was not found. */ - spAttachment* 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(spVertexEffect* effect); - - // --- 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; - -CC_CONSTRUCTOR_ACCESS: - SkeletonRenderer (); - SkeletonRenderer (spSkeletonData* skeletonData, bool ownsSkeletonData = false); - SkeletonRenderer (const std::string& skeletonDataFile, spAtlas* atlas, float scale = 1); - SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); - - virtual ~SkeletonRenderer (); - - void initWithData (spSkeletonData* skeletonData, bool ownsSkeletonData = false); - void initWithJsonFile (const std::string& skeletonDataFile, spAtlas* atlas, float scale = 1); - void initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); - void initWithBinaryFile (const std::string& skeletonDataFile, spAtlas* atlas, float scale = 1); - void initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); - - virtual void initialize (); - -protected: - void setSkeletonData (spSkeletonData* skeletonData, bool ownsSkeletonData); - virtual AttachmentVertices* getAttachmentVertices (spRegionAttachment* attachment) const; - virtual AttachmentVertices* getAttachmentVertices (spMeshAttachment* attachment) const; - - bool _ownsSkeletonData; - spAtlas* _atlas; - spAttachmentLoader* _attachmentLoader; - cocos2d::CustomCommand _debugCommand; - cocos2d::BlendFunc _blendFunc; - float* _worldVertices; - bool _premultipliedAlpha; - spSkeleton* _skeleton; - float _timeScale; - bool _debugSlots; - bool _debugBones; - bool _debugMeshes; - spSkeletonClipping* _clipper; - spVertexEffect* _effect; -}; - } #endif /* SPINE_SKELETONRENDERER_H_ */ diff --git a/cocos/editor-support/spine/SkeletonTwoColorBatch.cpp b/cocos/editor-support/spine/SkeletonTwoColorBatch.cpp index 32a11d3591d8..44fd15363b20 100644 --- a/cocos/editor-support/spine/SkeletonTwoColorBatch.cpp +++ b/cocos/editor-support/spine/SkeletonTwoColorBatch.cpp @@ -1,39 +1,41 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include -#include +#include #include #include // offsetof #include "base/ccTypes.h" #include "base/ccUtils.h" +#include "xxhash.h" +#include "renderer/ccShaders.h" + USING_NS_CC; #define EVENT_AFTER_DRAW_RESET_POSITION "director_after_draw" using std::max; @@ -43,116 +45,185 @@ using std::max; #define STRINGIFY(A) #A +namespace { + + const char* TWO_COLOR_TINT_VERTEX_SHADER = STRINGIFY( + uniform mat4 u_PMatrix; + attribute vec4 a_position; + attribute vec4 a_color; + attribute vec4 a_color2; + attribute vec2 a_texCoords; + + \n#ifdef GL_ES\n + varying lowp vec4 v_light; + varying lowp vec4 v_dark; + varying mediump vec2 v_texCoord; + \n#else\n + varying vec4 v_light; + varying vec4 v_dark; + varying vec2 v_texCoord; + + \n#endif\n + + void main() { + v_light = a_color; + v_dark = a_color2; + v_texCoord = a_texCoords; + gl_Position = u_PMatrix * a_position; + } + ); + + const char* TWO_COLOR_TINT_FRAGMENT_SHADER = STRINGIFY( + \n#ifdef GL_ES\n + precision lowp float; + \n#endif\n + uniform sampler2D u_texture; + varying vec4 v_light; + varying vec4 v_dark; + varying vec2 v_texCoord; + + void main() { + vec4 texColor = texture2D(u_texture, v_texCoord); + float alpha = texColor.a * v_light.a; + gl_FragColor.a = alpha; + gl_FragColor.rgb = ((texColor.a - 1.0) * v_dark.a + 1.0 - texColor.rgb) * v_dark.rgb + texColor.rgb * v_light.rgb; + } + ); + + + std::shared_ptr __twoColorProgramState = nullptr; + backend::UniformLocation __locPMatrix; + backend::UniformLocation __locTexture; + + void initTwoColorProgramState() + { + if (__twoColorProgramState) + { + return; + } + auto *programState = new backend::ProgramState(TWO_COLOR_TINT_VERTEX_SHADER, TWO_COLOR_TINT_FRAGMENT_SHADER); + + __locPMatrix = programState->getUniformLocation("u_PMatrix"); + __locTexture = programState->getUniformLocation("u_texture"); + + auto layout = programState->getVertexLayout(); + + layout->setAttribute("a_position", 0, backend::VertexFormat::FLOAT3, offsetof(spine::V3F_C4B_C4B_T2F, position), false); + layout->setAttribute("a_color", 1, backend::VertexFormat::UBYTE4, offsetof(spine::V3F_C4B_C4B_T2F, color), true); + layout->setAttribute("a_color2", 2, backend::VertexFormat::UBYTE4, offsetof(spine::V3F_C4B_C4B_T2F, color2), true); + layout->setAttribute("a_texCoords", 3, backend::VertexFormat::FLOAT2, offsetof(spine::V3F_C4B_C4B_T2F, texCoords), false); + layout->setLayout(sizeof(spine::V3F_C4B_C4B_T2F)); + + __twoColorProgramState = std::shared_ptr(programState); + } + +} + namespace spine { -TwoColorTrianglesCommand::TwoColorTrianglesCommand() :_materialID(0), _textureID(0), _glProgramState(nullptr), _glProgram(nullptr), _blendType(BlendFunc::DISABLE), _alphaTextureID(0) { +TwoColorTrianglesCommand::TwoColorTrianglesCommand() :_materialID(0), _texture(nullptr), _blendType(BlendFunc::DISABLE), _alphaTextureID(0) { _type = RenderCommand::Type::CUSTOM_COMMAND; - func = [this]() { draw(); }; } -void TwoColorTrianglesCommand::init(float globalOrder, GLuint textureID, GLProgramState* glProgramState, BlendFunc blendType, const TwoColorTriangles& triangles, const Mat4& mv, uint32_t flags) { - CCASSERT(glProgramState, "Invalid GLProgramState"); - CCASSERT(glProgramState->getVertexAttribsFlags() == 0, "No custom attributes are supported in QuadCommand"); +void TwoColorTrianglesCommand::init(float globalOrder, cocos2d::Texture2D *texture, BlendFunc blendType, const TwoColorTriangles& triangles, const Mat4& mv, uint32_t flags) { - RenderCommand::init(globalOrder, mv, flags); + updateCommandPipelineDescriptor(); + const cocos2d::Mat4& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); + + auto finalMatrix = projectionMat * mv; - _triangles = triangles; - if(_triangles.indexCount % 3 != 0) { - int count = _triangles.indexCount; - _triangles.indexCount = count / 3 * 3; - CCLOGERROR("Resize indexCount from %d to %d, size must be multiple times of 3", count, _triangles.indexCount); - } - _mv = mv; + _programState->setUniform(_locPMatrix, finalMatrix.m, sizeof(finalMatrix.m)); + _programState->setTexture(_locTexture, 0, texture->getBackendTexture()); + + + RenderCommand::init(globalOrder, mv, flags); - if( _textureID != textureID || _blendType.src != blendType.src || _blendType.dst != blendType.dst || - _glProgramState != glProgramState || - _glProgram != glProgramState->getGLProgram()) { - _textureID = textureID; + _triangles = triangles; + if (_triangles.indexCount % 3 != 0) { + int count = _triangles.indexCount; + _triangles.indexCount = count / 3 * 3; + CCLOGERROR("Resize indexCount from %d to %d, size must be multiple times of 3", count, _triangles.indexCount); + } + + _mv = mv; + + if (_blendType.src != blendType.src || _blendType.dst != blendType.dst || + _texture != texture->getBackendTexture() || _pipelineDescriptor.programState != _programState) + { + _texture = texture->getBackendTexture(); _blendType = blendType; - _glProgramState = glProgramState; - _glProgram = glProgramState->getGLProgram(); + + _prog = _programState->getProgram(); + + auto& blendDescriptor = _pipelineDescriptor.blendDescriptor; + blendDescriptor.blendEnabled = true; + blendDescriptor.sourceRGBBlendFactor = blendDescriptor.sourceAlphaBlendFactor = blendType.src; + blendDescriptor.destinationRGBBlendFactor = blendDescriptor.destinationAlphaBlendFactor = blendType.dst; generateMaterialID(); } } -TwoColorTrianglesCommand::~TwoColorTrianglesCommand() { + + +void TwoColorTrianglesCommand::updateCommandPipelineDescriptor() +{ + if (!__twoColorProgramState) + { + initTwoColorProgramState(); + } + + CC_SAFE_RELEASE_NULL(_programState); + _programState = __twoColorProgramState->clone(); + _locPMatrix = __locPMatrix; + _locTexture = __locTexture; + _pipelineDescriptor.programState = _programState; +} + +TwoColorTrianglesCommand::~TwoColorTrianglesCommand() +{ + CC_SAFE_RELEASE_NULL(_programState); } void TwoColorTrianglesCommand::generateMaterialID() { // do not batch if using custom uniforms (since we cannot batch) it - if(_glProgramState->getUniformCount() > 0) { - _materialID = Renderer::MATERIAL_ID_DO_NOT_BATCH; - setSkipBatching(true); - } - else { - int glProgram = (int)_glProgram->getProgram(); - _materialID = glProgram + (int)_textureID + (int)_blendType.src + (int)_blendType.dst; - } -} -void TwoColorTrianglesCommand::useMaterial() const { - //Set texture - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, _textureID); - - if (_alphaTextureID > 0) + + struct { - // ANDROID ETC1 ALPHA supports. - glActiveTexture(GL_TEXTURE0 + 1); - glBindTexture(GL_TEXTURE_2D, _alphaTextureID); - } - //set blend mode - cocos2d::utils::setBlending(_blendType.src, _blendType.dst); - - _glProgramState->apply(_mv); -} - -void TwoColorTrianglesCommand::draw() { - SkeletonTwoColorBatch::getInstance()->batch(this); + void* texture; + void* prog; + backend::BlendFactor src; + backend::BlendFactor dst; + }hashMe; + + // NOTE: Initialize hashMe struct to make the value of padding bytes be filled with zero. + // It's important since XXH32 below will also consider the padding bytes which probably + // are set to random values by different compilers. + memset(&hashMe, 0, sizeof(hashMe)); + + hashMe.texture = _texture; + hashMe.src = _blendType.src; + hashMe.dst = _blendType.dst; + hashMe.prog = _prog; + _materialID = XXH32((const void*)&hashMe, sizeof(hashMe), 0); } -const char* TWO_COLOR_TINT_VERTEX_SHADER = STRINGIFY( -attribute vec4 a_position; -attribute vec4 a_color; -attribute vec4 a_color2; -attribute vec2 a_texCoords; - -\n#ifdef GL_ES\n -varying lowp vec4 v_light; -varying lowp vec4 v_dark; -varying mediump vec2 v_texCoord; -\n#else\n -varying vec4 v_light; -varying vec4 v_dark; -varying vec2 v_texCoord; - -\n#endif\n - -void main() { - v_light = a_color; - v_dark = a_color2; - v_texCoord = a_texCoords; - gl_Position = CC_PMatrix * a_position; + +void TwoColorTrianglesCommand::draw(Renderer *r) { + SkeletonTwoColorBatch::getInstance()->batch(r, this); } -); - -const char* TWO_COLOR_TINT_FRAGMENT_SHADER = STRINGIFY( -\n#ifdef GL_ES\n -precision lowp float; -\n#endif\n - -varying vec4 v_light; -varying vec4 v_dark; -varying vec2 v_texCoord; - -void main() { - vec4 texColor = texture2D(CC_Texture0, v_texCoord); - float alpha = texColor.a * v_light.a; - gl_FragColor.a = alpha; - gl_FragColor.rgb = (1.0 - texColor.rgb) * v_dark.rgb * alpha + texColor.rgb * v_light.rgb; + +void TwoColorTrianglesCommand::updateVertexAndIndexBuffer(Renderer *r, V3F_C4B_C4B_T2F *vertices, int verticesSize, uint16_t *indices, int indicesSize) +{ + if(verticesSize != _vertexCapacity) + createVertexBuffer(sizeof(V3F_C4B_C4B_T2F), verticesSize, CustomCommand::BufferUsage::DYNAMIC); + if(indicesSize != _indexCapacity) + createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, indicesSize, CustomCommand::BufferUsage::DYNAMIC); + + updateVertexBuffer(vertices, sizeof(V3F_C4B_C4B_T2F) * verticesSize); + updateIndexBuffer(indices, sizeof(uint16_t) * indicesSize); } -); static SkeletonTwoColorBatch* instance = nullptr; @@ -169,47 +240,32 @@ void SkeletonTwoColorBatch::destroyInstance () { } } -SkeletonTwoColorBatch::SkeletonTwoColorBatch () { +SkeletonTwoColorBatch::SkeletonTwoColorBatch () : _vertexBuffer(0), _indexBuffer(0) { + _commandsPool.reserve(INITIAL_SIZE); for (unsigned int i = 0; i < INITIAL_SIZE; i++) { _commandsPool.push_back(new TwoColorTrianglesCommand()); } - _indices = spUnsignedShortArray_create(8); - reset (); // callback after drawing is finished so we can clear out the batch state // for the next frame Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_AFTER_DRAW_RESET_POSITION, [this](EventCustom* eventCustom){ this->update(0); - });; - - _twoColorTintShader = cocos2d::GLProgram::createWithByteArrays(TWO_COLOR_TINT_VERTEX_SHADER, TWO_COLOR_TINT_FRAGMENT_SHADER); - _twoColorTintShaderState = GLProgramState::getOrCreateWithGLProgram(_twoColorTintShader); - _twoColorTintShaderState->retain(); + }); - glGenBuffers(1, &_vertexBufferHandle); - _vertexBuffer = new V3F_C4B_C4B_T2F[MAX_VERTICES]; - glGenBuffers(1, &_indexBufferHandle); - _indexBuffer = new unsigned short[MAX_INDICES]; - _positionAttributeLocation = _twoColorTintShader->getAttribLocation("a_position"); - _colorAttributeLocation = _twoColorTintShader->getAttribLocation("a_color"); - _color2AttributeLocation = _twoColorTintShader->getAttribLocation("a_color2"); - _texCoordsAttributeLocation = _twoColorTintShader->getAttribLocation("a_texCoords"); } SkeletonTwoColorBatch::~SkeletonTwoColorBatch () { Director::getInstance()->getEventDispatcher()->removeCustomEventListeners(EVENT_AFTER_DRAW_RESET_POSITION); - spUnsignedShortArray_dispose(_indices); - for (unsigned int i = 0; i < _commandsPool.size(); i++) { delete _commandsPool[i]; _commandsPool[i] = nullptr; } - _twoColorTintShader->release(); - delete _vertexBuffer; - delete _indexBuffer; + + delete[] _vertexBuffer; + delete[] _indexBuffer; } void SkeletonTwoColorBatch::update (float delta) { @@ -240,11 +296,11 @@ void SkeletonTwoColorBatch::deallocateVertices(uint32_t numVertices) { unsigned short* SkeletonTwoColorBatch::allocateIndices(uint32_t numIndices) { - if (_indices->capacity - _indices->size < numIndices) { - unsigned short* oldData = _indices->items; - int oldSize =_indices->size; - spUnsignedShortArray_ensureCapacity(_indices, _indices->size + numIndices); - unsigned short* newData = _indices->items; + if (_indices.getCapacity() - _indices.size() < numIndices) { + unsigned short* oldData = _indices.buffer(); + int oldSize =_indices.size(); + _indices.ensureCapacity(_indices.size() + numIndices); + unsigned short* newData = _indices.buffer(); for (uint32_t i = 0; i < this->_nextFreeCommand; i++) { TwoColorTrianglesCommand* command = _commandsPool[i]; TwoColorTriangles& triangles = (TwoColorTriangles&)command->getTriangles(); @@ -254,25 +310,31 @@ unsigned short* SkeletonTwoColorBatch::allocateIndices(uint32_t numIndices) { } } - unsigned short* indices = _indices->items + _indices->size; - _indices->size += numIndices; + unsigned short* indices = _indices.buffer() + _indices.size(); + _indices.setSize(_indices.size() + numIndices, 0); return indices; } void SkeletonTwoColorBatch::deallocateIndices(uint32_t numIndices) { - _indices->size -= numIndices; + _indices.setSize(_indices.size() - numIndices, 0); } -TwoColorTrianglesCommand* SkeletonTwoColorBatch::addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags) { +TwoColorTrianglesCommand* SkeletonTwoColorBatch::addCommand(cocos2d::Renderer* renderer, float globalOrder, cocos2d::Texture2D* texture, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags) { TwoColorTrianglesCommand* command = nextFreeCommand(); - command->init(globalOrder, textureID, glProgramState, blendType, triangles, mv, flags); + command->init(globalOrder, texture, blendType, triangles, mv, flags); + command->updateVertexAndIndexBuffer(renderer, triangles.verts, triangles.vertCount, triangles.indices, triangles.indexCount); renderer->addCommand(command); return command; } -void SkeletonTwoColorBatch::batch (TwoColorTrianglesCommand* command) { +void SkeletonTwoColorBatch::batch (cocos2d::Renderer *renderer, TwoColorTrianglesCommand* command) { if (_numVerticesBuffer + command->getTriangles().vertCount >= MAX_VERTICES || _numIndicesBuffer + command->getTriangles().indexCount >= MAX_INDICES) { - flush(_lastCommand); + flush(renderer, _lastCommand); + } + + uint32_t materialID = command->getMaterialID(); + if (_lastCommand && _lastCommand->getMaterialID() != materialID) { + flush(renderer, _lastCommand); } memcpy(_vertexBuffer + _numVerticesBuffer, command->getTriangles().verts, sizeof(V3F_C4B_C4B_T2F) * command->getTriangles().vertCount); @@ -290,42 +352,21 @@ void SkeletonTwoColorBatch::batch (TwoColorTrianglesCommand* command) { _numVerticesBuffer += command->getTriangles().vertCount; _numIndicesBuffer += command->getTriangles().indexCount; - uint32_t materialID = command->getMaterialID(); - - if ((_lastCommand && _lastCommand->getMaterialID() != materialID) || command->isForceFlush()) { - flush(_lastCommand); + if (command->isForceFlush()) { + flush(renderer, command); } _lastCommand = command; } -void SkeletonTwoColorBatch::flush (TwoColorTrianglesCommand* materialCommand) { +void SkeletonTwoColorBatch::flush (cocos2d::Renderer *renderer, TwoColorTrianglesCommand* materialCommand) { if (!materialCommand) return; - materialCommand->useMaterial(); - - glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferHandle); - glBufferData(GL_ARRAY_BUFFER, sizeof(V3F_C4B_C4B_T2F) * _numVerticesBuffer , _vertexBuffer, GL_DYNAMIC_DRAW); - - glEnableVertexAttribArray(_positionAttributeLocation); - glEnableVertexAttribArray(_colorAttributeLocation); - glEnableVertexAttribArray(_color2AttributeLocation); - glEnableVertexAttribArray(_texCoordsAttributeLocation); - - glVertexAttribPointer(_positionAttributeLocation, 3, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_C4B_T2F), (GLvoid*)offsetof(V3F_C4B_C4B_T2F, position)); - glVertexAttribPointer(_colorAttributeLocation, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V3F_C4B_C4B_T2F), (GLvoid*)offsetof(V3F_C4B_C4B_T2F, color)); - glVertexAttribPointer(_color2AttributeLocation, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V3F_C4B_C4B_T2F), (GLvoid*)offsetof(V3F_C4B_C4B_T2F, color2)); - glVertexAttribPointer(_texCoordsAttributeLocation, 2, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_C4B_T2F), (GLvoid*)offsetof(V3F_C4B_C4B_T2F, texCoords)); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferHandle); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * _numIndicesBuffer, _indexBuffer, GL_STATIC_DRAW); - - glDrawElements(GL_TRIANGLES, (GLsizei)_numIndicesBuffer, GL_UNSIGNED_SHORT, 0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - _numVerticesBuffer = 0; + materialCommand->updateVertexAndIndexBuffer(renderer, _vertexBuffer, _numVerticesBuffer, _indexBuffer, _numIndicesBuffer); + + renderer->addCommand(materialCommand); + + _numVerticesBuffer = 0; _numIndicesBuffer = 0; _numBatches++; } @@ -333,7 +374,7 @@ void SkeletonTwoColorBatch::flush (TwoColorTrianglesCommand* materialCommand) { void SkeletonTwoColorBatch::reset() { _nextFreeCommand = 0; _numVertices = 0; - _indices->size = 0; + _indices.setSize(0, 0); _numVerticesBuffer = 0; _numIndicesBuffer = 0; _lastCommand = nullptr; @@ -342,8 +383,8 @@ void SkeletonTwoColorBatch::reset() { TwoColorTrianglesCommand* SkeletonTwoColorBatch::nextFreeCommand() { if (_commandsPool.size() <= _nextFreeCommand) { - size_t newSize = _commandsPool.size() * 2 + 1; - for (size_t i = _commandsPool.size(); i < newSize; i++) { + unsigned int newSize = _commandsPool.size() * 2 + 1; + for (int i = _commandsPool.size(); i < newSize; i++) { _commandsPool.push_back(new TwoColorTrianglesCommand()); } } diff --git a/cocos/editor-support/spine/SkeletonTwoColorBatch.h b/cocos/editor-support/spine/SkeletonTwoColorBatch.h index 8035fad47d2c..4f2aa203b39c 100644 --- a/cocos/editor-support/spine/SkeletonTwoColorBatch.h +++ b/cocos/editor-support/spine/SkeletonTwoColorBatch.h @@ -1,31 +1,30 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef SPINE_SKELETONTWOCOLORBATCH_H_ @@ -55,15 +54,19 @@ namespace spine { TwoColorTrianglesCommand(); ~TwoColorTrianglesCommand(); - - void init(float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); - - void useMaterial() const; + + void init(float globalOrder, cocos2d::Texture2D* texture, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); + + void updateCommandPipelineDescriptor(); + + inline cocos2d::backend::TextureBackend* getTexture() const { return _texture; } + + void draw(cocos2d::Renderer *renderer); + + void updateVertexAndIndexBuffer(cocos2d::Renderer *renderer, V3F_C4B_C4B_T2F *vertices, int verticesSize, uint16_t *indices, int indicesSize); inline uint32_t getMaterialID() const { return _materialID; } - inline GLuint getTextureID() const { return _textureID; } - inline const TwoColorTriangles& getTriangles() const { return _triangles; } inline ssize_t getVertexCount() const { return _triangles.vertCount; } @@ -74,14 +77,10 @@ namespace spine { inline const unsigned short* getIndices() const { return _triangles.indices; } - inline cocos2d::GLProgramState* getGLProgramState() const { return _glProgramState; } - inline cocos2d::BlendFunc getBlendType() const { return _blendType; } inline const cocos2d::Mat4& getModelView() const { return _mv; } - void draw (); - void setForceFlush (bool forceFlush) { _forceFlush = forceFlush; } bool isForceFlush () { return _forceFlush; }; @@ -89,14 +88,19 @@ namespace spine { protected: void generateMaterialID(); uint32_t _materialID; - GLuint _textureID; - cocos2d::GLProgramState* _glProgramState; - cocos2d::GLProgram* _glProgram; - cocos2d::BlendFunc _blendType; - TwoColorTriangles _triangles; - cocos2d::Mat4 _mv; - GLuint _alphaTextureID; - bool _forceFlush; + + + void *_prog = nullptr; + cocos2d::backend::TextureBackend *_texture = nullptr; + cocos2d::backend::ProgramState *_programState = nullptr; + cocos2d::backend::UniformLocation _locPMatrix; + cocos2d::backend::UniformLocation _locTexture; + + cocos2d::BlendFunc _blendType; + TwoColorTriangles _triangles; + cocos2d::Mat4 _mv; + GLuint _alphaTextureID; + bool _forceFlush; }; class SkeletonTwoColorBatch { @@ -113,14 +117,12 @@ namespace spine { unsigned short* allocateIndices(uint32_t numIndices); void deallocateIndices(uint32_t numIndices); - TwoColorTrianglesCommand* addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); + TwoColorTrianglesCommand* addCommand(cocos2d::Renderer* renderer, float globalOrder, cocos2d::Texture2D* texture, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags); + + void batch(cocos2d::Renderer* renderer, TwoColorTrianglesCommand* command); + + void flush(cocos2d::Renderer* renderer, TwoColorTrianglesCommand* materialCommand); - cocos2d::GLProgramState* getTwoColorTintProgramState () { return _twoColorTintShaderState; } - - void batch (TwoColorTrianglesCommand* command); - - void flush (TwoColorTrianglesCommand* materialCommand); - uint32_t getNumBatches () { return _numBatches; }; protected: @@ -140,27 +142,19 @@ namespace spine { uint32_t _numVertices; // pool of indices - spUnsignedShortArray* _indices; + Vector _indices; - // two color tint shader and state - cocos2d::GLProgram* _twoColorTintShader; - cocos2d::GLProgramState* _twoColorTintShaderState; // VBO handles & attribute locations GLuint _vertexBufferHandle; V3F_C4B_C4B_T2F* _vertexBuffer; uint32_t _numVerticesBuffer; - GLuint _indexBufferHandle; - uint32_t _numIndicesBuffer; - unsigned short* _indexBuffer; - GLint _positionAttributeLocation; - GLint _colorAttributeLocation; - GLint _color2AttributeLocation; - GLint _texCoordsAttributeLocation; - + uint32_t _numIndicesBuffer; + unsigned short* _indexBuffer; + // last batched command, needed for flushing to set material - TwoColorTrianglesCommand* _lastCommand; - + TwoColorTrianglesCommand* _lastCommand = nullptr; + // number of batches in the last frame uint32_t _numBatches; }; diff --git a/cocos/editor-support/spine/Skin.cpp b/cocos/editor-support/spine/Skin.cpp new file mode 100644 index 000000000000..4f2c8152f7ea --- /dev/null +++ b/cocos/editor-support/spine/Skin.cpp @@ -0,0 +1,145 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include + +#include + +using namespace spine; + +Skin::AttachmentMap::AttachmentMap() { +} + +void Skin::AttachmentMap::put(size_t slotIndex, const String &attachmentName, Attachment *attachment) { + if (slotIndex >= _buckets.size()) + _buckets.setSize(slotIndex + 1, Vector()); + Vector &bucket = _buckets[slotIndex]; + int existing = findInBucket(bucket, attachmentName); + if (existing >= 0) { + bucket[existing]._attachment = attachment; + } else { + bucket.add(Entry(slotIndex, attachmentName, attachment)); + } +} + +Attachment *Skin::AttachmentMap::get(size_t slotIndex, const String &attachmentName) { + if (slotIndex >= _buckets.size()) return NULL; + int existing = findInBucket(_buckets[slotIndex], attachmentName); + return existing >= 0 ? _buckets[slotIndex][existing]._attachment : NULL; +} + +void Skin::AttachmentMap::remove(size_t slotIndex, const String &attachmentName) { + if (slotIndex >= _buckets.size()) return; + int existing = findInBucket(_buckets[slotIndex], attachmentName); + if (existing >= 0) _buckets[slotIndex].removeAt(existing); +} + +int Skin::AttachmentMap::findInBucket(Vector &bucket, const String &attachmentName) { + for (size_t i = 0; i < bucket.size(); i++) + if (bucket[i]._name == attachmentName) return i; + return -1; +} + +Skin::AttachmentMap::Entries Skin::AttachmentMap::getEntries() { + return Skin::AttachmentMap::Entries(_buckets); +} + +Skin::Skin(const String &name) : _name(name), _attachments() { + assert(_name.length() > 0); +} + +Skin::~Skin() { + Skin::AttachmentMap::Entries entries = _attachments.getEntries(); + while (entries.hasNext()) { + Skin::AttachmentMap::Entry entry = entries.next(); + delete entry._attachment; + } +} + +void Skin::addAttachment(size_t slotIndex, const String &name, Attachment *attachment) { + assert(attachment); + _attachments.put(slotIndex, name, attachment); +} + +Attachment *Skin::getAttachment(size_t slotIndex, const String &name) { + return _attachments.get(slotIndex, name); +} + +void Skin::findNamesForSlot(size_t slotIndex, Vector &names) { + Skin::AttachmentMap::Entries entries = _attachments.getEntries(); + while (entries.hasNext()) { + Skin::AttachmentMap::Entry &entry = entries.next(); + if (entry._slotIndex == slotIndex) { + names.add(entry._name); + } + } +} + +void Skin::findAttachmentsForSlot(size_t slotIndex, Vector &attachments) { + Skin::AttachmentMap::Entries entries = _attachments.getEntries(); + while (entries.hasNext()) { + Skin::AttachmentMap::Entry &entry = entries.next(); + if (entry._slotIndex == slotIndex) { + attachments.add(entry._attachment); + } + } +} + +const String &Skin::getName() { + return _name; +} + +Skin::AttachmentMap::Entries Skin::getAttachments() { + return _attachments.getEntries(); +} + +void Skin::attachAll(Skeleton &skeleton, Skin &oldSkin) { + Vector &slots = skeleton.getSlots(); + Skin::AttachmentMap::Entries entries = oldSkin.getAttachments(); + while (entries.hasNext()) { + Skin::AttachmentMap::Entry &entry = entries.next(); + int slotIndex = entry._slotIndex; + Slot *slot = slots[slotIndex]; + + if (slot->getAttachment() == entry._attachment) { + Attachment *attachment = getAttachment(slotIndex, entry._name); + if (attachment) { + slot->setAttachment(attachment); + } + } + } +} diff --git a/cocos/editor-support/spine/Skin.h b/cocos/editor-support/spine/Skin.h index 488ad1450543..6a4a6b6e0c97 100644 --- a/cocos/editor-support/spine/Skin.h +++ b/cocos/editor-support/spine/Skin.h @@ -1,95 +1,148 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_SKIN_H_ -#define SPINE_SKIN_H_ +#ifndef Spine_Skin_h +#define Spine_Skin_h -#include -#include +#include +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace spine { +class Attachment; -struct spSkeleton; +class Skeleton; -typedef struct spSkin { - const char* const name; +/// Stores attachments by slot index and attachment name. +/// See SkeletonData::getDefaultSkin, Skeleton::getSkin, and +/// http://esotericsoftware.com/spine-runtime-skins in the Spine Runtimes Guide. +class SP_API Skin : public SpineObject { + friend class Skeleton; -#ifdef __cplusplus - spSkin() : - name(0) { - } -#endif -} spSkin; +public: + class SP_API AttachmentMap : public SpineObject { + friend class Skin; -/* Private structs, needed by Skeleton */ -typedef struct _Entry _Entry; -struct _Entry { - int slotIndex; - const char* name; - spAttachment* attachment; - _Entry* next; -}; + public: + struct SP_API Entry { + size_t _slotIndex; + String _name; + Attachment *_attachment; + + Entry(size_t slotIndex, const String &name, Attachment *attachment) : + _slotIndex(slotIndex), + _name(name), + _attachment(attachment) { + } + }; + + class SP_API Entries { + friend class AttachmentMap; + + public: + bool hasNext() { + while(true) { + if (_slotIndex >= _buckets.size()) return false; + if (_bucketIndex >= _buckets[_slotIndex].size()) { + _bucketIndex = 0; + ++_slotIndex; + continue; + }; + return true; + } + } + + Entry &next() { + Entry &result = _buckets[_slotIndex][_bucketIndex]; + ++_bucketIndex; + return result; + } + + protected: + Entries(Vector< Vector > &buckets) : _buckets(buckets), _slotIndex(0), _bucketIndex(0) { + } + + private: + Vector< Vector > &_buckets; + size_t _slotIndex; + size_t _bucketIndex; + }; + + void put(size_t slotIndex, const String &attachmentName, Attachment *attachment); + + Attachment *get(size_t slotIndex, const String &attachmentName); -typedef struct { - spSkin super; - _Entry* entries; -} _spSkin; + void remove(size_t slotIndex, const String &attachmentName); -SP_API spSkin* spSkin_create (const char* name); -SP_API void spSkin_dispose (spSkin* self); + Entries getEntries(); -/* The Skin owns the attachment. */ -SP_API void spSkin_addAttachment (spSkin* self, int slotIndex, const char* name, spAttachment* attachment); -/* Returns 0 if the attachment was not found. */ -SP_API spAttachment* spSkin_getAttachment (const spSkin* self, int slotIndex, const char* name); + protected: + AttachmentMap(); -/* Returns 0 if the slot or attachment was not found. */ -SP_API const char* spSkin_getAttachmentName (const spSkin* self, int slotIndex, int attachmentIndex); + private: -/** Attach each attachment in this skin if the corresponding attachment in oldSkin is currently attached. */ -SP_API void spSkin_attachAll (const spSkin* self, struct spSkeleton* skeleton, const spSkin* oldspSkin); + int findInBucket(Vector &, const String &attachmentName); -#ifdef SPINE_SHORT_NAMES -typedef spSkin Skin; -#define Skin_create(...) spSkin_create(__VA_ARGS__) -#define Skin_dispose(...) spSkin_dispose(__VA_ARGS__) -#define Skin_addAttachment(...) spSkin_addAttachment(__VA_ARGS__) -#define Skin_getAttachment(...) spSkin_getAttachment(__VA_ARGS__) -#define Skin_getAttachmentName(...) spSkin_getAttachmentName(__VA_ARGS__) -#define Skin_attachAll(...) spSkin_attachAll(__VA_ARGS__) -#endif + Vector > _buckets; + }; -#ifdef __cplusplus + explicit Skin(const String &name); + + ~Skin(); + + /// Adds an attachment to the skin for the specified slot index and name. + /// If the name already exists for the slot, the previous value is replaced. + void addAttachment(size_t slotIndex, const String &name, Attachment *attachment); + + /// Returns the attachment for the specified slot index and name, or NULL. + Attachment *getAttachment(size_t slotIndex, const String &name); + + /// Finds the skin keys for a given slot. The results are added to the passed array of names. + /// @param slotIndex The target slotIndex. To find the slot index, use Skeleton::findSlotIndex or SkeletonData::findSlotIndex + /// @param names Found skin key names will be added to this array. + void findNamesForSlot(size_t slotIndex, Vector &names); + + /// Finds the attachments for a given slot. The results are added to the passed array of Attachments. + /// @param slotIndex The target slotIndex. To find the slot index, use Skeleton::findSlotIndex or SkeletonData::findSlotIndex + /// @param attachments Found Attachments will be added to this array. + void findAttachmentsForSlot(size_t slotIndex, Vector &attachments); + + const String &getName(); + + AttachmentMap::Entries getAttachments(); + +private: + const String _name; + AttachmentMap _attachments; + + /// Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached. + void attachAll(Skeleton &skeleton, Skin &oldSkin); +}; } -#endif -#endif /* SPINE_SKIN_H_ */ +#endif /* Spine_Skin_h */ diff --git a/cocos/editor-support/spine/Slot.cpp b/cocos/editor-support/spine/Slot.cpp new file mode 100644 index 000000000000..2dc61dd32794 --- /dev/null +++ b/cocos/editor-support/spine/Slot.cpp @@ -0,0 +1,114 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include +#include +#include + +using namespace spine; + +Slot::Slot(SlotData &data, Bone &bone) : + _data(data), + _bone(bone), + _skeleton(bone.getSkeleton()), + _color(1, 1, 1, 1), + _darkColor(0, 0, 0, 0), + _hasDarkColor(data.hasDarkColor()), + _attachment(NULL), + _attachmentTime(0) { + setToSetupPose(); +} + +void Slot::setToSetupPose() { + _color.set(_data.getColor()); + + const String &attachmentName = _data.getAttachmentName(); + if (attachmentName.length() > 0) { + _attachment = NULL; + setAttachment(_skeleton.getAttachment(_data.getIndex(), attachmentName)); + } else { + setAttachment(NULL); + } +} + +SlotData &Slot::getData() { + return _data; +} + +Bone &Slot::getBone() { + return _bone; +} + +Skeleton &Slot::getSkeleton() { + return _skeleton; +} + +Color &Slot::getColor() { + return _color; +} + +Color &Slot::getDarkColor() { + return _darkColor; +} + +bool Slot::hasDarkColor() { + return _hasDarkColor; +} + +Attachment *Slot::getAttachment() { + return _attachment; +} + +void Slot::setAttachment(Attachment *inValue) { + if (_attachment == inValue) { + return; + } + + _attachment = inValue; + _attachmentTime = _skeleton.getTime(); + _attachmentVertices.clear(); +} + +float Slot::getAttachmentTime() { + return _skeleton.getTime() - _attachmentTime; +} + +void Slot::setAttachmentTime(float inValue) { + _attachmentTime = _skeleton.getTime() - inValue; +} + +Vector &Slot::getAttachmentVertices() { + return _attachmentVertices; +} diff --git a/cocos/editor-support/spine/Slot.h b/cocos/editor-support/spine/Slot.h index 16db951b6d9e..c92f4d703520 100644 --- a/cocos/editor-support/spine/Slot.h +++ b/cocos/editor-support/spine/Slot.h @@ -1,93 +1,126 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_SLOT_H_ -#define SPINE_SLOT_H_ - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spSlot { - spSlotData* const data; - spBone* const bone; - spColor color; - spColor* darkColor; - spAttachment* const attachment; - - int attachmentVerticesCapacity; - int attachmentVerticesCount; - float* attachmentVertices; - -#ifdef __cplusplus - spSlot() : - data(0), - bone(0), - color(), - darkColor(0), - attachment(0), - attachmentVerticesCapacity(0), - attachmentVerticesCount(0), - attachmentVertices(0) { - } -#endif -} spSlot; - -SP_API spSlot* spSlot_create (spSlotData* data, spBone* bone); -SP_API void spSlot_dispose (spSlot* self); - -/* @param attachment May be 0 to clear the attachment for the slot. */ -SP_API void spSlot_setAttachment (spSlot* self, spAttachment* attachment); - -SP_API void spSlot_setAttachmentTime (spSlot* self, float time); -SP_API float spSlot_getAttachmentTime (const spSlot* self); - -SP_API void spSlot_setToSetupPose (spSlot* self); - -#ifdef SPINE_SHORT_NAMES -typedef spSlot Slot; -#define Slot_create(...) spSlot_create(__VA_ARGS__) -#define Slot_dispose(...) spSlot_dispose(__VA_ARGS__) -#define Slot_setAttachment(...) spSlot_setAttachment(__VA_ARGS__) -#define Slot_setAttachmentTime(...) spSlot_setAttachmentTime(__VA_ARGS__) -#define Slot_getAttachmentTime(...) spSlot_getAttachmentTime(__VA_ARGS__) -#define Slot_setToSetupPose(...) spSlot_setToSetupPose(__VA_ARGS__) -#endif - -#ifdef __cplusplus +#ifndef Spine_Slot_h +#define Spine_Slot_h + +#include +#include +#include + +#include + +namespace spine { +class SlotData; + +class Bone; + +class Skeleton; + +class Attachment; + +class SP_API Slot : public SpineObject { + friend class VertexAttachment; + + friend class Skeleton; + + friend class SkeletonBounds; + + friend class SkeletonClipping; + + friend class AttachmentTimeline; + + friend class ColorTimeline; + + friend class DeformTimeline; + + friend class DrawOrderTimeline; + + friend class EventTimeline; + + friend class IkConstraintTimeline; + + friend class PathConstraintMixTimeline; + + friend class PathConstraintPositionTimeline; + + friend class PathConstraintSpacingTimeline; + + friend class ScaleTimeline; + + friend class ShearTimeline; + + friend class TransformConstraintTimeline; + + friend class TranslateTimeline; + + friend class TwoColorTimeline; + +public: + Slot(SlotData &data, Bone &bone); + + void setToSetupPose(); + + SlotData &getData(); + + Bone &getBone(); + + Skeleton &getSkeleton(); + + Color &getColor(); + + Color &getDarkColor(); + + bool hasDarkColor(); + + /// May be NULL. + Attachment *getAttachment(); + + void setAttachment(Attachment *inValue); + + float getAttachmentTime(); + + void setAttachmentTime(float inValue); + + Vector &getAttachmentVertices(); + +private: + SlotData &_data; + Bone &_bone; + Skeleton &_skeleton; + Color _color; + Color _darkColor; + bool _hasDarkColor; + Attachment *_attachment; + float _attachmentTime; + Vector _attachmentVertices; +}; } -#endif -#endif /* SPINE_SLOT_H_ */ +#endif /* Spine_Slot_h */ diff --git a/cocos/editor-support/spine/SlotData.cpp b/cocos/editor-support/spine/SlotData.cpp new file mode 100644 index 000000000000..940f958045e0 --- /dev/null +++ b/cocos/editor-support/spine/SlotData.cpp @@ -0,0 +1,95 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +using namespace spine; + +SlotData::SlotData(int index, const String &name, BoneData &boneData) : + _index(index), + _name(name), + _boneData(boneData), + _color(1, 1, 1, 1), + _darkColor(0, 0, 0, 0), + _hasDarkColor(false), + _attachmentName(), + _blendMode(BlendMode_Normal) { + assert(_index >= 0); + assert(_name.length() > 0); +} + +int SlotData::getIndex() { + return _index; +} + +const String &SlotData::getName() { + return _name; +} + +BoneData &SlotData::getBoneData() { + return _boneData; +} + +Color &SlotData::getColor() { + return _color; +} + +Color &SlotData::getDarkColor() { + return _darkColor; +} + +bool SlotData::hasDarkColor() { + return _hasDarkColor; +} + +void SlotData::setHasDarkColor(bool inValue) { + _hasDarkColor = inValue; +} + +const String &SlotData::getAttachmentName() { + return _attachmentName; +} + +void SlotData::setAttachmentName(const String &inValue) { + _attachmentName = inValue; +} + +BlendMode SlotData::getBlendMode() { + return _blendMode; +} + +void SlotData::setBlendMode(BlendMode inValue) { + _blendMode = inValue; +} diff --git a/cocos/editor-support/spine/SlotData.h b/cocos/editor-support/spine/SlotData.h index 669d01f50cb1..e8abac61ff31 100644 --- a/cocos/editor-support/spine/SlotData.h +++ b/cocos/editor-support/spine/SlotData.h @@ -1,90 +1,113 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_SLOTDATA_H_ -#define SPINE_SLOTDATA_H_ +#ifndef Spine_SlotData_h +#define Spine_SlotData_h -#include -#include +#include +#include +#include #include -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - SP_BLEND_MODE_NORMAL, SP_BLEND_MODE_ADDITIVE, SP_BLEND_MODE_MULTIPLY, SP_BLEND_MODE_SCREEN -} spBlendMode; - -typedef struct spSlotData { - const int index; - const char* const name; - const spBoneData* const boneData; - const char* attachmentName; - spColor color; - spColor* darkColor; - spBlendMode blendMode; - -#ifdef __cplusplus - spSlotData() : - index(0), - name(0), - boneData(0), - attachmentName(0), - color(), - darkColor(0), - blendMode(SP_BLEND_MODE_NORMAL) { - } -#endif -} spSlotData; - -SP_API spSlotData* spSlotData_create (const int index, const char* name, spBoneData* boneData); -SP_API void spSlotData_dispose (spSlotData* self); - -/* @param attachmentName May be 0 for no setup pose attachment. */ -SP_API void spSlotData_setAttachmentName (spSlotData* self, const char* attachmentName); - -#ifdef SPINE_SHORT_NAMES -typedef spBlendMode BlendMode; -#define BLEND_MODE_NORMAL SP_BLEND_MODE_NORMAL -#define BLEND_MODE_ADDITIVE SP_BLEND_MODE_ADDITIVE -#define BLEND_MODE_MULTIPLY SP_BLEND_MODE_MULTIPLY -#define BLEND_MODE_SCREEN SP_BLEND_MODE_SCREEN -typedef spSlotData SlotData; -#define SlotData_create(...) spSlotData_create(__VA_ARGS__) -#define SlotData_dispose(...) spSlotData_dispose(__VA_ARGS__) -#define SlotData_setAttachmentName(...) spSlotData_setAttachmentName(__VA_ARGS__) -#endif - -#ifdef __cplusplus +namespace spine { +class BoneData; + +class SP_API SlotData : public SpineObject { + friend class SkeletonBinary; + + friend class SkeletonJson; + + friend class AttachmentTimeline; + + friend class ColorTimeline; + + friend class DeformTimeline; + + friend class DrawOrderTimeline; + + friend class EventTimeline; + + friend class IkConstraintTimeline; + + friend class PathConstraintMixTimeline; + + friend class PathConstraintPositionTimeline; + + friend class PathConstraintSpacingTimeline; + + friend class ScaleTimeline; + + friend class ShearTimeline; + + friend class TransformConstraintTimeline; + + friend class TranslateTimeline; + + friend class TwoColorTimeline; + +public: + SlotData(int index, const String &name, BoneData &boneData); + + int getIndex(); + + const String &getName(); + + BoneData &getBoneData(); + + Color &getColor(); + + Color &getDarkColor(); + + bool hasDarkColor(); + + void setHasDarkColor(bool inValue); + + /// May be empty. + const String &getAttachmentName(); + + void setAttachmentName(const String &inValue); + + BlendMode getBlendMode(); + + void setBlendMode(BlendMode inValue); + +private: + const int _index; + String _name; + BoneData &_boneData; + Color _color; + Color _darkColor; + + bool _hasDarkColor; + String _attachmentName; + BlendMode _blendMode; +}; } -#endif -#endif /* SPINE_SLOTDATA_H_ */ +#endif /* Spine_SlotData_h */ diff --git a/cocos/editor-support/spine/SpacingMode.h b/cocos/editor-support/spine/SpacingMode.h new file mode 100644 index 000000000000..61ec06ddec99 --- /dev/null +++ b/cocos/editor-support/spine/SpacingMode.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_SpacingMode_h +#define Spine_SpacingMode_h + +namespace spine { +enum SpacingMode { + SpacingMode_Length = 0, + SpacingMode_Fixed, + SpacingMode_Percent +}; +} + +#endif /* Spine_SpacingMode_h */ diff --git a/cocos/editor-support/spine/SpineObject.cpp b/cocos/editor-support/spine/SpineObject.cpp new file mode 100644 index 000000000000..fdcdc49bfdb8 --- /dev/null +++ b/cocos/editor-support/spine/SpineObject.cpp @@ -0,0 +1,65 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include + +using namespace spine; + +void *SpineObject::operator new(size_t sz) { + return SpineExtension::calloc(sz, __FILE__, __LINE__); +} + +void *SpineObject::operator new(size_t sz, const char *file, int line) { + return SpineExtension::calloc(sz, file, line); +} + +void *SpineObject::operator new(size_t sz, void *ptr) { + SP_UNUSED(sz); + return ptr; +} + +void SpineObject::operator delete(void *p, const char *file, int line) { + SpineExtension::free(p, file, line); +} + +void SpineObject::operator delete(void *p, void *mem) { + SP_UNUSED(mem); + SpineExtension::free(p, __FILE__, __LINE__); +} + +void SpineObject::operator delete(void *p) { + SpineExtension::free(p, __FILE__, __LINE__); +} + +SpineObject::~SpineObject() { +} diff --git a/cocos/editor-support/spine/SpineObject.h b/cocos/editor-support/spine/SpineObject.h new file mode 100644 index 000000000000..7cd64f17b908 --- /dev/null +++ b/cocos/editor-support/spine/SpineObject.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Object_h +#define Spine_Object_h + +#include +#include + +#include + +namespace spine { +class String; + +class SP_API SpineObject { +public: + void *operator new(size_t sz); + + void *operator new(size_t sz, const char *file, int line); + + void *operator new(size_t sz, void *ptr); + + void operator delete(void *p, const char *file, int line); + + void operator delete(void *p, void *mem); + + void operator delete(void *p); + + virtual ~SpineObject(); +}; +} + +#endif diff --git a/cocos/editor-support/spine/SpineString.h b/cocos/editor-support/spine/SpineString.h new file mode 100644 index 000000000000..fa91c12efc8e --- /dev/null +++ b/cocos/editor-support/spine/SpineString.h @@ -0,0 +1,212 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef SPINE_STRING_H +#define SPINE_STRING_H + +#include +#include + +#include +#include + +// Required for sprintf on MSVC +#ifdef _MSC_VER +#pragma warning(disable:4996) +#endif + +namespace spine { +class SP_API String : public SpineObject { +public: + String() : _length(0), _buffer(NULL) { + } + + String(const char *chars, bool own = false) { + if (!chars) { + _length = 0; + _buffer = NULL; + } else { + _length = strlen(chars); + if (!own) { + _buffer = SpineExtension::calloc(_length + 1, __FILE__, __LINE__); + memcpy((void *) _buffer, chars, _length + 1); + } else { + _buffer = (char *) chars; + } + } + } + + String(const String &other) { + if (!other._buffer) { + _length = 0; + _buffer = NULL; + } else { + _length = other._length; + _buffer = SpineExtension::calloc(other._length + 1, __FILE__, __LINE__); + memcpy((void *) _buffer, other._buffer, other._length + 1); + } + } + + size_t length() const { + return _length; + } + + bool isEmpty() const { + return _length == 0; + } + + const char *buffer() const { + return _buffer; + } + + void own(const String &other) { + if (this == &other) return; + if (_buffer) { + SpineExtension::free(_buffer, __FILE__, __LINE__); + } + _length = other._length; + _buffer = other._buffer; + other._length = 0; + other._buffer = NULL; + } + + void own(const char *chars) { + if (_buffer == chars) return; + if (_buffer) { + SpineExtension::free(_buffer, __FILE__, __LINE__); + } + + if (!chars) { + _length = 0; + _buffer = NULL; + } else { + _length = strlen(chars); + _buffer = (char *) chars; + } + } + + void unown() { + _length = 0; + _buffer = NULL; + } + + String &operator=(const String &other) { + if (this == &other) return *this; + if (_buffer) { + SpineExtension::free(_buffer, __FILE__, __LINE__); + } + if (!other._buffer) { + _length = 0; + _buffer = NULL; + } else { + _length = other._length; + _buffer = SpineExtension::calloc(other._length + 1, __FILE__, __LINE__); + memcpy((void *) _buffer, other._buffer, other._length + 1); + } + return *this; + } + + String &operator=(const char *chars) { + if (_buffer == chars) return *this; + if (_buffer) { + SpineExtension::free(_buffer, __FILE__, __LINE__); + } + if (!chars) { + _length = 0; + _buffer = NULL; + } else { + _length = strlen(chars); + _buffer = SpineExtension::calloc(_length + 1, __FILE__, __LINE__); + memcpy((void *) _buffer, chars, _length + 1); + } + return *this; + } + + String &append(const char *chars) { + size_t len = strlen(chars); + size_t thisLen = _length; + _length = _length + len; + bool same = chars == _buffer; + _buffer = SpineExtension::realloc(_buffer, _length + 1, __FILE__, __LINE__); + memcpy((void *) (_buffer + thisLen), (void *) (same ? _buffer : chars), len + 1); + return *this; + } + + String &append(const String &other) { + size_t len = other.length(); + size_t thisLen = _length; + _length = _length + len; + bool same = other._buffer == _buffer; + _buffer = SpineExtension::realloc(_buffer, _length + 1, __FILE__, __LINE__); + memcpy((void *) (_buffer + thisLen), (void *) (same ? _buffer : other._buffer), len + 1); + return *this; + } + + String &append(int other) { + char str[100]; + sprintf(str, "%i", other); + append(str); + return *this; + } + + String &append(float other) { + char str[100]; + sprintf(str, "%f", other); + append(str); + return *this; + } + + friend bool operator==(const String &a, const String &b) { + if (a._buffer == b._buffer) return true; + if (a._length != b._length) return false; + if (a._buffer && b._buffer) { + return strcmp(a._buffer, b._buffer) == 0; + } else { + return false; + } + } + + friend bool operator!=(const String &a, const String &b) { + return !(a == b); + } + + ~String() { + if (_buffer) { + SpineExtension::free(_buffer, __FILE__, __LINE__); + } + } + +private: + mutable size_t _length; + mutable char *_buffer; +}; +} + + +#endif //SPINE_STRING_H diff --git a/cocos/editor-support/spine/TextureLoader.cpp b/cocos/editor-support/spine/TextureLoader.cpp new file mode 100644 index 000000000000..f9ff51c82d56 --- /dev/null +++ b/cocos/editor-support/spine/TextureLoader.cpp @@ -0,0 +1,42 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +namespace spine { +TextureLoader::TextureLoader() { +} + +TextureLoader::~TextureLoader() { +} +} diff --git a/cocos/editor-support/spine/TextureLoader.h b/cocos/editor-support/spine/TextureLoader.h new file mode 100644 index 000000000000..644c2b4bd58b --- /dev/null +++ b/cocos/editor-support/spine/TextureLoader.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TextureLoader_h +#define Spine_TextureLoader_h + +#include +#include + +namespace spine { + class AtlasPage; + + class SP_API TextureLoader : public SpineObject { + public: + TextureLoader(); + + virtual ~TextureLoader(); + + virtual void load(AtlasPage& page, const String& path) = 0; + + virtual void unload(void* texture) = 0; + }; +} + +#endif /* Spine_TextureLoader_h */ diff --git a/cocos/editor-support/spine/Timeline.cpp b/cocos/editor-support/spine/Timeline.cpp new file mode 100644 index 000000000000..e9029ef4932c --- /dev/null +++ b/cocos/editor-support/spine/Timeline.cpp @@ -0,0 +1,48 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +namespace spine { +RTTI_IMPL_NOPARENT(Timeline) + +Timeline::Timeline() { +} + +Timeline::~Timeline() { +} + +} diff --git a/cocos/editor-support/spine/Timeline.h b/cocos/editor-support/spine/Timeline.h new file mode 100644 index 000000000000..311108c3c996 --- /dev/null +++ b/cocos/editor-support/spine/Timeline.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Timeline_h +#define Spine_Timeline_h + +#include +#include +#include +#include +#include + +namespace spine { +class Skeleton; + +class Event; + +class SP_API Timeline : public SpineObject { +RTTI_DECL + +public: + Timeline(); + + virtual ~Timeline(); + + /// Sets the value(s) for the specified time. + /// @param skeleton The skeleton the timeline is being applied to. This provides access to the bones, slots, and other skeleton components the timeline may change. + /// @param lastTime lastTime The time this timeline was last applied. Timelines such as EventTimeline trigger only at specific times rather than every frame. In that case, the timeline triggers everything between lastTime (exclusive) and time (inclusive). + /// @param time The time within the animation. Most timelines find the key before and the key after this time so they can interpolate between the keys. + /// @param pEvents If any events are fired, they are added to this array. Can be NULL to ignore firing events or if the timeline does not fire events. May be NULL. + /// @param alpha alpha 0 applies the current or setup pose value (depending on pose parameter). 1 applies the timeline + /// value. Between 0 and 1 applies a value between the current or setup pose and the timeline value. By adjusting + /// alpha over time, an animation can be mixed in or out. alpha can also be useful to + /// apply animations on top of each other (layered). + /// @param blend Controls how mixing is applied when alpha is than 1. + /// @param direction Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions such as DrawOrderTimeline and AttachmentTimeline. + virtual void + apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, MixBlend blend, + MixDirection direction) = 0; + + virtual int getPropertyId() = 0; +}; +} + +#endif /* Spine_Timeline_h */ diff --git a/cocos/editor-support/spine/TimelineType.h b/cocos/editor-support/spine/TimelineType.h new file mode 100644 index 000000000000..2626b5ae492c --- /dev/null +++ b/cocos/editor-support/spine/TimelineType.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TimelineType_h +#define Spine_TimelineType_h + +namespace spine { +enum TimelineType { + TimelineType_Rotate = 0, + TimelineType_Translate, + TimelineType_Scale, + TimelineType_Shear, + TimelineType_Attachment, + TimelineType_Color, + TimelineType_Deform, + TimelineType_Event, + TimelineType_DrawOrder, + TimelineType_IkConstraint, + TimelineType_TransformConstraint, + TimelineType_PathConstraintPosition, + TimelineType_PathConstraintSpacing, + TimelineType_PathConstraintMix, + TimelineType_TwoColor +}; +} + +#endif /* Spine_TimelineType_h */ diff --git a/cocos/editor-support/spine/TransformConstraint.cpp b/cocos/editor-support/spine/TransformConstraint.cpp new file mode 100644 index 000000000000..70f3907fa82a --- /dev/null +++ b/cocos/editor-support/spine/TransformConstraint.cpp @@ -0,0 +1,382 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include +#include + +#include + +using namespace spine; + +RTTI_IMPL(TransformConstraint, Constraint) + +TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton &skeleton) : Constraint(), + _data(data), + _target(skeleton.findBone( + data.getTarget()->getName())), + _rotateMix( + data.getRotateMix()), + _translateMix( + data.getTranslateMix()), + _scaleMix( + data.getScaleMix()), + _shearMix( + data.getShearMix()) { + _bones.ensureCapacity(_data.getBones().size()); + for (size_t i = 0; i < _data.getBones().size(); ++i) { + BoneData *boneData = _data.getBones()[i]; + + _bones.add(skeleton.findBone(boneData->getName())); + } +} + +void TransformConstraint::apply() { + update(); +} + +void TransformConstraint::update() { + if (_data.isLocal()) { + if (_data.isRelative()) { + applyRelativeLocal(); + } else { + applyAbsoluteLocal(); + } + } else { + if (_data.isRelative()) { + applyRelativeWorld(); + } else { + applyAbsoluteWorld(); + } + } +} + +int TransformConstraint::getOrder() { + return _data.getOrder(); +} + +TransformConstraintData &TransformConstraint::getData() { + return _data; +} + +Vector &TransformConstraint::getBones() { + return _bones; +} + +Bone *TransformConstraint::getTarget() { + return _target; +} + +void TransformConstraint::setTarget(Bone *inValue) { + _target = inValue; +} + +float TransformConstraint::getRotateMix() { + return _rotateMix; +} + +void TransformConstraint::setRotateMix(float inValue) { + _rotateMix = inValue; +} + +float TransformConstraint::getTranslateMix() { + return _translateMix; +} + +void TransformConstraint::setTranslateMix(float inValue) { + _translateMix = inValue; +} + +float TransformConstraint::getScaleMix() { + return _scaleMix; +} + +void TransformConstraint::setScaleMix(float inValue) { + _scaleMix = inValue; +} + +float TransformConstraint::getShearMix() { + return _shearMix; +} + +void TransformConstraint::setShearMix(float inValue) { + _shearMix = inValue; +} + +void TransformConstraint::applyAbsoluteWorld() { + float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix; + Bone &target = *_target; + float ta = target._a, tb = target._b, tc = target._c, td = target._d; + float degRadReflect = ta * td - tb * tc > 0 ? MathUtil::Deg_Rad : -MathUtil::Deg_Rad; + float offsetRotation = _data._offsetRotation * degRadReflect, offsetShearY = _data._offsetShearY * degRadReflect; + + for (size_t i = 0; i < _bones.size(); ++i) { + Bone *item = _bones[i]; + Bone &bone = *item; + + bool modified = false; + + if (rotateMix != 0) { + float a = bone._a, b = bone._b, c = bone._c, d = bone._d; + float r = MathUtil::atan2(tc, ta) - MathUtil::atan2(c, a) + offsetRotation; + if (r > MathUtil::Pi) { + r -= MathUtil::Pi_2; + } else if (r < -MathUtil::Pi) { + r += MathUtil::Pi_2; + } + + r *= rotateMix; + float cos = MathUtil::cos(r), sin = MathUtil::sin(r); + bone._a = cos * a - sin * c; + bone._b = cos * b - sin * d; + bone._c = sin * a + cos * c; + bone._d = sin * b + cos * d; + modified = true; + } + + if (translateMix != 0) { + float tx, ty; + target.localToWorld(_data._offsetX, _data._offsetY, tx, ty); + bone._worldX += (tx - bone._worldX) * translateMix; + bone._worldY += (ty - bone._worldY) * translateMix; + modified = true; + } + + if (scaleMix > 0) { + float s = MathUtil::sqrt(bone._a * bone._a + bone._c * bone._c); + + if (s > 0.00001f) { + s = (s + (MathUtil::sqrt(ta * ta + tc * tc) - s + _data._offsetScaleX) * scaleMix) / s; + } + bone._a *= s; + bone._c *= s; + s = MathUtil::sqrt(bone._b * bone._b + bone._d * bone._d); + + if (s > 0.00001f) { + s = (s + (MathUtil::sqrt(tb * tb + td * td) - s + _data._offsetScaleY) * scaleMix) / s; + } + bone._b *= s; + bone._d *= s; + modified = true; + } + + if (shearMix > 0) { + float b = bone._b, d = bone._d; + float by = MathUtil::atan2(d, b); + float r = MathUtil::atan2(td, tb) - MathUtil::atan2(tc, ta) - (by - MathUtil::atan2(bone._c, bone._a)); + if (r > MathUtil::Pi) { + r -= MathUtil::Pi_2; + } else if (r < -MathUtil::Pi) { + r += MathUtil::Pi_2; + } + + r = by + (r + offsetShearY) * shearMix; + float s = MathUtil::sqrt(b * b + d * d); + bone._b = MathUtil::cos(r) * s; + bone._d = MathUtil::sin(r) * s; + modified = true; + } + + if (modified) { + bone._appliedValid = false; + } + } +} + +void TransformConstraint::applyRelativeWorld() { + float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix; + Bone &target = *_target; + float ta = target._a, tb = target._b, tc = target._c, td = target._d; + float degRadReflect = ta * td - tb * tc > 0 ? MathUtil::Deg_Rad : -MathUtil::Deg_Rad; + float offsetRotation = _data._offsetRotation * degRadReflect, offsetShearY = _data._offsetShearY * degRadReflect; + for (size_t i = 0; i < _bones.size(); ++i) { + Bone *item = _bones[i]; + Bone &bone = *item; + + bool modified = false; + + if (rotateMix != 0) { + float a = bone._a, b = bone._b, c = bone._c, d = bone._d; + float r = MathUtil::atan2(tc, ta) + offsetRotation; + if (r > MathUtil::Pi) { + r -= MathUtil::Pi_2; + } else if (r < -MathUtil::Pi) { + r += MathUtil::Pi_2; + } + + r *= rotateMix; + float cos = MathUtil::cos(r), sin = MathUtil::sin(r); + bone._a = cos * a - sin * c; + bone._b = cos * b - sin * d; + bone._c = sin * a + cos * c; + bone._d = sin * b + cos * d; + modified = true; + } + + if (translateMix != 0) { + float tx, ty; + target.localToWorld(_data._offsetX, _data._offsetY, tx, ty); + bone._worldX += tx * translateMix; + bone._worldY += ty * translateMix; + modified = true; + } + + if (scaleMix > 0) { + float s = (MathUtil::sqrt(ta * ta + tc * tc) - 1 + _data._offsetScaleX) * scaleMix + 1; + bone._a *= s; + bone._c *= s; + s = (MathUtil::sqrt(tb * tb + td * td) - 1 + _data._offsetScaleY) * scaleMix + 1; + bone._b *= s; + bone._d *= s; + modified = true; + } + + if (shearMix > 0) { + float r = MathUtil::atan2(td, tb) - MathUtil::atan2(tc, ta); + if (r > MathUtil::Pi) { + r -= MathUtil::Pi_2; + } else if (r < -MathUtil::Pi) { + r += MathUtil::Pi_2; + } + + float b = bone._b, d = bone._d; + r = MathUtil::atan2(d, b) + (r - MathUtil::Pi / 2 + offsetShearY) * shearMix; + float s = MathUtil::sqrt(b * b + d * d); + bone._b = MathUtil::cos(r) * s; + bone._d = MathUtil::sin(r) * s; + modified = true; + } + + if (modified) { + bone._appliedValid = false; + } + } +} + +void TransformConstraint::applyAbsoluteLocal() { + float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix; + Bone &target = *_target; + if (!target._appliedValid) { + target.updateAppliedTransform(); + } + + for (size_t i = 0; i < _bones.size(); ++i) { + Bone *item = _bones[i]; + Bone &bone = *item; + + if (!bone._appliedValid) { + bone.updateAppliedTransform(); + } + + float rotation = bone._arotation; + if (rotateMix != 0) { + float r = target._arotation - rotation + _data._offsetRotation; + r -= (16384 - (int) (16384.499999999996 - r / 360)) * 360; + rotation += r * rotateMix; + } + + float x = bone._ax, y = bone._ay; + if (translateMix != 0) { + x += (target._ax - x + _data._offsetX) * translateMix; + y += (target._ay - y + _data._offsetY) * translateMix; + } + + float scaleX = bone._ascaleX, scaleY = bone._ascaleY; + if (scaleMix != 0) { + if (scaleX > 0.00001f) { + scaleX = (scaleX + (target._ascaleX - scaleX + _data._offsetScaleX) * scaleMix) / scaleX; + } + + if (scaleY > 0.00001f) { + scaleY = (scaleY + (target._ascaleY - scaleY + _data._offsetScaleY) * scaleMix) / scaleY; + } + } + + float shearY = bone._ashearY; + if (shearMix != 0) { + float r = target._ashearY - shearY + _data._offsetShearY; + r -= (16384 - (int) (16384.499999999996 - r / 360)) * 360; + bone._shearY += r * shearMix; + } + + bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone._ashearX, shearY); + } +} + +void TransformConstraint::applyRelativeLocal() { + float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix; + Bone &target = *_target; + if (!target._appliedValid) { + target.updateAppliedTransform(); + } + + for (size_t i = 0; i < _bones.size(); ++i) { + Bone *item = _bones[i]; + Bone &bone = *item; + + if (!bone._appliedValid) { + bone.updateAppliedTransform(); + } + + float rotation = bone._arotation; + if (rotateMix != 0) { + rotation += (target._arotation + _data._offsetRotation) * rotateMix; + } + + float x = bone._ax, y = bone._ay; + if (translateMix != 0) { + x += (target._ax + _data._offsetX) * translateMix; + y += (target._ay + _data._offsetY) * translateMix; + } + + float scaleX = bone._ascaleX, scaleY = bone._ascaleY; + if (scaleMix != 0) { + if (scaleX > 0.00001f) { + scaleX *= ((target._ascaleX - 1 + _data._offsetScaleX) * scaleMix) + 1; + } + + if (scaleY > 0.00001f) { + scaleY *= ((target._ascaleY - 1 + _data._offsetScaleY) * scaleMix) + 1; + } + } + + float shearY = bone._ashearY; + if (shearMix != 0) { + shearY += (target._ashearY + _data._offsetShearY) * shearMix; + } + + bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone._ashearX, shearY); + } +} diff --git a/cocos/editor-support/spine/TransformConstraint.h b/cocos/editor-support/spine/TransformConstraint.h index 0af1d8d5d3de..e17c4a591dfb 100644 --- a/cocos/editor-support/spine/TransformConstraint.h +++ b/cocos/editor-support/spine/TransformConstraint.h @@ -1,81 +1,92 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_TRANSFORMCONSTRAINT_H_ -#define SPINE_TRANSFORMCONSTRAINT_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct spSkeleton; - -typedef struct spTransformConstraint { - spTransformConstraintData* const data; - int bonesCount; - spBone** const bones; - spBone* target; - float rotateMix, translateMix, scaleMix, shearMix; - -#ifdef __cplusplus - spTransformConstraint() : - data(0), - bonesCount(0), - bones(0), - target(0), - rotateMix(0), - translateMix(0), - scaleMix(0), - shearMix(0) { - } -#endif -} spTransformConstraint; - -SP_API spTransformConstraint* spTransformConstraint_create (spTransformConstraintData* data, const struct spSkeleton* skeleton); -SP_API void spTransformConstraint_dispose (spTransformConstraint* self); +#ifndef Spine_TransformConstraint_h +#define Spine_TransformConstraint_h -SP_API void spTransformConstraint_apply (spTransformConstraint* self); +#include -#ifdef SPINE_SHORT_NAMES -typedef spTransformConstraint TransformConstraint; -#define TransformConstraint_create(...) spTransformConstraint_create(__VA_ARGS__) -#define TransformConstraint_dispose(...) spTransformConstraint_dispose(__VA_ARGS__) -#define TransformConstraint_apply(...) spTransformConstraint_apply(__VA_ARGS__) -#endif +#include -#ifdef __cplusplus +namespace spine { + class TransformConstraintData; + class Skeleton; + class Bone; + + class SP_API TransformConstraint : public Constraint { + friend class Skeleton; + friend class TransformConstraintTimeline; + + RTTI_DECL + + public: + TransformConstraint(TransformConstraintData& data, Skeleton& skeleton); + + void apply(); + + virtual void update(); + + virtual int getOrder(); + + TransformConstraintData& getData(); + + Vector& getBones(); + + Bone* getTarget(); + void setTarget(Bone* inValue); + + float getRotateMix(); + void setRotateMix(float inValue); + + float getTranslateMix(); + void setTranslateMix(float inValue); + + float getScaleMix(); + void setScaleMix(float inValue); + + float getShearMix(); + void setShearMix(float inValue); + + private: + TransformConstraintData& _data; + Vector _bones; + Bone* _target; + float _rotateMix, _translateMix, _scaleMix, _shearMix; + + void applyAbsoluteWorld(); + + void applyRelativeWorld(); + + void applyAbsoluteLocal(); + + void applyRelativeLocal(); + }; } -#endif -#endif /* SPINE_TRANSFORMCONSTRAINT_H_ */ +#endif /* Spine_TransformConstraint_h */ diff --git a/cocos/editor-support/spine/TransformConstraintData.cpp b/cocos/editor-support/spine/TransformConstraintData.cpp new file mode 100644 index 000000000000..404b75af2945 --- /dev/null +++ b/cocos/editor-support/spine/TransformConstraintData.cpp @@ -0,0 +1,122 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +#include + +using namespace spine; +TransformConstraintData::TransformConstraintData(const String &name) : + _name(name), + _order(0), + _target(NULL), + _rotateMix(0), + _translateMix(0), + _scaleMix(0), + _shearMix(0), + _offsetRotation(0), + _offsetX(0), + _offsetY(0), + _offsetScaleX(0), + _offsetScaleY(0), + _offsetShearY(0), + _relative(false), + _local(false) { + assert(_name.length() > 0); +} + +const String &TransformConstraintData::getName() { + return _name; +} + +int TransformConstraintData::getOrder() { + return _order; +} + +Vector &TransformConstraintData::getBones() { + return _bones; +} + +BoneData *TransformConstraintData::getTarget() { + return _target; +} + +float TransformConstraintData::getRotateMix() { + return _rotateMix; +} + +float TransformConstraintData::getTranslateMix() { + return _translateMix; +} + +float TransformConstraintData::getScaleMix() { + return _scaleMix; +} + +float TransformConstraintData::getShearMix() { + return _shearMix; +} + +float TransformConstraintData::getOffsetRotation() { + return _offsetRotation; +} + +float TransformConstraintData::getOffsetX() { + return _offsetX; +} + +float TransformConstraintData::getOffsetY() { + return _offsetY; +} + +float TransformConstraintData::getOffsetScaleX() { + return _offsetScaleX; +} + +float TransformConstraintData::getOffsetScaleY() { + return _offsetScaleY; +} + +float TransformConstraintData::getOffsetShearY() { + return _offsetShearY; +} + +bool TransformConstraintData::isRelative() { + return _relative; +} + +bool TransformConstraintData::isLocal() { + return _local; +} diff --git a/cocos/editor-support/spine/TransformConstraintData.h b/cocos/editor-support/spine/TransformConstraintData.h index 34920b877dbc..585ca641b1ed 100644 --- a/cocos/editor-support/spine/TransformConstraintData.h +++ b/cocos/editor-support/spine/TransformConstraintData.h @@ -1,87 +1,81 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_TRANSFORMCONSTRAINTDATA_H_ -#define SPINE_TRANSFORMCONSTRAINTDATA_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spTransformConstraintData { - const char* const name; - int order; - int bonesCount; - spBoneData** const bones; - spBoneData* target; - float rotateMix, translateMix, scaleMix, shearMix; - float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; - int /*boolean*/ relative; - int /*boolean*/ local; - -#ifdef __cplusplus - spTransformConstraintData() : - name(0), - bonesCount(0), - bones(0), - target(0), - rotateMix(0), - translateMix(0), - scaleMix(0), - shearMix(0), - offsetRotation(0), - offsetX(0), - offsetY(0), - offsetScaleX(0), - offsetScaleY(0), - offsetShearY(0), - relative(0), - local(0) { - } -#endif -} spTransformConstraintData; +#ifndef Spine_TransformConstraintData_h +#define Spine_TransformConstraintData_h -SP_API spTransformConstraintData* spTransformConstraintData_create (const char* name); -SP_API void spTransformConstraintData_dispose (spTransformConstraintData* self); +#include +#include +#include -#ifdef SPINE_SHORT_NAMES -typedef spTransformConstraintData TransformConstraintData; -#define TransformConstraintData_create(...) spTransformConstraintData_create(__VA_ARGS__) -#define TransformConstraintData_dispose(...) spTransformConstraintData_dispose(__VA_ARGS__) -#endif +namespace spine { + class BoneData; + + class SP_API TransformConstraintData : public SpineObject { + friend class SkeletonBinary; + friend class SkeletonJson; + + friend class TransformConstraint; + friend class Skeleton; + friend class TransformConstraintTimeline; + + public: + explicit TransformConstraintData(const String& name); -#ifdef __cplusplus + const String& getName(); + int getOrder(); + Vector& getBones(); + BoneData* getTarget(); + float getRotateMix(); + float getTranslateMix(); + float getScaleMix(); + float getShearMix(); + + float getOffsetRotation(); + float getOffsetX(); + float getOffsetY(); + float getOffsetScaleX(); + float getOffsetScaleY(); + float getOffsetShearY(); + + bool isRelative(); + bool isLocal(); + + private: + const String _name; + int _order; + Vector _bones; + BoneData* _target; + float _rotateMix, _translateMix, _scaleMix, _shearMix; + float _offsetRotation, _offsetX, _offsetY, _offsetScaleX, _offsetScaleY, _offsetShearY; + bool _relative, _local; + }; } -#endif -#endif /* SPINE_TRANSFORMCONSTRAINTDATA_H_ */ +#endif /* Spine_TransformConstraintData_h */ diff --git a/cocos/editor-support/spine/TransformConstraintTimeline.cpp b/cocos/editor-support/spine/TransformConstraintTimeline.cpp new file mode 100644 index 000000000000..9dccc361b196 --- /dev/null +++ b/cocos/editor-support/spine/TransformConstraintTimeline.cpp @@ -0,0 +1,146 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(TransformConstraintTimeline, CurveTimeline) + +const int TransformConstraintTimeline::ENTRIES = 5; +const int TransformConstraintTimeline::PREV_TIME = -5; +const int TransformConstraintTimeline::PREV_ROTATE = -4; +const int TransformConstraintTimeline::PREV_TRANSLATE = -3; +const int TransformConstraintTimeline::PREV_SCALE = -2; +const int TransformConstraintTimeline::PREV_SHEAR = -1; +const int TransformConstraintTimeline::ROTATE = 1; +const int TransformConstraintTimeline::TRANSLATE = 2; +const int TransformConstraintTimeline::SCALE = 3; +const int TransformConstraintTimeline::SHEAR = 4; + +TransformConstraintTimeline::TransformConstraintTimeline(int frameCount) : CurveTimeline(frameCount), + _transformConstraintIndex(0) { + _frames.setSize(frameCount * ENTRIES, 0); +} + +void TransformConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, + float alpha, MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + TransformConstraint *constraintP = skeleton._transformConstraints[_transformConstraintIndex]; + TransformConstraint &constraint = *constraintP; + + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + constraint._rotateMix = constraint._data._rotateMix; + constraint._translateMix = constraint._data._translateMix; + constraint._scaleMix = constraint._data._scaleMix; + constraint._shearMix = constraint._data._shearMix; + return; + case MixBlend_First: + constraint._rotateMix += (constraint._data._rotateMix - constraint._rotateMix) * alpha; + constraint._translateMix += (constraint._data._translateMix - constraint._translateMix) * alpha; + constraint._scaleMix += (constraint._data._scaleMix - constraint._scaleMix) * alpha; + constraint._shearMix += (constraint._data._shearMix - constraint._shearMix) * alpha; + return; + default: + return; + } + } + + float rotate, translate, scale, shear; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + size_t i = _frames.size(); + rotate = _frames[i + PREV_ROTATE]; + translate = _frames[i + PREV_TRANSLATE]; + scale = _frames[i + PREV_SCALE]; + shear = _frames[i + PREV_SHEAR]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + rotate = _frames[frame + PREV_ROTATE]; + translate = _frames[frame + PREV_TRANSLATE]; + scale = _frames[frame + PREV_SCALE]; + shear = _frames[frame + PREV_SHEAR]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + rotate += (_frames[frame + ROTATE] - rotate) * percent; + translate += (_frames[frame + TRANSLATE] - translate) * percent; + scale += (_frames[frame + SCALE] - scale) * percent; + shear += (_frames[frame + SHEAR] - shear) * percent; + } + + if (blend == MixBlend_Setup) { + TransformConstraintData &data = constraint._data; + constraint._rotateMix = data._rotateMix + (rotate - data._rotateMix) * alpha; + constraint._translateMix = data._translateMix + (translate - data._translateMix) * alpha; + constraint._scaleMix = data._scaleMix + (scale - data._scaleMix) * alpha; + constraint._shearMix = data._shearMix + (shear - data._shearMix) * alpha; + } else { + constraint._rotateMix += (rotate - constraint._rotateMix) * alpha; + constraint._translateMix += (translate - constraint._translateMix) * alpha; + constraint._scaleMix += (scale - constraint._scaleMix) * alpha; + constraint._shearMix += (shear - constraint._shearMix) * alpha; + } +} + +int TransformConstraintTimeline::getPropertyId() { + return ((int) TimelineType_TransformConstraint << 24) + _transformConstraintIndex; +} + +void +TransformConstraintTimeline::setFrame(size_t frameIndex, float time, float rotateMix, float translateMix, float scaleMix, + float shearMix) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + ROTATE] = rotateMix; + _frames[frameIndex + TRANSLATE] = translateMix; + _frames[frameIndex + SCALE] = scaleMix; + _frames[frameIndex + SHEAR] = shearMix; +} diff --git a/cocos/editor-support/spine/TransformConstraintTimeline.h b/cocos/editor-support/spine/TransformConstraintTimeline.h new file mode 100644 index 000000000000..18390f418e96 --- /dev/null +++ b/cocos/editor-support/spine/TransformConstraintTimeline.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TransformConstraintTimeline_h +#define Spine_TransformConstraintTimeline_h + +#include + +namespace spine { + + class SP_API TransformConstraintTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + static const int ENTRIES; + + explicit TransformConstraintTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + void setFrame(size_t frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix); + + private: + static const int PREV_TIME; + static const int PREV_ROTATE; + static const int PREV_TRANSLATE; + static const int PREV_SCALE; + static const int PREV_SHEAR; + static const int ROTATE; + static const int TRANSLATE; + static const int SCALE; + static const int SHEAR; + + Vector _frames; + int _transformConstraintIndex; + }; +} + +#endif /* Spine_TransformConstraintTimeline_h */ diff --git a/cocos/editor-support/spine/TransformMode.h b/cocos/editor-support/spine/TransformMode.h new file mode 100644 index 000000000000..7990bc878f5b --- /dev/null +++ b/cocos/editor-support/spine/TransformMode.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TransformMode_h +#define Spine_TransformMode_h + +namespace spine { + enum TransformMode { + TransformMode_Normal = 0, + TransformMode_OnlyTranslation, + TransformMode_NoRotationOrReflection, + TransformMode_NoScale, + TransformMode_NoScaleOrReflection + }; +} + +#endif /* Spine_TransformMode_h */ diff --git a/cocos/editor-support/spine/TranslateTimeline.cpp b/cocos/editor-support/spine/TranslateTimeline.cpp new file mode 100644 index 000000000000..17811c0bdb48 --- /dev/null +++ b/cocos/editor-support/spine/TranslateTimeline.cpp @@ -0,0 +1,129 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(TranslateTimeline, CurveTimeline) + +const int TranslateTimeline::ENTRIES = 3; +const int TranslateTimeline::PREV_TIME = -3; +const int TranslateTimeline::PREV_X = -2; +const int TranslateTimeline::PREV_Y = -1; +const int TranslateTimeline::X = 1; +const int TranslateTimeline::Y = 2; + +TranslateTimeline::TranslateTimeline(int frameCount) : CurveTimeline(frameCount), _boneIndex(0) { + _frames.ensureCapacity(frameCount * ENTRIES); + _frames.setSize(frameCount * ENTRIES, 0); +} + +TranslateTimeline::~TranslateTimeline() { +} + +void TranslateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + Bone *boneP = skeleton._bones[_boneIndex]; + Bone &bone = *boneP; + + if (time < _frames[0]) { + switch (blend) { + case MixBlend_Setup: + bone._x = bone._data._x; + bone._y = bone._data._y; + return; + case MixBlend_First: + bone._x += (bone._data._x - bone._x) * alpha; + bone._y += (bone._data._y - bone._y) * alpha; + default: {} + } + return; + } + + float x, y; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + x = _frames[_frames.size() + PREV_X]; + y = _frames[_frames.size() + PREV_Y]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + x = _frames[frame + PREV_X]; + y = _frames[frame + PREV_Y]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + x += (_frames[frame + X] - x) * percent; + y += (_frames[frame + Y] - y) * percent; + } + + switch (blend) { + case MixBlend_Setup: + bone._x = bone._data._x + x * alpha; + bone._y = bone._data._y + y * alpha; + break; + case MixBlend_First: + case MixBlend_Replace: + bone._x += (bone._data._x + x - bone._x) * alpha; + bone._y += (bone._data._y + y - bone._y) * alpha; + break; + case MixBlend_Add: + bone._x += x * alpha; + bone._y += y * alpha; + } +} + +int TranslateTimeline::getPropertyId() { + return ((int) TimelineType_Translate << 24) + _boneIndex; +} + +void TranslateTimeline::setFrame(int frameIndex, float time, float x, float y) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + X] = x; + _frames[frameIndex + Y] = y; +} diff --git a/cocos/editor-support/spine/TranslateTimeline.h b/cocos/editor-support/spine/TranslateTimeline.h new file mode 100644 index 000000000000..8e4d2cdb6684 --- /dev/null +++ b/cocos/editor-support/spine/TranslateTimeline.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TranslateTimeline_h +#define Spine_TranslateTimeline_h + +#include + +#include +#include + +namespace spine { + + class SP_API TranslateTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + static const int ENTRIES; + + explicit TranslateTimeline(int frameCount); + + virtual ~TranslateTimeline(); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float x, float y); + + protected: + static const int PREV_TIME; + static const int PREV_X; + static const int PREV_Y; + static const int X; + static const int Y; + + Vector _frames; + int _boneIndex; + }; +} + +#endif /* Spine_TranslateTimeline_h */ diff --git a/cocos/editor-support/spine/Triangulator.cpp b/cocos/editor-support/spine/Triangulator.cpp new file mode 100644 index 000000000000..e456d454d15c --- /dev/null +++ b/cocos/editor-support/spine/Triangulator.cpp @@ -0,0 +1,300 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +using namespace spine; + +Triangulator::~Triangulator() { + ContainerUtil::cleanUpVectorOfPointers(_convexPolygons); + ContainerUtil::cleanUpVectorOfPointers(_convexPolygonsIndices); +} + +Vector &Triangulator::triangulate(Vector &vertices) { + size_t vertexCount = vertices.size() >> 1; + + Vector &indices = _indices; + indices.clear(); + indices.ensureCapacity(vertexCount); + indices.setSize(vertexCount, 0); + for (size_t i = 0; i < vertexCount; ++i) { + indices[i] = i; + } + + Vector &isConcaveArray = _isConcaveArray; + isConcaveArray.ensureCapacity(vertexCount); + isConcaveArray.setSize(vertexCount, 0); + for (size_t i = 0, n = vertexCount; i < n; ++i) { + isConcaveArray[i] = isConcave(i, vertexCount, vertices, indices); + } + + Vector &triangles = _triangles; + triangles.clear(); + triangles.ensureCapacity(MathUtil::max((int)0, (int)vertexCount - 2) << 2); + + while (vertexCount > 3) { + // Find ear tip. + size_t previous = vertexCount - 1, i = 0, next = 1; + + // outer: + while (true) { + if (!isConcaveArray[i]) { + int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1; + float p1x = vertices[p1], p1y = vertices[p1 + 1]; + float p2x = vertices[p2], p2y = vertices[p2 + 1]; + float p3x = vertices[p3], p3y = vertices[p3 + 1]; + for (size_t ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) { + if (!isConcaveArray[ii]) { + continue; + } + + int v = indices[ii] << 1; + float &vx = vertices[v], vy = vertices[v + 1]; + if (positiveArea(p3x, p3y, p1x, p1y, vx, vy)) { + if (positiveArea(p1x, p1y, p2x, p2y, vx, vy)) { + if (positiveArea(p2x, p2y, p3x, p3y, vx, vy)) { + goto break_outer; // break outer; + } + } + } + } + break; + } + break_outer: + + if (next == 0) { + do { + if (!isConcaveArray[i]) { + break; + } + i--; + } while (i > 0); + break; + } + + previous = i; + i = next; + next = (next + 1) % vertexCount; + } + + // Cut ear tip. + triangles.add(indices[(vertexCount + i - 1) % vertexCount]); + triangles.add(indices[i]); + triangles.add(indices[(i + 1) % vertexCount]); + indices.removeAt(i); + isConcaveArray.removeAt(i); + vertexCount--; + + int previousIndex = (vertexCount + i - 1) % vertexCount; + int nextIndex = i == vertexCount ? 0 : i; + isConcaveArray[previousIndex] = isConcave(previousIndex, vertexCount, vertices, indices); + isConcaveArray[nextIndex] = isConcave(nextIndex, vertexCount, vertices, indices); + } + + if (vertexCount == 3) { + triangles.add(indices[2]); + triangles.add(indices[0]); + triangles.add(indices[1]); + } + + return triangles; +} + +Vector *> &Triangulator::decompose(Vector &vertices, Vector &triangles) { + Vector *> &convexPolygons = _convexPolygons; + for (size_t i = 0, n = convexPolygons.size(); i < n; ++i) { + _polygonPool.free(convexPolygons[i]); + } + convexPolygons.clear(); + + Vector *> &convexPolygonsIndices = _convexPolygonsIndices; + for (size_t i = 0, n = convexPolygonsIndices.size(); i < n; ++i) { + _polygonIndicesPool.free(convexPolygonsIndices[i]); + } + convexPolygonsIndices.clear(); + + Vector *polygonIndices = _polygonIndicesPool.obtain(); + polygonIndices->clear(); + + Vector *polygon = _polygonPool.obtain(); + polygon->clear(); + + // Merge subsequent triangles if they form a triangle fan. + int fanBaseIndex = -1, lastwinding = 0; + for (size_t i = 0, n = triangles.size(); i < n; i += 3) { + int t1 = triangles[i] << 1, t2 = triangles[i + 1] << 1, t3 = triangles[i + 2] << 1; + float x1 = vertices[t1], y1 = vertices[t1 + 1]; + float x2 = vertices[t2], y2 = vertices[t2 + 1]; + float x3 = vertices[t3], y3 = vertices[t3 + 1]; + + // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan). + bool merged = false; + if (fanBaseIndex == t1) { + size_t o = polygon->size() - 4; + Vector &p = *polygon; + int winding1 = winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3); + int winding2 = winding(x3, y3, p[0], p[1], p[2], p[3]); + if (winding1 == lastwinding && winding2 == lastwinding) { + polygon->add(x3); + polygon->add(y3); + polygonIndices->add(t3); + merged = true; + } + } + + // Otherwise make this triangle the new base. + if (!merged) { + if (polygon->size() > 0) { + convexPolygons.add(polygon); + convexPolygonsIndices.add(polygonIndices); + } else { + _polygonPool.free(polygon); + _polygonIndicesPool.free(polygonIndices); + } + + polygon = _polygonPool.obtain(); + polygon->clear(); + polygon->add(x1); + polygon->add(y1); + polygon->add(x2); + polygon->add(y2); + polygon->add(x3); + polygon->add(y3); + polygonIndices = _polygonIndicesPool.obtain(); + polygonIndices->clear(); + polygonIndices->add(t1); + polygonIndices->add(t2); + polygonIndices->add(t3); + lastwinding = winding(x1, y1, x2, y2, x3, y3); + fanBaseIndex = t1; + } + } + + if (polygon->size() > 0) { + convexPolygons.add(polygon); + convexPolygonsIndices.add(polygonIndices); + } + + // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans. + for (size_t i = 0, n = convexPolygons.size(); i < n; ++i) { + polygonIndices = convexPolygonsIndices[i]; + + if (polygonIndices->size() == 0) continue; + int firstIndex = (*polygonIndices)[0]; + int lastIndex = (*polygonIndices)[polygonIndices->size() - 1]; + + polygon = convexPolygons[i]; + size_t o = polygon->size() - 4; + Vector &p = *polygon; + float prevPrevX = p[o], prevPrevY = p[o + 1]; + float prevX = p[o + 2], prevY = p[o + 3]; + float firstX = p[0], firstY = p[1]; + float secondX = p[2], secondY = p[3]; + int winding0 = winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); + + for (size_t ii = 0; ii < n; ++ii) { + if (ii == i) { + continue; + } + + Vector *otherIndicesP = convexPolygonsIndices[ii]; + Vector &otherIndices = *otherIndicesP; + + if (otherIndices.size() != 3) { + continue; + } + + int otherFirstIndex = otherIndices[0]; + int otherSecondIndex = otherIndices[1]; + int otherLastIndex = otherIndices[2]; + + Vector *otherPolyP = convexPolygons[ii]; + Vector &otherPoly = *otherPolyP; + + float x3 = otherPoly[otherPoly.size() - 2], y3 = otherPoly[otherPoly.size() - 1]; + + if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) { + continue; + } + + int winding1 = winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3); + int winding2 = winding(x3, y3, firstX, firstY, secondX, secondY); + if (winding1 == winding0 && winding2 == winding0) { + otherPoly.clear(); + otherIndices.clear(); + polygon->add(x3); + polygon->add(y3); + polygonIndices->add(otherLastIndex); + prevPrevX = prevX; + prevPrevY = prevY; + prevX = x3; + prevY = y3; + ii = 0; + } + } + } + + // Remove empty polygons that resulted from the merge step above. + for (int i = (int)convexPolygons.size() - 1; i >= 0; --i) { + polygon = convexPolygons[i]; + if (polygon->size() == 0) { + convexPolygons.removeAt(i); + _polygonPool.free(polygon); + polygonIndices = convexPolygonsIndices[i]; + convexPolygonsIndices.removeAt(i); + _polygonIndicesPool.free(polygonIndices); + } + } + + return convexPolygons; +} + +bool Triangulator::isConcave(int index, int vertexCount, Vector &vertices, Vector &indices) { + int previous = indices[(vertexCount + index - 1) % vertexCount] << 1; + int current = indices[index] << 1; + int next = indices[(index + 1) % vertexCount] << 1; + + return !positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], + vertices[next], vertices[next + 1]); +} + +bool Triangulator::positiveArea(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0; +} + +int Triangulator::winding(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + float px = p2x - p1x, py = p2y - p1y; + + return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1; +} diff --git a/cocos/editor-support/spine/Triangulator.h b/cocos/editor-support/spine/Triangulator.h index 69863b2f78c7..291e1beb5fb7 100644 --- a/cocos/editor-support/spine/Triangulator.h +++ b/cocos/editor-support/spine/Triangulator.h @@ -1,63 +1,64 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_TRIANGULATOR_H -#define SPINE_TRIANGULATOR_H +#ifndef Spine_Triangulator_h +#define Spine_Triangulator_h + +#include +#include + +namespace spine { +class SP_API Triangulator : public SpineObject { +public: + ~Triangulator(); -#include -#include + Vector &triangulate(Vector &vertices); -#ifdef __cplusplus -extern "C" { -#endif + Vector< Vector* > &decompose(Vector &vertices, Vector &triangles); -typedef struct spTriangulator { - spArrayFloatArray* convexPolygons; - spArrayShortArray* convexPolygonsIndices; +private: + Vector* > _convexPolygons; + Vector* > _convexPolygonsIndices; - spShortArray* indicesArray; - spIntArray* isConcaveArray; - spShortArray* triangles; + Vector _indices; + Vector _isConcaveArray; + Vector _triangles; - spArrayFloatArray* polygonPool; - spArrayShortArray* polygonIndicesPool; -} spTriangulator; + Pool > _polygonPool; + Pool > _polygonIndicesPool; -SP_API spTriangulator* spTriangulator_create(); -SP_API spShortArray* spTriangulator_triangulate(spTriangulator* self, spFloatArray* verticesArray); -SP_API spArrayFloatArray* spTriangulator_decompose(spTriangulator* self, spFloatArray* verticesArray, spShortArray* triangles); -SP_API void spTriangulator_dispose(spTriangulator* self); + static bool isConcave(int index, int vertexCount, Vector &vertices, Vector &indices); + static bool positiveArea(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y); -#ifdef __cplusplus + static int winding(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y); +}; } -#endif -#endif /* SPINE_TRIANGULATOR_H_ */ +#endif /* Spine_Triangulator_h */ diff --git a/cocos/editor-support/spine/TwoColorTimeline.cpp b/cocos/editor-support/spine/TwoColorTimeline.cpp new file mode 100644 index 000000000000..8060d8e035f0 --- /dev/null +++ b/cocos/editor-support/spine/TwoColorTimeline.cpp @@ -0,0 +1,180 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(TwoColorTimeline, CurveTimeline) + +const int TwoColorTimeline::ENTRIES = 8; +const int TwoColorTimeline::PREV_TIME = -8; +const int TwoColorTimeline::PREV_R = -7; +const int TwoColorTimeline::PREV_G = -6; +const int TwoColorTimeline::PREV_B = -5; +const int TwoColorTimeline::PREV_A = -4; +const int TwoColorTimeline::PREV_R2 = -3; +const int TwoColorTimeline::PREV_G2 = -2; +const int TwoColorTimeline::PREV_B2 = -1; +const int TwoColorTimeline::R = 1; +const int TwoColorTimeline::G = 2; +const int TwoColorTimeline::B = 3; +const int TwoColorTimeline::A = 4; +const int TwoColorTimeline::R2 = 5; +const int TwoColorTimeline::G2 = 6; +const int TwoColorTimeline::B2 = 7; + +TwoColorTimeline::TwoColorTimeline(int frameCount) : CurveTimeline(frameCount), _slotIndex(0) { + _frames.ensureCapacity(frameCount * ENTRIES); + _frames.setSize(frameCount * ENTRIES, 0); +} + +void TwoColorTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + Slot *slotP = skeleton._slots[_slotIndex]; + Slot &slot = *slotP; + + if (time < _frames[0]) { + // Time is before first frame. + switch (blend) { + case MixBlend_Setup: + slot.getColor().set(slot.getData().getColor()); + slot.getDarkColor().set(slot.getData().getDarkColor()); + return; + case MixBlend_First: { + Color &color = slot.getColor(); + color.r += (color.r - slot._data.getColor().r) * alpha; + color.g += (color.g - slot._data.getColor().g) * alpha; + color.b += (color.b - slot._data.getColor().b) * alpha; + color.a += (color.a - slot._data.getColor().a) * alpha; + + Color &darkColor = slot.getDarkColor(); + darkColor.r += (darkColor.r - slot._data.getDarkColor().r) * alpha; + darkColor.g += (darkColor.g - slot._data.getDarkColor().g) * alpha; + darkColor.b += (darkColor.b - slot._data.getDarkColor().b) * alpha; + return; + } + default: + return; + } + } + + float r, g, b, a, r2, g2, b2; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + size_t i = _frames.size(); + r = _frames[i + PREV_R]; + g = _frames[i + PREV_G]; + b = _frames[i + PREV_B]; + a = _frames[i + PREV_A]; + r2 = _frames[i + PREV_R2]; + g2 = _frames[i + PREV_G2]; + b2 = _frames[i + PREV_B2]; + } else { + // Interpolate between the previous frame and the current frame. + size_t frame = (size_t)Animation::binarySearch(_frames, time, ENTRIES); + r = _frames[frame + PREV_R]; + g = _frames[frame + PREV_G]; + b = _frames[frame + PREV_B]; + a = _frames[frame + PREV_A]; + r2 = _frames[frame + PREV_R2]; + g2 = _frames[frame + PREV_G2]; + b2 = _frames[frame + PREV_B2]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + r += (_frames[frame + R] - r) * percent; + g += (_frames[frame + G] - g) * percent; + b += (_frames[frame + B] - b) * percent; + a += (_frames[frame + A] - a) * percent; + r2 += (_frames[frame + R2] - r2) * percent; + g2 += (_frames[frame + G2] - g2) * percent; + b2 += (_frames[frame + B2] - b2) * percent; + } + + if (alpha == 1) { + Color &color = slot.getColor(); + color.set(r, g, b, a); + + Color &darkColor = slot.getDarkColor(); + darkColor.set(r2, g2, b2, 1); + } else { + Color &light = slot._color; + Color &dark = slot._darkColor; + if (blend == MixBlend_Setup) { + light.set(slot._data._color); + dark.set(slot._data._darkColor); + } + light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha); + dark.add((r2 - dark.r) * alpha, (g2 - dark.g) * alpha, (b2 - dark.b) * alpha, 0); + } +} + +int TwoColorTimeline::getPropertyId() { + return ((int) TimelineType_TwoColor << 24) + _slotIndex; +} + +void TwoColorTimeline::setFrame(int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, + float b2) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + R] = r; + _frames[frameIndex + G] = g; + _frames[frameIndex + B] = b; + _frames[frameIndex + A] = a; + _frames[frameIndex + R2] = r2; + _frames[frameIndex + G2] = g2; + _frames[frameIndex + B2] = b2; +} + +int TwoColorTimeline::getSlotIndex() { + return _slotIndex; +} + +void TwoColorTimeline::setSlotIndex(int inValue) { + assert(inValue >= 0); + _slotIndex = inValue; +} diff --git a/cocos/editor-support/spine/TwoColorTimeline.h b/cocos/editor-support/spine/TwoColorTimeline.h new file mode 100644 index 000000000000..0b852bd5ff8f --- /dev/null +++ b/cocos/editor-support/spine/TwoColorTimeline.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TwoColorTimeline_h +#define Spine_TwoColorTimeline_h + +#include + +namespace spine { + + class SP_API TwoColorTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + static const int ENTRIES; + + explicit TwoColorTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixBlend blend, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2); + + int getSlotIndex(); + void setSlotIndex(int inValue); + + private: + static const int PREV_TIME; + static const int PREV_R; + static const int PREV_G; + static const int PREV_B; + static const int PREV_A; + static const int PREV_R2; + static const int PREV_G2; + static const int PREV_B2; + static const int R; + static const int G; + static const int B; + static const int A; + static const int R2; + static const int G2; + static const int B2; + + Vector _frames; // time, r, g, b, a, r2, g2, b2, ... + int _slotIndex; + }; +} + +#endif /* Spine_TwoColorTimeline_h */ diff --git a/cocos/editor-support/spine/Updatable.cpp b/cocos/editor-support/spine/Updatable.cpp new file mode 100644 index 000000000000..abdb02b4494c --- /dev/null +++ b/cocos/editor-support/spine/Updatable.cpp @@ -0,0 +1,44 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +using namespace spine; + +RTTI_IMPL_NOPARENT(Updatable) + +Updatable::Updatable() { +} + +Updatable::~Updatable() { +} diff --git a/cocos/editor-support/spine/Updatable.h b/cocos/editor-support/spine/Updatable.h new file mode 100644 index 000000000000..21b5e9b173f5 --- /dev/null +++ b/cocos/editor-support/spine/Updatable.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Updatable_h +#define Spine_Updatable_h + +#include +#include + +namespace spine { +class SP_API Updatable : public SpineObject { +RTTI_DECL + +public: + Updatable(); + + virtual ~Updatable(); + + virtual void update() = 0; +}; +} + +#endif /* Spine_Updatable_h */ diff --git a/cocos/editor-support/spine/Vector.h b/cocos/editor-support/spine/Vector.h new file mode 100644 index 000000000000..9e5c0c0d000e --- /dev/null +++ b/cocos/editor-support/spine/Vector.h @@ -0,0 +1,222 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Vector_h +#define Spine_Vector_h + +#include +#include +#include +#include +#include +#include + +namespace spine { +template +class SP_API Vector : public SpineObject { +public: + Vector() : _size(0), _capacity(0), _buffer(NULL) { + } + + Vector(const Vector &inVector) : _size(inVector._size), _capacity(inVector._capacity), _buffer(NULL) { + if (_capacity > 0) { + _buffer = allocate(_capacity); + for (size_t i = 0; i < _size; ++i) { + construct(_buffer + i, inVector._buffer[i]); + } + } + } + + ~Vector() { + clear(); + deallocate(_buffer); + } + + inline void clear() { + for (size_t i = 0; i < _size; ++i) { + destroy(_buffer + (_size - 1 - i)); + } + + _size = 0; + } + + inline size_t getCapacity() const { + return _capacity; + } + + inline size_t size() const { + return _size; + } + + inline void setSize(size_t newSize, const T &defaultValue) { + assert(newSize >= 0); + size_t oldSize = _size; + _size = newSize; + if (_capacity < newSize) { + _capacity = (int) (_size * 1.75f); + if (_capacity < 8) _capacity = 8; + _buffer = spine::SpineExtension::realloc(_buffer, _capacity, __FILE__, __LINE__); + } + if (oldSize < _size) { + for (size_t i = oldSize; i < _size; i++) { + construct(_buffer + i, defaultValue); + } + } + } + + inline void ensureCapacity(size_t newCapacity = 0) { + if (_capacity >= newCapacity) return; + _capacity = newCapacity; + _buffer = SpineExtension::realloc(_buffer, newCapacity, __FILE__, __LINE__); + } + + inline void add(const T &inValue) { + if (_size == _capacity) { + // inValue might reference an element in this buffer + // When we reallocate, the reference becomes invalid. + // We thus need to create a defensive copy before + // reallocating. + T valueCopy = inValue; + _capacity = (int) (_size * 1.75f); + if (_capacity < 8) _capacity = 8; + _buffer = spine::SpineExtension::realloc(_buffer, _capacity, __FILE__, __LINE__); + construct(_buffer + _size++, valueCopy); + } else { + construct(_buffer + _size++, inValue); + } + } + + inline void addAll(Vector &inValue) { + ensureCapacity(this->size() + inValue.size()); + for (size_t i = 0; i < inValue.size(); i++) { + add(inValue[i]); + } + } + + inline void clearAndAddAll(Vector &inValue) { + this->clear(); + this->addAll(inValue); + } + + inline void removeAt(size_t inIndex) { + assert(inIndex < _size); + + --_size; + + if (inIndex != _size) { + for (size_t i = inIndex; i < _size; ++i) { + std::swap(_buffer[i], _buffer[i + 1]); + } + } + + destroy(_buffer + _size); + } + + inline bool contains(const T &inValue) { + for (size_t i = 0; i < _size; ++i) { + if (_buffer[i] == inValue) { + return true; + } + } + + return false; + } + + inline int indexOf(const T &inValue) { + for (size_t i = 0; i < _size; ++i) { + if (_buffer[i] == inValue) { + return (int)i; + } + } + + return -1; + } + + inline T &operator[](size_t inIndex) { + assert(inIndex < _size); + + return _buffer[inIndex]; + } + + inline friend bool operator==(Vector &lhs, Vector &rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + + for (size_t i = 0, n = lhs.size(); i < n; ++i) { + if (lhs[i] != rhs[i]) { + return false; + } + } + + return true; + } + + inline friend bool operator!=(Vector &lhs, Vector &rhs) { + return !(lhs == rhs); + } + + inline T *buffer() { + return _buffer; + } + +private: + size_t _size; + size_t _capacity; + T *_buffer; + + inline T *allocate(size_t n) { + assert(n > 0); + + T *ptr = SpineExtension::calloc(n, __FILE__, __LINE__); + + assert(ptr); + + return ptr; + } + + inline void deallocate(T *buffer) { + if (_buffer) { + SpineExtension::free(buffer, __FILE__, __LINE__); + } + } + + inline void construct(T *buffer, const T &val) { + new(buffer) T(val); + } + + inline void destroy(T *buffer) { + buffer->~T(); + } + + // Vector &operator=(const Vector &inVector) {}; +}; +} + +#endif /* Spine_Vector_h */ diff --git a/cocos/editor-support/spine/VertexAttachment.cpp b/cocos/editor-support/spine/VertexAttachment.cpp new file mode 100644 index 000000000000..62b84bcc5abd --- /dev/null +++ b/cocos/editor-support/spine/VertexAttachment.cpp @@ -0,0 +1,162 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include + +#include + +#include +#include + +using namespace spine; + +RTTI_IMPL(VertexAttachment, Attachment) + +VertexAttachment::VertexAttachment(const String &name) : Attachment(name), _worldVerticesLength(0), _id(getNextID()) { +} + +VertexAttachment::~VertexAttachment() { +} + +void VertexAttachment::computeWorldVertices(Slot &slot, Vector &worldVertices) { + computeWorldVertices(slot, 0, _worldVerticesLength, worldVertices, 0); +} + +void VertexAttachment::computeWorldVertices(Slot &slot, float *worldVertices) { + computeWorldVertices(slot, 0, _worldVerticesLength, worldVertices, 0); +} + +void VertexAttachment::computeWorldVertices(Slot &slot, size_t start, size_t count, Vector &worldVertices, size_t offset, + size_t stride) { + computeWorldVertices(slot, start, count, worldVertices.buffer(), offset, stride); +} + +void VertexAttachment::computeWorldVertices(Slot &slot, size_t start, size_t count, float *worldVertices, size_t offset, + size_t stride) { + count = offset + (count >> 1) * stride; + Skeleton &skeleton = slot._bone._skeleton; + Vector *deformArray = &slot.getAttachmentVertices(); + Vector *vertices = &_vertices; + Vector &bones = _bones; + if (bones.size() == 0) { + if (deformArray->size() > 0) { + vertices = deformArray; + } + + Bone &bone = slot._bone; + float x = bone._worldX; + float y = bone._worldY; + float a = bone._a, b = bone._b, c = bone._c, d = bone._d; + for (size_t vv = start, w = offset; w < count; vv += 2, w += stride) { + float vx = (*vertices)[vv]; + float vy = (*vertices)[vv + 1]; + worldVertices[w] = vx * a + vy * b + x; + worldVertices[w + 1] = vx * c + vy * d + y; + } + return; + } + + int v = 0, skip = 0; + for (size_t i = 0; i < start; i += 2) { + int n = bones[v]; + v += n + 1; + skip += n; + } + + Vector &skeletonBones = skeleton.getBones(); + if (deformArray->size() == 0) { + for (size_t w = offset, b = skip * 3; w < count; w += stride) { + float wx = 0, wy = 0; + int n = bones[v++]; + n += v; + for (; v < n; v++, b += 3) { + Bone *boneP = skeletonBones[bones[v]]; + Bone &bone = *boneP; + float vx = (*vertices)[b]; + float vy = (*vertices)[b + 1]; + float weight = (*vertices)[b + 2]; + wx += (vx * bone._a + vy * bone._b + bone._worldX) * weight; + wy += (vx * bone._c + vy * bone._d + bone._worldY) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } else { + for (size_t w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { + float wx = 0, wy = 0; + int n = bones[v++]; + n += v; + for (; v < n; v++, b += 3, f += 2) { + Bone *boneP = skeletonBones[bones[v]]; + Bone &bone = *boneP; + float vx = (*vertices)[b] + (*deformArray)[f]; + float vy = (*vertices)[b + 1] + (*deformArray)[f + 1]; + float weight = (*vertices)[b + 2]; + wx += (vx * bone._a + vy * bone._b + bone._worldX) * weight; + wy += (vx * bone._c + vy * bone._d + bone._worldY) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } +} + +bool VertexAttachment::applyDeform(VertexAttachment *sourceAttachment) { + return this == sourceAttachment; +} + +int VertexAttachment::getId() { + return _id; +} + +Vector &VertexAttachment::getBones() { + return _bones; +} + +Vector &VertexAttachment::getVertices() { + return _vertices; +} + +size_t VertexAttachment::getWorldVerticesLength() { + return _worldVerticesLength; +} + +void VertexAttachment::setWorldVerticesLength(size_t inValue) { + _worldVerticesLength = inValue; +} + +int VertexAttachment::getNextID() { + static int nextID = 0; + + return (nextID++ & 65535) << 11; +} diff --git a/cocos/editor-support/spine/VertexAttachment.h b/cocos/editor-support/spine/VertexAttachment.h index 3bb766ade69f..30b7e15989d1 100644 --- a/cocos/editor-support/spine/VertexAttachment.h +++ b/cocos/editor-support/spine/VertexAttachment.h @@ -1,68 +1,90 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_VERTEXATTACHMENT_H_ -#define SPINE_VERTEXATTACHMENT_H_ +#ifndef Spine_VertexAttachment_h +#define Spine_VertexAttachment_h -#include #include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct spVertexAttachment spVertexAttachment; -struct spVertexAttachment { - spAttachment super; - - int bonesCount; - int* bones; - - int verticesCount; - float* vertices; - - int worldVerticesLength; - - int id; -}; -SP_API void spVertexAttachment_computeWorldVertices (spVertexAttachment* self, spSlot* slot, int start, int count, float* worldVertices, int offset, int stride); +#include -#ifdef SPINE_SHORT_NAMES -typedef spVertexAttachment VertexAttachment; -#define VertexAttachment_computeWorldVertices(...) spVertexAttachment_computeWorldVertices(__VA_ARGS__) -#endif +namespace spine { + class Slot; + + /// An attachment with vertices that are transformed by one or more bones and can be deformed by a slot's vertices. + class SP_API VertexAttachment : public Attachment { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class DeformTimeline; + + RTTI_DECL + + public: + explicit VertexAttachment(const String& name); -#ifdef __cplusplus + virtual ~VertexAttachment(); + + void computeWorldVertices(Slot& slot, float* worldVertices); + void computeWorldVertices(Slot& slot, Vector& worldVertices); + + /// Transforms local vertices to world coordinates. + /// @param start The index of the first Vertices value to transform. Each vertex has 2 values, x and y. + /// @param count The number of world vertex values to output. Must be less than or equal to WorldVerticesLength - start. + /// @param worldVertices The output world vertices. Must have a length greater than or equal to offset + count. + /// @param offset The worldVertices index to begin writing values. + /// @param stride The number of worldVertices entries between the value pairs written. + void computeWorldVertices(Slot& slot, size_t start, size_t count, float* worldVertices, size_t offset, size_t stride = 2); + void computeWorldVertices(Slot& slot, size_t start, size_t count, Vector& worldVertices, size_t offset, size_t stride = 2); + + /// @return true if a deform originally applied to the specified attachment should be applied to this attachment. + virtual bool applyDeform(VertexAttachment* sourceAttachment); + + /// Gets a unique ID for this attachment. + int getId(); + + Vector& getBones(); + + Vector& getVertices(); + + size_t getWorldVerticesLength(); + void setWorldVerticesLength(size_t inValue); + + protected: + Vector _bones; + Vector _vertices; + size_t _worldVerticesLength; + + private: + const int _id; + + static int getNextID(); + }; } -#endif -#endif /* SPINE_VERTEXATTACHMENT_H_ */ +#endif /* Spine_VertexAttachment_h */ diff --git a/cocos/editor-support/spine/VertexEffect.cpp b/cocos/editor-support/spine/VertexEffect.cpp new file mode 100644 index 000000000000..ccff77079a78 --- /dev/null +++ b/cocos/editor-support/spine/VertexEffect.cpp @@ -0,0 +1,159 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifdef SPINE_UE4 +#include "SpinePluginPrivatePCH.h" +#endif + +#include +#include +#include + +using namespace spine; + +JitterVertexEffect::JitterVertexEffect(float jitterX, float jitterY): _jitterX(jitterX), _jitterY(jitterY) { +} + +void JitterVertexEffect::begin(Skeleton &skeleton) { + SP_UNUSED(skeleton); +} + +void JitterVertexEffect::transform(float &x, float &y, float &u, float &v, Color &light, Color &dark) { + SP_UNUSED(u); + SP_UNUSED(v); + SP_UNUSED(light); + SP_UNUSED(dark); + float jitterX = _jitterX; + float jitterY = _jitterY; + x += MathUtil::randomTriangular(-jitterX, jitterX); + y += MathUtil::randomTriangular(-jitterX, jitterY); +} + +void JitterVertexEffect::end() { +} + +void JitterVertexEffect::setJitterX(float jitterX) { + _jitterX = jitterX; +} + +float JitterVertexEffect::getJitterX() { + return _jitterX; +} + +void JitterVertexEffect::setJitterY(float jitterY) { + _jitterY = jitterY; +} + +float JitterVertexEffect::getJitterY() { + return _jitterY; +} + +SwirlVertexEffect::SwirlVertexEffect(float radius, Interpolation &interpolation): + _centerX(0), + _centerY(0), + _radius(radius), + _angle(0), + _worldX(0), + _worldY(0), + _interpolation(interpolation) { +} + +void SwirlVertexEffect::begin(Skeleton &skeleton) { + _worldX = skeleton.getX() + _centerX; + _worldY = skeleton.getY() + _centerY; +} + +void SwirlVertexEffect::transform(float &positionX, float &positionY, float &u, float &v, Color &light, Color &dark) { + SP_UNUSED(u); + SP_UNUSED(v); + SP_UNUSED(light); + SP_UNUSED(dark); + + float x = positionX - _worldX; + float y = positionY - _worldY; + float dist = (float)MathUtil::sqrt(x * x + y * y); + if (dist < _radius) { + float theta = _interpolation.interpolate(0, _angle, (_radius - dist) / _radius); + float cos = MathUtil::cos(theta), sin = MathUtil::sin(theta); + positionX = cos * x - sin * y + _worldX; + positionY = sin * x + cos * y + _worldY; + } +} + +void SwirlVertexEffect::end() { + +} + +void SwirlVertexEffect::setCenterX(float centerX) { + _centerX = centerX; +} + +float SwirlVertexEffect::getCenterX() { + return _centerX; +} + +void SwirlVertexEffect::setCenterY(float centerY) { + _centerY = centerY; +} + +float SwirlVertexEffect::getCenterY() { + return _centerY; +} + +void SwirlVertexEffect::setRadius(float radius) { + _radius = radius; +} + +float SwirlVertexEffect::getRadius() { + return _radius; +} + +void SwirlVertexEffect::setAngle(float angle) { + _angle = angle * MathUtil::Deg_Rad; +} + +float SwirlVertexEffect::getAngle() { + return _angle; +} + +void SwirlVertexEffect::setWorldX(float worldX) { + _worldX = worldX; +} + +float SwirlVertexEffect::getWorldX() { + return _worldX; +} + +void SwirlVertexEffect::setWorldY(float worldY) { + _worldY = worldY; +} + +float SwirlVertexEffect::getWorldY() { + return _worldY; +} diff --git a/cocos/editor-support/spine/VertexEffect.h b/cocos/editor-support/spine/VertexEffect.h index 4f670119b3cd..b7e4cc5fc281 100644 --- a/cocos/editor-support/spine/VertexEffect.h +++ b/cocos/editor-support/spine/VertexEffect.h @@ -1,85 +1,105 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef SPINE_VERTEXEFFECT_H_ -#define SPINE_VERTEXEFFECT_H_ +#ifndef Spine_VertexEffect_h +#define Spine_VertexEffect_h + +#include +#include + +namespace spine { + +class Skeleton; +class Color; + +class SP_API VertexEffect: public SpineObject { +public: + virtual void begin(Skeleton& skeleton) = 0; + virtual void transform(float& x, float& y, float &u, float &v, Color &light, Color &dark) = 0; + virtual void end() = 0; +}; + +class SP_API JitterVertexEffect: public VertexEffect { +public: + JitterVertexEffect(float jitterX, float jitterY); -#include -#include -#include + void begin(Skeleton& skeleton); + void transform(float& x, float& y, float &u, float &v, Color &light, Color &dark); + void end(); -#ifdef __cplusplus -extern "C" { -#endif + void setJitterX(float jitterX); + float getJitterX(); -struct spVertexEffect; + void setJitterY(float jitterY); + float getJitterY(); -typedef void (*spVertexEffectBegin)(struct spVertexEffect *self, spSkeleton *skeleton); +protected: + float _jitterX; + float _jitterY; +}; -typedef void (*spVertexEffectTransform)(struct spVertexEffect *self, float *x, float *y, float *u, float *v, - spColor *light, spColor *dark); +class SP_API SwirlVertexEffect: public VertexEffect { +public: + SwirlVertexEffect(float radius, Interpolation &interpolation); -typedef void (*spVertexEffectEnd)(struct spVertexEffect *self); + void begin(Skeleton& skeleton); + void transform(float& x, float& y, float &u, float &v, Color &light, Color &dark); + void end(); -typedef struct spVertexEffect { - spVertexEffectBegin begin; - spVertexEffectTransform transform; - spVertexEffectEnd end; -} spVertexEffect; + void setCenterX(float centerX); + float getCenterX(); -typedef struct spJitterVertexEffect { - spVertexEffect super; - float jitterX; - float jitterY; -} spJitterVertexEffect; + void setCenterY(float centerY); + float getCenterY(); -typedef struct spSwirlVertexEffect { - spVertexEffect super; - float centerX; - float centerY; - float radius; - float angle; - float worldX; - float worldY; -} spSwirlVertexEffect; + void setRadius(float radius); + float getRadius(); -SP_API spJitterVertexEffect *spJitterVertexEffect_create(float jitterX, float jitterY); + void setAngle(float angle); + float getAngle(); -SP_API void spJitterVertexEffect_dispose(spJitterVertexEffect *effect); + void setWorldX(float worldX); + float getWorldX(); -SP_API spSwirlVertexEffect *spSwirlVertexEffect_create(float radius); + void setWorldY(float worldY); + float getWorldY(); -SP_API void spSwirlVertexEffect_dispose(spSwirlVertexEffect *effect); +protected: + float _centerX; + float _centerY; + float _radius; + float _angle; + float _worldX; + float _worldY; -#ifdef __cplusplus + Interpolation& _interpolation; +}; } -#endif -#endif /* SPINE_VERTEX_EFFECT_H_ */ +#endif /* Spine_VertexEffect_h */ diff --git a/cocos/editor-support/spine/Vertices.h b/cocos/editor-support/spine/Vertices.h new file mode 100644 index 000000000000..1f7878abfcc9 --- /dev/null +++ b/cocos/editor-support/spine/Vertices.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Vertices_h +#define Spine_Vertices_h + +#include + +namespace spine { +class SP_API Vertices : public SpineObject { +public: + Vector _bones; + Vector _vertices; +}; +} + +#endif /* Spine_Vertices_h */ diff --git a/cocos/editor-support/spine/dll.h b/cocos/editor-support/spine/dll.h index ed9c328b54ed..cef97153c00b 100644 --- a/cocos/editor-support/spine/dll.h +++ b/cocos/editor-support/spine/dll.h @@ -1,48 +1,51 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef SPINE_SHAREDLIB_H #define SPINE_SHAREDLIB_H -#define SP_API - #ifdef _WIN32 - #define DLLIMPORT __declspec(dllimport) - #define DLLEXPORT __declspec(dllexport) +#define DLLIMPORT __declspec(dllimport) +#define DLLEXPORT __declspec(dllexport) #else - #define DLLIMPORT - #define DLLEXPORT +#ifndef DLLIMPORT +#define DLLIMPORT +#endif +#ifndef DLLEXPORT +#define DLLEXPORT +#endif #endif #ifdef SPINEPLUGIN_API - #define SP_API SPINEPLUGIN_API +#define SP_API SPINEPLUGIN_API +#else +#define SP_API #endif #endif /* SPINE_SHAREDLIB_H */ diff --git a/cocos/editor-support/spine/extension.h b/cocos/editor-support/spine/extension.h index 0823b5667cb4..c442ca98d49d 100644 --- a/cocos/editor-support/spine/extension.h +++ b/cocos/editor-support/spine/extension.h @@ -1,318 +1,117 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -/* - Implementation notes: - - - An OOP style is used where each "class" is made up of a struct and a number of functions prefixed with the struct name. - - - struct fields that are const are readonly. Either they are set in a create function and can never be changed, or they can only - be changed by calling a function. - - - Inheritance is done using a struct field named "super" as the first field, allowing the struct to be cast to its "super class". - This works because a pointer to a struct is guaranteed to be a pointer to the first struct field. - - - Classes intended for inheritance provide init/deinit functions which subclasses must call in their create/dispose functions. - - - Polymorphism is done by a base class providing function pointers in its init function. The public API delegates to these - function pointers. - - - Subclasses do not provide a dispose function, instead the base class' dispose function should be used, which will delegate to - a dispose function pointer. - - - Classes not designed for inheritance cannot be extended because they may use an internal subclass to hide private data and don't - expose function pointers. - - - The public API hides implementation details, such as init/deinit functions. An internal API is exposed by extension.h to allow - classes to be extended. Internal functions begin with underscore (_). - - - OOP in C tends to lose type safety. Macros for casting are provided in extension.h to give context for why a cast is being done. - - - If SPINE_SHORT_NAMES is defined, the "sp" prefix for all class names is optional. - */ - -#ifndef SPINE_EXTENSION_H_ -#define SPINE_EXTENSION_H_ - -/* All allocation uses these. */ -#define MALLOC(TYPE,COUNT) ((TYPE*)_spMalloc(sizeof(TYPE) * (COUNT), __FILE__, __LINE__)) -#define CALLOC(TYPE,COUNT) ((TYPE*)_spCalloc(COUNT, sizeof(TYPE), __FILE__, __LINE__)) -#define REALLOC(PTR,TYPE,COUNT) ((TYPE*)_spRealloc(PTR, sizeof(TYPE) * (COUNT))) -#define NEW(TYPE) CALLOC(TYPE,1) - -/* Gets the direct super class. Type safe. */ -#define SUPER(VALUE) (&VALUE->super) - -/* Cast to a super class. Not type safe, use with care. Prefer SUPER() where possible. */ -#define SUPER_CAST(TYPE,VALUE) ((TYPE*)VALUE) - -/* Cast to a sub class. Not type safe, use with care. */ -#define SUB_CAST(TYPE,VALUE) ((TYPE*)VALUE) - -/* Casts away const. Can be used as an lvalue. Not type safe, use with care. */ -#define CONST_CAST(TYPE,VALUE) (*(TYPE*)&VALUE) - -/* Gets the vtable for the specified type. Not type safe, use with care. */ -#define VTABLE(TYPE,VALUE) ((_##TYPE##Vtable*)((TYPE*)VALUE)->vtable) - -/* Frees memory. Can be used on const types. */ -#define FREE(VALUE) _spFree((void*)VALUE) - -/* Allocates a new char[], assigns it to TO, and copies FROM to it. Can be used on const types. */ -#define MALLOC_STR(TO,FROM) strcpy(CONST_CAST(char*, TO) = (char*)MALLOC(char, strlen(FROM) + 1), FROM) - -#define PI 3.1415926535897932385f -#define PI2 (PI * 2) -#define DEG_RAD (PI / 180) -#define RAD_DEG (180 / PI) - -#define ABS(A) ((A) < 0? -(A): (A)) -#define SIGNUM(A) ((A) < 0? -1: (A) > 0 ? 1 : 0) - -#ifdef __STDC_VERSION__ -#define FMOD(A,B) fmodf(A, B) -#define ATAN2(A,B) atan2f(A, B) -#define SIN(A) sinf(A) -#define COS(A) cosf(A) -#define SQRT(A) sqrtf(A) -#define ACOS(A) acosf(A) -#define POW(A,B) pow(A, B) -#else -#define FMOD(A,B) (float)fmod(A, B) -#define ATAN2(A,B) (float)atan2(A, B) -#define COS(A) (float)cos(A) -#define SIN(A) (float)sin(A) -#define SQRT(A) (float)sqrt(A) -#define ACOS(A) (float)acos(A) -#define POW(A,B) (float)pow(A, B) -#endif - -#define SIN_DEG(A) SIN((A) * DEG_RAD) -#define COS_DEG(A) COS((A) * DEG_RAD) -#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) -#ifndef MIN -#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#endif -#ifndef MAX -#define MAX(x, y) ((x) > (y) ? (x) : (y)) -#endif - -#define UNUSED(x) (void)(x) +#ifndef Spine_Extension_h +#define Spine_Extension_h #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Functions that must be implemented: - */ - -void _spAtlasPage_createTexture (spAtlasPage* self, const char* path); -void _spAtlasPage_disposeTexture (spAtlasPage* self); -char* _spUtil_readFile (const char* path, int* length); - -#ifdef SPINE_SHORT_NAMES -#define _AtlasPage_createTexture(...) _spAtlasPage_createTexture(__VA_ARGS__) -#define _AtlasPage_disposeTexture(...) _spAtlasPage_disposeTexture(__VA_ARGS__) -#define _Util_readFile(...) _spUtil_readFile(__VA_ARGS__) -#endif - -/* - * Internal API available for extension: - */ - -void* _spMalloc (size_t size, const char* file, int line); -void* _spCalloc (size_t num, size_t size, const char* file, int line); -void* _spRealloc(void* ptr, size_t size); -void _spFree (void* ptr); -float _spRandom (); - -void _spSetMalloc (void* (*_malloc) (size_t size)); -void _spSetDebugMalloc (void* (*_malloc) (size_t size, const char* file, int line)); -void _spSetRealloc(void* (*_realloc) (void* ptr, size_t size)); -void _spSetFree (void (*_free) (void* ptr)); -void _spSetRandom(float (*_random) ()); - -char* _spReadFile (const char* path, int* length); +#include +#define SP_UNUSED(x) (void)(x) -/* - * Math utilities - */ -float _spMath_random(float min, float max); -float _spMath_randomTriangular(float min, float max); -float _spMath_randomTriangularWith(float min, float max, float mode); -float _spMath_interpolate(float (*apply) (float a), float start, float end, float a); -float _spMath_pow2_apply(float a); -float _spMath_pow2out_apply(float a); +namespace spine { +class String; -/**/ - -typedef union _spEventQueueItem { - int type; - spTrackEntry* entry; - spEvent* event; -} _spEventQueueItem; - -typedef struct _spAnimationState _spAnimationState; +class SP_API SpineExtension { +public: + template + static T *alloc(size_t num, const char *file, int line) { + return (T *) getInstance()->_alloc(sizeof(T) * num, file, line); + } -typedef struct _spEventQueue { - _spAnimationState* state; - _spEventQueueItem* objects; - int objectsCount; - int objectsCapacity; - int /*boolean*/ drainDisabled; + template + static T *calloc(size_t num, const char *file, int line) { + return (T *) getInstance()->_calloc(sizeof(T) * num, file, line); + } -#ifdef __cplusplus - _spEventQueue() : - state(0), - objects(0), - objectsCount(0), - objectsCapacity(0), - drainDisabled(0) { + template + static T *realloc(T *ptr, size_t num, const char *file, int line) { + return (T *) getInstance()->_realloc(ptr, sizeof(T) * num, file, line); } -#endif -} _spEventQueue; -struct _spAnimationState { - spAnimationState super; + template + static void free(T *ptr, const char *file, int line) { + getInstance()->_free((void *) ptr, file, line); + } - int eventsCount; - spEvent** events; + static char *readFile(const String &path, int *length) { + return getInstance()->_readFile(path, length); + } - _spEventQueue* queue; + static void setInstance(SpineExtension *inSpineExtension); - int* propertyIDs; - int propertyIDsCount; - int propertyIDsCapacity; + static SpineExtension *getInstance(); - int /*boolean*/ animationsChanged; + virtual ~SpineExtension(); -#ifdef __cplusplus - _spAnimationState() : - super(), - eventsCount(0), - events(0), - queue(0), - propertyIDs(0), - propertyIDsCount(0), - propertyIDsCapacity(0), - animationsChanged(0) { - } -#endif -}; + /// Implement this function to use your own memory allocator + virtual void *_alloc(size_t size, const char *file, int line) = 0; + virtual void *_calloc(size_t size, const char *file, int line) = 0; -/**/ + virtual void *_realloc(void *ptr, size_t size, const char *file, int line) = 0; -/* configureAttachment and disposeAttachment may be 0. */ -void _spAttachmentLoader_init (spAttachmentLoader* self, - void (*dispose) (spAttachmentLoader* self), - spAttachment* (*createAttachment) (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name, - const char* path), - void (*configureAttachment) (spAttachmentLoader* self, spAttachment*), - void (*disposeAttachment) (spAttachmentLoader* self, spAttachment*) -); -void _spAttachmentLoader_deinit (spAttachmentLoader* self); -/* Can only be called from createAttachment. */ -void _spAttachmentLoader_setError (spAttachmentLoader* self, const char* error1, const char* error2); -void _spAttachmentLoader_setUnknownTypeError (spAttachmentLoader* self, spAttachmentType type); + /// If you provide a spineAllocFunc, you should also provide a spineFreeFunc + virtual void _free(void *mem, const char *file, int line) = 0; -#ifdef SPINE_SHORT_NAMES -#define _AttachmentLoader_init(...) _spAttachmentLoader_init(__VA_ARGS__) -#define _AttachmentLoader_deinit(...) _spAttachmentLoader_deinit(__VA_ARGS__) -#define _AttachmentLoader_setError(...) _spAttachmentLoader_setError(__VA_ARGS__) -#define _AttachmentLoader_setUnknownTypeError(...) _spAttachmentLoader_setUnknownTypeError(__VA_ARGS__) -#endif + virtual char *_readFile(const String &path, int *length) = 0; -/**/ +protected: + SpineExtension(); -void _spAttachment_init (spAttachment* self, const char* name, spAttachmentType type, -void (*dispose) (spAttachment* self)); -void _spAttachment_deinit (spAttachment* self); -void _spVertexAttachment_init (spVertexAttachment* self); -void _spVertexAttachment_deinit (spVertexAttachment* self); +private: + static SpineExtension *_instance; +}; -#ifdef SPINE_SHORT_NAMES -#define _Attachment_init(...) _spAttachment_init(__VA_ARGS__) -#define _Attachment_deinit(...) _spAttachment_deinit(__VA_ARGS__) -#define _VertexAttachment_deinit(...) _spVertexAttachment_deinit(__VA_ARGS__) -#endif +class SP_API DefaultSpineExtension : public SpineExtension { +public: + DefaultSpineExtension(); -/**/ + virtual ~DefaultSpineExtension(); -void _spTimeline_init (spTimeline* self, spTimelineType type, - void (*dispose) (spTimeline* self), - void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents, - int* eventsCount, float alpha, spMixPose pose, spMixDirection direction), - int (*getPropertyId) (const spTimeline* self)); -void _spTimeline_deinit (spTimeline* self); +protected: + virtual void *_alloc(size_t size, const char *file, int line); -#ifdef SPINE_SHORT_NAMES -#define _Timeline_init(...) _spTimeline_init(__VA_ARGS__) -#define _Timeline_deinit(...) _spTimeline_deinit(__VA_ARGS__) -#endif + virtual void *_calloc(size_t size, const char *file, int line); -/**/ + virtual void *_realloc(void *ptr, size_t size, const char *file, int line); -void _spCurveTimeline_init (spCurveTimeline* self, spTimelineType type, int framesCount, - void (*dispose) (spTimeline* self), - void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction), - int (*getPropertyId) (const spTimeline* self)); -void _spCurveTimeline_deinit (spCurveTimeline* self); -int _spCurveTimeline_binarySearch (float *values, int valuesLength, float target, int step); + virtual void _free(void *mem, const char *file, int line); -#ifdef SPINE_SHORT_NAMES -#define _CurveTimeline_init(...) _spCurveTimeline_init(__VA_ARGS__) -#define _CurveTimeline_deinit(...) _spCurveTimeline_deinit(__VA_ARGS__) -#define _CurveTimeline_binarySearch(...) _spCurveTimeline_binarySearch(__VA_ARGS__) -#endif + virtual char *_readFile(const String &path, int *length); +}; -#ifdef __cplusplus +// This function is to be implemented by engine specific runtimes to provide +// the default extension for that engine. It is called the first time +// SpineExtension::getInstance() is called, when no instance has been set +// yet. +extern SpineExtension *getDefaultExtension(); } -#endif -#endif /* SPINE_EXTENSION_H_ */ +#endif /* Spine_Extension_h */ diff --git a/cocos/editor-support/spine/spine-cocos2dx.cpp b/cocos/editor-support/spine/spine-cocos2dx.cpp index 6e75b4eed841..e409fd379bc7 100644 --- a/cocos/editor-support/spine/spine-cocos2dx.cpp +++ b/cocos/editor-support/spine/spine-cocos2dx.cpp @@ -1,85 +1,171 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include -#include +#include +#include USING_NS_CC; +using namespace spine; + +static void deleteAttachmentVertices (void* vertices) { + delete (AttachmentVertices *) vertices; +} + +static unsigned short quadTriangles[6] = {0, 1, 2, 2, 3, 0}; + +static void setAttachmentVertices(RegionAttachment* attachment) { + AtlasRegion* region = (AtlasRegion*)attachment->getRendererObject(); + AttachmentVertices* attachmentVertices = new AttachmentVertices((Texture2D*)region->page->getRendererObject(), 4, quadTriangles, 6); + V3F_C4B_T2F* vertices = attachmentVertices->_triangles->verts; + for (int i = 0, ii = 0; i < 4; ++i, ii += 2) { + vertices[i].texCoords.u = attachment->getUVs()[ii]; + vertices[i].texCoords.v = attachment->getUVs()[ii + 1]; + } + attachment->setRendererObject(attachmentVertices, deleteAttachmentVertices); +} + +static void setAttachmentVertices(MeshAttachment* attachment) { + AtlasRegion* region = (AtlasRegion*)attachment->getRendererObject(); + AttachmentVertices* attachmentVertices = new AttachmentVertices((Texture2D*)region->page->getRendererObject(), + attachment->getWorldVerticesLength() >> 1, attachment->getTriangles().buffer(), attachment->getTriangles().size()); + V3F_C4B_T2F* vertices = attachmentVertices->_triangles->verts; + for (int i = 0, ii = 0, nn = attachment->getWorldVerticesLength(); ii < nn; ++i, ii += 2) { + vertices[i].texCoords.u = attachment->getUVs()[ii]; + vertices[i].texCoords.v = attachment->getUVs()[ii + 1]; + } + attachment->setRendererObject(attachmentVertices, deleteAttachmentVertices); +} -GLuint wrap (spAtlasWrap wrap) { - return wrap == SP_ATLAS_CLAMPTOEDGE ? GL_CLAMP_TO_EDGE : GL_REPEAT; +Cocos2dAtlasAttachmentLoader::Cocos2dAtlasAttachmentLoader(Atlas* atlas): AtlasAttachmentLoader(atlas) { } -GLuint filter (spAtlasFilter filter) { +Cocos2dAtlasAttachmentLoader::~Cocos2dAtlasAttachmentLoader() { } + +void Cocos2dAtlasAttachmentLoader::configureAttachment(Attachment* attachment) { + if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { + setAttachmentVertices((RegionAttachment*)attachment); + } else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { + setAttachmentVertices((MeshAttachment*)attachment); + } +} + +#if COCOS2D_VERSION >= 0x0040000 + +backend::SamplerAddressMode wrap (TextureWrap wrap) { + return wrap == TextureWrap_ClampToEdge ? backend::SamplerAddressMode::CLAMP_TO_EDGE : backend::SamplerAddressMode::REPEAT; +} + +backend::SamplerFilter filter (TextureFilter filter) { switch (filter) { - case SP_ATLAS_UNKNOWN_FILTER: + case TextureFilter_Unknown: break; - case SP_ATLAS_NEAREST: - return GL_NEAREST; - case SP_ATLAS_LINEAR: - return GL_LINEAR; - case SP_ATLAS_MIPMAP: - return GL_LINEAR_MIPMAP_LINEAR; - case SP_ATLAS_MIPMAP_NEAREST_NEAREST: - return GL_NEAREST_MIPMAP_NEAREST; - case SP_ATLAS_MIPMAP_LINEAR_NEAREST: - return GL_LINEAR_MIPMAP_NEAREST; - case SP_ATLAS_MIPMAP_NEAREST_LINEAR: - return GL_NEAREST_MIPMAP_LINEAR; - case SP_ATLAS_MIPMAP_LINEAR_LINEAR: - return GL_LINEAR_MIPMAP_LINEAR; + case TextureFilter_Nearest: + return backend::SamplerFilter::NEAREST; + case TextureFilter_Linear: + return backend::SamplerFilter::LINEAR; + case TextureFilter_MipMap: + return backend::SamplerFilter::LINEAR; + case TextureFilter_MipMapNearestNearest: + return backend::SamplerFilter::NEAREST; + case TextureFilter_MipMapLinearNearest: + return backend::SamplerFilter::NEAREST; + case TextureFilter_MipMapNearestLinear: + return backend::SamplerFilter::LINEAR; + case TextureFilter_MipMapLinearLinear: + return backend::SamplerFilter::LINEAR; } - return GL_LINEAR; + return backend::SamplerFilter::LINEAR; } -void _spAtlasPage_createTexture (spAtlasPage* self, const char* path) { - Texture2D* texture = Director::getInstance()->getTextureCache()->addImage(path); - CCASSERT(texture != nullptr, "Invalid image"); - texture->retain(); +#else - Texture2D::TexParams textureParams = {filter(self->minFilter), filter(self->magFilter), wrap(self->uWrap), wrap(self->vWrap)}; - texture->setTexParameters(textureParams); +GLuint wrap (TextureWrap wrap) { + return wrap == TextureWrap_ClampToEdge ? GL_CLAMP_TO_EDGE : GL_REPEAT; +} - self->rendererObject = texture; - self->width = texture->getPixelsWide(); - self->height = texture->getPixelsHigh(); +GLuint filter (TextureFilter filter) { + switch (filter) { + case TextureFilter_Unknown: + break; + case TextureFilter_Nearest: + return GL_NEAREST; + case TextureFilter_Linear: + return GL_LINEAR; + case TextureFilter_MipMap: + return GL_LINEAR_MIPMAP_LINEAR; + case TextureFilter_MipMapNearestNearest: + return GL_NEAREST_MIPMAP_NEAREST; + case TextureFilter_MipMapLinearNearest: + return GL_LINEAR_MIPMAP_NEAREST; + case TextureFilter_MipMapNearestLinear: + return GL_NEAREST_MIPMAP_LINEAR; + case TextureFilter_MipMapLinearLinear: + return GL_LINEAR_MIPMAP_LINEAR; + } + return GL_LINEAR; } -void _spAtlasPage_disposeTexture (spAtlasPage* self) { - ((Texture2D*)self->rendererObject)->release(); +#endif + +Cocos2dTextureLoader::Cocos2dTextureLoader() : TextureLoader() { } +Cocos2dTextureLoader::~Cocos2dTextureLoader() { } + +void Cocos2dTextureLoader::load(AtlasPage& page, const spine::String& path) { + Texture2D* texture = Director::getInstance()->getTextureCache()->addImage(path.buffer()); + CCASSERT(texture != nullptr, "Invalid image"); + texture->retain(); +#if COCOS2D_VERSION >= 0x0040000 + Texture2D::TexParams textureParams(filter(page.minFilter), filter(page.magFilter), wrap(page.uWrap), wrap(page.vWrap)); +#else + Texture2D::TexParams textureParams = {filter(page.minFilter), filter(page.magFilter), wrap(page.uWrap), wrap(page.vWrap)}; +#endif + texture->setTexParameters(textureParams); + + page.setRendererObject(texture); + page.width = texture->getPixelsWide(); + page.height = texture->getPixelsHigh(); +} + +void Cocos2dTextureLoader::unload(void* texture) { + ((Texture2D*)texture)->release(); } -char* _spUtil_readFile (const char* path, int* length) { - Data data = FileUtils::getInstance()->getDataFromFile(FileUtils::getInstance()->fullPathForFilename(path)); - if (data.isNull()) return 0; +Cocos2dExtension::Cocos2dExtension() : DefaultSpineExtension() { } + +Cocos2dExtension::~Cocos2dExtension() { } + +char *Cocos2dExtension::_readFile(const spine::String &path, int *length) { + Data data = FileUtils::getInstance()->getDataFromFile(FileUtils::getInstance()->fullPathForFilename(path.buffer())); + if (data.isNull()) return 0; + // avoid buffer overflow (int is shorter than ssize_t in certain platforms) #if COCOS2D_VERSION >= 0x00031200 ssize_t tmpLen; @@ -87,9 +173,14 @@ char* _spUtil_readFile (const char* path, int* length) { *length = static_cast(tmpLen); return ret; #else - *length = static_cast(data.getSize()); - char* bytes = MALLOC(char, *length); - memcpy(bytes, data.getBytes(), *length); - return bytes; + *length = static_cast(data.getSize()); + char* bytes = MALLOC(char, *length); + memcpy(bytes, data.getBytes(), *length); + return bytes; #endif } + +SpineExtension *spine::getDefaultExtension () { + return new Cocos2dExtension(); +} + diff --git a/cocos/editor-support/spine/spine-cocos2dx.h b/cocos/editor-support/spine/spine-cocos2dx.h index 7f09e359ff16..03df19fc498e 100644 --- a/cocos/editor-support/spine/spine-cocos2dx.h +++ b/cocos/editor-support/spine/spine-cocos2dx.h @@ -1,31 +1,30 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef SPINE_COCOS2DX_H_ @@ -33,9 +32,39 @@ #include #include "cocos2d.h" -#include + #include #include #include +namespace spine { + class Cocos2dAtlasAttachmentLoader: public AtlasAttachmentLoader { + public: + Cocos2dAtlasAttachmentLoader(Atlas* atlas); + virtual ~Cocos2dAtlasAttachmentLoader(); + virtual void configureAttachment(Attachment* attachment); + }; + + class Cocos2dTextureLoader: public TextureLoader { + public: + Cocos2dTextureLoader(); + + virtual ~Cocos2dTextureLoader(); + + virtual void load(AtlasPage& page, const String& path); + + virtual void unload(void* texture); + }; + + class Cocos2dExtension: public DefaultSpineExtension { + public: + Cocos2dExtension(); + + virtual ~Cocos2dExtension(); + + protected: + virtual char *_readFile(const String &path, int *length); + }; +} + #endif /* SPINE_COCOS2DX_H_ */ diff --git a/cocos/editor-support/spine/spine.h b/cocos/editor-support/spine/spine.h index 6c85d7cb995d..f2ad93a229d8 100644 --- a/cocos/editor-support/spine/spine.h +++ b/cocos/editor-support/spine/spine.h @@ -1,37 +1,35 @@ /****************************************************************************** - * Spine Runtimes Software License v2.5 + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. + * Copyright (c) 2013-2019, Esoteric Software LLC * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef SPINE_SPINE_H_ #define SPINE_SPINE_H_ -#include #include #include #include @@ -39,25 +37,77 @@ #include #include #include +#include +#include +#include #include #include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include +#include #include -#include #include #include #include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include -#endif /* SPINE_SPINE_H_ */ +#endif diff --git a/cocos/renderer/backend/ProgramState.cpp b/cocos/renderer/backend/ProgramState.cpp index 878d8c5b6ddb..52083d4fa2a8 100644 --- a/cocos/renderer/backend/ProgramState.cpp +++ b/cocos/renderer/backend/ProgramState.cpp @@ -234,7 +234,6 @@ ProgramState *ProgramState::clone() const memcpy(cp->_fragmentUniformBuffer, _fragmentUniformBuffer, _fragmentUniformBufferSize); #endif CC_SAFE_RETAIN(cp->_program); - return cp; } diff --git a/cocos/scripting/lua-bindings/CMakeLists.txt b/cocos/scripting/lua-bindings/CMakeLists.txt index ff22348108b1..678bcc6e89cc 100644 --- a/cocos/scripting/lua-bindings/CMakeLists.txt +++ b/cocos/scripting/lua-bindings/CMakeLists.txt @@ -59,7 +59,7 @@ set(lua_bindings_manual_files manual/network/Lua_web_socket.cpp manual/network/lua_xml_http_request.cpp manual/network/lua_downloader.cpp - #manual/spine/lua_cocos2dx_spine_manual.cpp + manual/spine/lua_cocos2dx_spine_manual.cpp manual/spine/LuaSkeletonAnimation.cpp manual/ui/lua_cocos2dx_ui_manual.cpp manual/audioengine/lua_cocos2dx_audioengine_manual.cpp @@ -90,7 +90,7 @@ set(lua_bindings_auto_files auto/lua_cocos2dx_controller_auto.cpp auto/lua_cocos2dx_extension_auto.cpp auto/lua_cocos2dx_physics_auto.cpp - #auto/lua_cocos2dx_spine_auto.cpp + auto/lua_cocos2dx_spine_auto.cpp auto/lua_cocos2dx_studio_auto.cpp auto/lua_cocos2dx_csloader_auto.cpp auto/lua_cocos2dx_ui_auto.cpp diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_auto.cpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_auto.cpp index 57080f9c3bec..add3fc6ae3fb 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_auto.cpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_auto.cpp @@ -49656,7 +49656,7 @@ int lua_register_cocos2dx_ActionTween(lua_State* tolua_S) return 1; } -int lua_cocos2dx_AtlasNode_updateAtlasValues(lua_State* tolua_S) +int lua_cocos2dx_AtlasNode_getBlendFunc(lua_State* tolua_S) { int argc = 0; cocos2d::AtlasNode* cobj = nullptr; @@ -49676,7 +49676,7 @@ int lua_cocos2dx_AtlasNode_updateAtlasValues(lua_State* tolua_S) #if COCOS2D_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_AtlasNode_updateAtlasValues'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_AtlasNode_getBlendFunc'", nullptr); return 0; } #endif @@ -49686,19 +49686,19 @@ int lua_cocos2dx_AtlasNode_updateAtlasValues(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_AtlasNode_updateAtlasValues'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_AtlasNode_getBlendFunc'", nullptr); return 0; } - cobj->updateAtlasValues(); - lua_settop(tolua_S, 1); + const cocos2d::BlendFunc& ret = cobj->getBlendFunc(); + blendfunc_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.AtlasNode:updateAtlasValues",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.AtlasNode:getBlendFunc",argc, 0); return 0; #if COCOS2D_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_AtlasNode_updateAtlasValues'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_AtlasNode_getBlendFunc'.",&tolua_err); #endif return 0; @@ -49956,7 +49956,7 @@ int lua_cocos2dx_AtlasNode_getTextureAtlas(lua_State* tolua_S) return 0; } -int lua_cocos2dx_AtlasNode_getBlendFunc(lua_State* tolua_S) +int lua_cocos2dx_AtlasNode_updateAtlasValues(lua_State* tolua_S) { int argc = 0; cocos2d::AtlasNode* cobj = nullptr; @@ -49976,7 +49976,7 @@ int lua_cocos2dx_AtlasNode_getBlendFunc(lua_State* tolua_S) #if COCOS2D_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_AtlasNode_getBlendFunc'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_AtlasNode_updateAtlasValues'", nullptr); return 0; } #endif @@ -49986,19 +49986,19 @@ int lua_cocos2dx_AtlasNode_getBlendFunc(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_AtlasNode_getBlendFunc'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_AtlasNode_updateAtlasValues'", nullptr); return 0; } - const cocos2d::BlendFunc& ret = cobj->getBlendFunc(); - blendfunc_to_luaval(tolua_S, ret); + cobj->updateAtlasValues(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.AtlasNode:getBlendFunc",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.AtlasNode:updateAtlasValues",argc, 0); return 0; #if COCOS2D_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_AtlasNode_getBlendFunc'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_AtlasNode_updateAtlasValues'.",&tolua_err); #endif return 0; @@ -50301,13 +50301,13 @@ int lua_register_cocos2dx_AtlasNode(lua_State* tolua_S) tolua_beginmodule(tolua_S,"AtlasNode"); tolua_function(tolua_S,"new",lua_cocos2dx_AtlasNode_constructor); - tolua_function(tolua_S,"updateAtlasValues",lua_cocos2dx_AtlasNode_updateAtlasValues); + tolua_function(tolua_S,"getBlendFunc",lua_cocos2dx_AtlasNode_getBlendFunc); tolua_function(tolua_S,"initWithTileFile",lua_cocos2dx_AtlasNode_initWithTileFile); tolua_function(tolua_S,"setBlendFunc",lua_cocos2dx_AtlasNode_setBlendFunc); tolua_function(tolua_S,"setTextureAtlas",lua_cocos2dx_AtlasNode_setTextureAtlas); tolua_function(tolua_S,"getTexture",lua_cocos2dx_AtlasNode_getTexture); tolua_function(tolua_S,"getTextureAtlas",lua_cocos2dx_AtlasNode_getTextureAtlas); - tolua_function(tolua_S,"getBlendFunc",lua_cocos2dx_AtlasNode_getBlendFunc); + tolua_function(tolua_S,"updateAtlasValues",lua_cocos2dx_AtlasNode_updateAtlasValues); tolua_function(tolua_S,"setTexture",lua_cocos2dx_AtlasNode_setTexture); tolua_function(tolua_S,"initWithTexture",lua_cocos2dx_AtlasNode_initWithTexture); tolua_function(tolua_S,"getQuadsToDraw",lua_cocos2dx_AtlasNode_getQuadsToDraw); @@ -62393,7 +62393,7 @@ int lua_cocos2dx_MotionStreak_reset(lua_State* tolua_S) return 0; } -int lua_cocos2dx_MotionStreak_setTexture(lua_State* tolua_S) +int lua_cocos2dx_MotionStreak_getBlendFunc(lua_State* tolua_S) { int argc = 0; cocos2d::MotionStreak* cobj = nullptr; @@ -62413,32 +62413,29 @@ int lua_cocos2dx_MotionStreak_setTexture(lua_State* tolua_S) #if COCOS2D_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_MotionStreak_setTexture'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_MotionStreak_getBlendFunc'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - cocos2d::Texture2D* arg0; - - ok &= luaval_to_object(tolua_S, 2, "cc.Texture2D",&arg0, "cc.MotionStreak:setTexture"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_MotionStreak_setTexture'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_MotionStreak_getBlendFunc'", nullptr); return 0; } - cobj->setTexture(arg0); - lua_settop(tolua_S, 1); + const cocos2d::BlendFunc& ret = cobj->getBlendFunc(); + blendfunc_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.MotionStreak:setTexture",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.MotionStreak:getBlendFunc",argc, 0); return 0; #if COCOS2D_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_MotionStreak_setTexture'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_MotionStreak_getBlendFunc'.",&tolua_err); #endif return 0; @@ -62640,7 +62637,7 @@ int lua_cocos2dx_MotionStreak_setStartingPositionInitialized(lua_State* tolua_S) return 0; } -int lua_cocos2dx_MotionStreak_getBlendFunc(lua_State* tolua_S) +int lua_cocos2dx_MotionStreak_setTexture(lua_State* tolua_S) { int argc = 0; cocos2d::MotionStreak* cobj = nullptr; @@ -62660,29 +62657,32 @@ int lua_cocos2dx_MotionStreak_getBlendFunc(lua_State* tolua_S) #if COCOS2D_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_MotionStreak_getBlendFunc'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_MotionStreak_setTexture'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + cocos2d::Texture2D* arg0; + + ok &= luaval_to_object(tolua_S, 2, "cc.Texture2D",&arg0, "cc.MotionStreak:setTexture"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_MotionStreak_getBlendFunc'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_MotionStreak_setTexture'", nullptr); return 0; } - const cocos2d::BlendFunc& ret = cobj->getBlendFunc(); - blendfunc_to_luaval(tolua_S, ret); + cobj->setTexture(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.MotionStreak:getBlendFunc",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.MotionStreak:setTexture",argc, 1); return 0; #if COCOS2D_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_MotionStreak_getBlendFunc'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_MotionStreak_setTexture'.",&tolua_err); #endif return 0; @@ -63138,12 +63138,12 @@ int lua_register_cocos2dx_MotionStreak(lua_State* tolua_S) tolua_beginmodule(tolua_S,"MotionStreak"); tolua_function(tolua_S,"new",lua_cocos2dx_MotionStreak_constructor); tolua_function(tolua_S,"reset",lua_cocos2dx_MotionStreak_reset); - tolua_function(tolua_S,"setTexture",lua_cocos2dx_MotionStreak_setTexture); + tolua_function(tolua_S,"getBlendFunc",lua_cocos2dx_MotionStreak_getBlendFunc); tolua_function(tolua_S,"setBlendFunc",lua_cocos2dx_MotionStreak_setBlendFunc); tolua_function(tolua_S,"tintWithColor",lua_cocos2dx_MotionStreak_tintWithColor); tolua_function(tolua_S,"getTexture",lua_cocos2dx_MotionStreak_getTexture); tolua_function(tolua_S,"setStartingPositionInitialized",lua_cocos2dx_MotionStreak_setStartingPositionInitialized); - tolua_function(tolua_S,"getBlendFunc",lua_cocos2dx_MotionStreak_getBlendFunc); + tolua_function(tolua_S,"setTexture",lua_cocos2dx_MotionStreak_setTexture); tolua_function(tolua_S,"isStartingPositionInitialized",lua_cocos2dx_MotionStreak_isStartingPositionInitialized); tolua_function(tolua_S,"isFastMode",lua_cocos2dx_MotionStreak_isFastMode); tolua_function(tolua_S,"getStroke",lua_cocos2dx_MotionStreak_getStroke); @@ -98841,56 +98841,6 @@ int lua_cocos2dx_TMXMapInfo_setTileSize(lua_State* tolua_S) return 0; } -int lua_cocos2dx_TMXMapInfo_initWithTMXFile(lua_State* tolua_S) -{ - int argc = 0; - cocos2d::TMXMapInfo* cobj = nullptr; - bool ok = true; - -#if COCOS2D_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if COCOS2D_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"cc.TMXMapInfo",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (cocos2d::TMXMapInfo*)tolua_tousertype(tolua_S,1,0); - -#if COCOS2D_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_TMXMapInfo_initWithTMXFile'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - std::string arg0; - - ok &= luaval_to_std_string(tolua_S, 2,&arg0, "cc.TMXMapInfo:initWithTMXFile"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_TMXMapInfo_initWithTMXFile'", nullptr); - return 0; - } - bool ret = cobj->initWithTMXFile(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.TMXMapInfo:initWithTMXFile",argc, 1); - return 0; - -#if COCOS2D_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_TMXMapInfo_initWithTMXFile'.",&tolua_err); -#endif - - return 0; -} int lua_cocos2dx_TMXMapInfo_getOrientation(lua_State* tolua_S) { int argc = 0; @@ -99379,49 +99329,52 @@ int lua_cocos2dx_TMXMapInfo_setHexSideLength(lua_State* tolua_S) return 0; } -int lua_cocos2dx_TMXMapInfo_getTilesets(lua_State* tolua_S) +int lua_cocos2dx_TMXMapInfo_initWithTMXFile(lua_State* tolua_S) { int argc = 0; cocos2d::TMXMapInfo* cobj = nullptr; bool ok = true; + #if COCOS2D_DEBUG >= 1 tolua_Error tolua_err; #endif + #if COCOS2D_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"cc.TMXMapInfo",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (cocos2d::TMXMapInfo*)tolua_tousertype(tolua_S,1,0); + #if COCOS2D_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_TMXMapInfo_getTilesets'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_TMXMapInfo_initWithTMXFile'", nullptr); return 0; } #endif + argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 0) { - cocos2d::Vector& ret = cobj->getTilesets(); - ccvector_to_luaval(tolua_S, ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - const cocos2d::Vector& ret = cobj->getTilesets(); - ccvector_to_luaval(tolua_S, ret); - return 1; + if (argc == 1) + { + std::string arg0; + + ok &= luaval_to_std_string(tolua_S, 2,&arg0, "cc.TMXMapInfo:initWithTMXFile"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_TMXMapInfo_initWithTMXFile'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.TMXMapInfo:getTilesets",argc, 0); + bool ret = cobj->initWithTMXFile(arg0); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.TMXMapInfo:initWithTMXFile",argc, 1); return 0; #if COCOS2D_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_TMXMapInfo_getTilesets'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_TMXMapInfo_initWithTMXFile'.",&tolua_err); #endif return 0; @@ -99473,6 +99426,53 @@ int lua_cocos2dx_TMXMapInfo_getParentGID(lua_State* tolua_S) return 0; } +int lua_cocos2dx_TMXMapInfo_getTilesets(lua_State* tolua_S) +{ + int argc = 0; + cocos2d::TMXMapInfo* cobj = nullptr; + bool ok = true; +#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; +#endif + +#if COCOS2D_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"cc.TMXMapInfo",0,&tolua_err)) goto tolua_lerror; +#endif + cobj = (cocos2d::TMXMapInfo*)tolua_tousertype(tolua_S,1,0); +#if COCOS2D_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_TMXMapInfo_getTilesets'", nullptr); + return 0; + } +#endif + argc = lua_gettop(tolua_S)-1; + do{ + if (argc == 0) { + cocos2d::Vector& ret = cobj->getTilesets(); + ccvector_to_luaval(tolua_S, ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 0) { + const cocos2d::Vector& ret = cobj->getTilesets(); + ccvector_to_luaval(tolua_S, ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.TMXMapInfo:getTilesets",argc, 0); + return 0; + +#if COCOS2D_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_TMXMapInfo_getTilesets'.",&tolua_err); +#endif + + return 0; +} int lua_cocos2dx_TMXMapInfo_setParentElement(lua_State* tolua_S) { int argc = 0; @@ -100720,7 +100720,6 @@ int lua_register_cocos2dx_TMXMapInfo(lua_State* tolua_S) tolua_function(tolua_S,"setCurrentString",lua_cocos2dx_TMXMapInfo_setCurrentString); tolua_function(tolua_S,"getHexSideLength",lua_cocos2dx_TMXMapInfo_getHexSideLength); tolua_function(tolua_S,"setTileSize",lua_cocos2dx_TMXMapInfo_setTileSize); - tolua_function(tolua_S,"initWithTMXFile",lua_cocos2dx_TMXMapInfo_initWithTMXFile); tolua_function(tolua_S,"getOrientation",lua_cocos2dx_TMXMapInfo_getOrientation); tolua_function(tolua_S,"setObjectGroups",lua_cocos2dx_TMXMapInfo_setObjectGroups); tolua_function(tolua_S,"setLayers",lua_cocos2dx_TMXMapInfo_setLayers); @@ -100731,8 +100730,9 @@ int lua_register_cocos2dx_TMXMapInfo(lua_State* tolua_S) tolua_function(tolua_S,"getLayers",lua_cocos2dx_TMXMapInfo_getLayers); tolua_function(tolua_S,"getStaggerAxis",lua_cocos2dx_TMXMapInfo_getStaggerAxis); tolua_function(tolua_S,"setHexSideLength",lua_cocos2dx_TMXMapInfo_setHexSideLength); - tolua_function(tolua_S,"getTilesets",lua_cocos2dx_TMXMapInfo_getTilesets); + tolua_function(tolua_S,"initWithTMXFile",lua_cocos2dx_TMXMapInfo_initWithTMXFile); tolua_function(tolua_S,"getParentGID",lua_cocos2dx_TMXMapInfo_getParentGID); + tolua_function(tolua_S,"getTilesets",lua_cocos2dx_TMXMapInfo_getTilesets); tolua_function(tolua_S,"setParentElement",lua_cocos2dx_TMXMapInfo_setParentElement); tolua_function(tolua_S,"initWithXML",lua_cocos2dx_TMXMapInfo_initWithXML); tolua_function(tolua_S,"setParentGID",lua_cocos2dx_TMXMapInfo_setParentGID); @@ -106254,8 +106254,6 @@ TOLUA_API int register_all_cocos2dx(lua_State* tolua_S) lua_register_cocos2dx_ShakyTiles3D(tolua_S); lua_register_cocos2dx_PageTurn3D(tolua_S); lua_register_cocos2dx_PolygonInfo(tolua_S); - lua_register_cocos2dx_TransitionSlideInL(tolua_S); - lua_register_cocos2dx_TransitionSlideInT(tolua_S); lua_register_cocos2dx_Grid3D(tolua_S); lua_register_cocos2dx_EventListenerController(tolua_S); lua_register_cocos2dx_TransitionProgressInOut(tolua_S); @@ -106295,6 +106293,7 @@ TOLUA_API int register_all_cocos2dx(lua_State* tolua_S) lua_register_cocos2dx_ComponentLua(tolua_S); lua_register_cocos2dx_MotionStreak3D(tolua_S); lua_register_cocos2dx_EaseOut(tolua_S); + lua_register_cocos2dx_TransitionSlideInL(tolua_S); lua_register_cocos2dx_MenuItemFont(tolua_S); lua_register_cocos2dx_TransitionFadeUp(tolua_S); lua_register_cocos2dx_LayerRadialGradient(tolua_S); @@ -106308,6 +106307,7 @@ TOLUA_API int register_all_cocos2dx(lua_State* tolua_S) lua_register_cocos2dx_RotateBy(tolua_S); lua_register_cocos2dx_FileUtils(tolua_S); lua_register_cocos2dx_Sprite(tolua_S); + lua_register_cocos2dx_TransitionSlideInT(tolua_S); lua_register_cocos2dx_ProgressTo(tolua_S); lua_register_cocos2dx_TransitionProgressOutIn(tolua_S); lua_register_cocos2dx_AnimationFrame(tolua_S); diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_auto.hpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_auto.hpp index 5aaefb89930a..aba6ace4988f 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_auto.hpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_auto.hpp @@ -2335,4 +2335,17 @@ int register_all_cocos2dx(lua_State* tolua_S); -#endif // __cocos2dx_h__ + + + + + + + + + + + + + + diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_extension_auto.cpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_extension_auto.cpp index ff7773308d13..b973fd800e78 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_extension_auto.cpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_extension_auto.cpp @@ -4861,7 +4861,7 @@ int lua_cocos2dx_extension_ControlPotentiometer_getValue(lua_State* tolua_S) return 0; } -int lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation(lua_State* tolua_S) +int lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint(lua_State* tolua_S) { int argc = 0; cocos2d::extension::ControlPotentiometer* cobj = nullptr; @@ -4881,34 +4881,40 @@ int lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation(lua_State* t #if COCOS2D_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + cocos2d::Vec2 arg0; + cocos2d::Vec2 arg1; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "cc.ControlPotentiometer:distanceBetweenPointAndPoint"); + + ok &= luaval_to_vec2(tolua_S, 3, &arg1, "cc.ControlPotentiometer:distanceBetweenPointAndPoint"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint'", nullptr); return 0; } - cocos2d::Vec2 ret = cobj->getPreviousLocation(); - vec2_to_luaval(tolua_S, ret); + double ret = cobj->distanceBetweenPointAndPoint(arg0, arg1); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.ControlPotentiometer:getPreviousLocation",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.ControlPotentiometer:distanceBetweenPointAndPoint",argc, 2); return 0; #if COCOS2D_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint'.",&tolua_err); #endif return 0; } -int lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint(lua_State* tolua_S) +int lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded(lua_State* tolua_S) { int argc = 0; cocos2d::extension::ControlPotentiometer* cobj = nullptr; @@ -4928,40 +4934,37 @@ int lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint(lua #if COCOS2D_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 1) { cocos2d::Vec2 arg0; - cocos2d::Vec2 arg1; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "cc.ControlPotentiometer:distanceBetweenPointAndPoint"); - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "cc.ControlPotentiometer:distanceBetweenPointAndPoint"); + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "cc.ControlPotentiometer:potentiometerEnded"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded'", nullptr); return 0; } - double ret = cobj->distanceBetweenPointAndPoint(arg0, arg1); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->potentiometerEnded(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.ControlPotentiometer:distanceBetweenPointAndPoint",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.ControlPotentiometer:potentiometerEnded",argc, 1); return 0; #if COCOS2D_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded'.",&tolua_err); #endif return 0; } -int lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded(lua_State* tolua_S) +int lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation(lua_State* tolua_S) { int argc = 0; cocos2d::extension::ControlPotentiometer* cobj = nullptr; @@ -4981,32 +4984,29 @@ int lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded(lua_State* to #if COCOS2D_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - cocos2d::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "cc.ControlPotentiometer:potentiometerEnded"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation'", nullptr); return 0; } - cobj->potentiometerEnded(arg0); - lua_settop(tolua_S, 1); + cocos2d::Vec2 ret = cobj->getPreviousLocation(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.ControlPotentiometer:potentiometerEnded",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.ControlPotentiometer:getPreviousLocation",argc, 0); return 0; #if COCOS2D_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation'.",&tolua_err); #endif return 0; @@ -5364,9 +5364,9 @@ int lua_register_cocos2dx_extension_ControlPotentiometer(lua_State* tolua_S) tolua_function(tolua_S,"getMinimumValue",lua_cocos2dx_extension_ControlPotentiometer_getMinimumValue); tolua_function(tolua_S,"setThumbSprite",lua_cocos2dx_extension_ControlPotentiometer_setThumbSprite); tolua_function(tolua_S,"getValue",lua_cocos2dx_extension_ControlPotentiometer_getValue); - tolua_function(tolua_S,"getPreviousLocation",lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation); tolua_function(tolua_S,"distanceBetweenPointAndPoint",lua_cocos2dx_extension_ControlPotentiometer_distanceBetweenPointAndPoint); tolua_function(tolua_S,"potentiometerEnded",lua_cocos2dx_extension_ControlPotentiometer_potentiometerEnded); + tolua_function(tolua_S,"getPreviousLocation",lua_cocos2dx_extension_ControlPotentiometer_getPreviousLocation); tolua_function(tolua_S,"setProgressTimer",lua_cocos2dx_extension_ControlPotentiometer_setProgressTimer); tolua_function(tolua_S,"setMinimumValue",lua_cocos2dx_extension_ControlPotentiometer_setMinimumValue); tolua_function(tolua_S,"getThumbSprite",lua_cocos2dx_extension_ControlPotentiometer_getThumbSprite); @@ -5380,56 +5380,6 @@ int lua_register_cocos2dx_extension_ControlPotentiometer(lua_State* tolua_S) return 1; } -int lua_cocos2dx_extension_ControlSlider_setBackgroundSprite(lua_State* tolua_S) -{ - int argc = 0; - cocos2d::extension::ControlSlider* cobj = nullptr; - bool ok = true; - -#if COCOS2D_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if COCOS2D_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"cc.ControlSlider",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (cocos2d::extension::ControlSlider*)tolua_tousertype(tolua_S,1,0); - -#if COCOS2D_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_ControlSlider_setBackgroundSprite'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - cocos2d::Sprite* arg0; - - ok &= luaval_to_object(tolua_S, 2, "cc.Sprite",&arg0, "cc.ControlSlider:setBackgroundSprite"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_ControlSlider_setBackgroundSprite'", nullptr); - return 0; - } - cobj->setBackgroundSprite(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.ControlSlider:setBackgroundSprite",argc, 1); - return 0; - -#if COCOS2D_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_ControlSlider_setBackgroundSprite'.",&tolua_err); -#endif - - return 0; -} int lua_cocos2dx_extension_ControlSlider_getMaximumAllowedValue(lua_State* tolua_S) { int argc = 0; @@ -6328,6 +6278,56 @@ int lua_cocos2dx_extension_ControlSlider_setSelectedThumbSprite(lua_State* tolua return 0; } +int lua_cocos2dx_extension_ControlSlider_setBackgroundSprite(lua_State* tolua_S) +{ + int argc = 0; + cocos2d::extension::ControlSlider* cobj = nullptr; + bool ok = true; + +#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + +#if COCOS2D_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"cc.ControlSlider",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (cocos2d::extension::ControlSlider*)tolua_tousertype(tolua_S,1,0); + +#if COCOS2D_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_ControlSlider_setBackgroundSprite'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + cocos2d::Sprite* arg0; + + ok &= luaval_to_object(tolua_S, 2, "cc.Sprite",&arg0, "cc.ControlSlider:setBackgroundSprite"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_ControlSlider_setBackgroundSprite'", nullptr); + return 0; + } + cobj->setBackgroundSprite(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.ControlSlider:setBackgroundSprite",argc, 1); + return 0; + +#if COCOS2D_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_ControlSlider_setBackgroundSprite'.",&tolua_err); +#endif + + return 0; +} int lua_cocos2dx_extension_ControlSlider_setMaximumAllowedValue(lua_State* tolua_S) { int argc = 0; @@ -6532,7 +6532,6 @@ int lua_register_cocos2dx_extension_ControlSlider(lua_State* tolua_S) tolua_beginmodule(tolua_S,"ControlSlider"); tolua_function(tolua_S,"new",lua_cocos2dx_extension_ControlSlider_constructor); - tolua_function(tolua_S,"setBackgroundSprite",lua_cocos2dx_extension_ControlSlider_setBackgroundSprite); tolua_function(tolua_S,"getMaximumAllowedValue",lua_cocos2dx_extension_ControlSlider_getMaximumAllowedValue); tolua_function(tolua_S,"initWithSprites",lua_cocos2dx_extension_ControlSlider_initWithSprites); tolua_function(tolua_S,"getMinimumAllowedValue",lua_cocos2dx_extension_ControlSlider_getMinimumAllowedValue); @@ -6551,6 +6550,7 @@ int lua_register_cocos2dx_extension_ControlSlider(lua_State* tolua_S) tolua_function(tolua_S,"setMinimumAllowedValue",lua_cocos2dx_extension_ControlSlider_setMinimumAllowedValue); tolua_function(tolua_S,"getProgressSprite",lua_cocos2dx_extension_ControlSlider_getProgressSprite); tolua_function(tolua_S,"setSelectedThumbSprite",lua_cocos2dx_extension_ControlSlider_setSelectedThumbSprite); + tolua_function(tolua_S,"setBackgroundSprite",lua_cocos2dx_extension_ControlSlider_setBackgroundSprite); tolua_function(tolua_S,"setMaximumAllowedValue",lua_cocos2dx_extension_ControlSlider_setMaximumAllowedValue); tolua_function(tolua_S,"create", lua_cocos2dx_extension_ControlSlider_create); tolua_endmodule(tolua_S); @@ -10231,56 +10231,6 @@ int lua_cocos2dx_extension_TableView_setVerticalFillOrder(lua_State* tolua_S) return 0; } -int lua_cocos2dx_extension_TableView_scrollViewDidZoom(lua_State* tolua_S) -{ - int argc = 0; - cocos2d::extension::TableView* cobj = nullptr; - bool ok = true; - -#if COCOS2D_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if COCOS2D_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"cc.TableView",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (cocos2d::extension::TableView*)tolua_tousertype(tolua_S,1,0); - -#if COCOS2D_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_TableView_scrollViewDidZoom'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - cocos2d::extension::ScrollView* arg0; - - ok &= luaval_to_object(tolua_S, 2, "cc.ScrollView",&arg0, "cc.TableView:scrollViewDidZoom"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_TableView_scrollViewDidZoom'", nullptr); - return 0; - } - cobj->scrollViewDidZoom(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.TableView:scrollViewDidZoom",argc, 1); - return 0; - -#if COCOS2D_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_TableView_scrollViewDidZoom'.",&tolua_err); -#endif - - return 0; -} int lua_cocos2dx_extension_TableView__updateContentSize(lua_State* tolua_S) { int argc = 0; @@ -10589,6 +10539,56 @@ int lua_cocos2dx_extension_TableView_reloadData(lua_State* tolua_S) return 0; } +int lua_cocos2dx_extension_TableView_scrollViewDidZoom(lua_State* tolua_S) +{ + int argc = 0; + cocos2d::extension::TableView* cobj = nullptr; + bool ok = true; + +#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + +#if COCOS2D_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"cc.TableView",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (cocos2d::extension::TableView*)tolua_tousertype(tolua_S,1,0); + +#if COCOS2D_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_extension_TableView_scrollViewDidZoom'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + cocos2d::extension::ScrollView* arg0; + + ok &= luaval_to_object(tolua_S, 2, "cc.ScrollView",&arg0, "cc.TableView:scrollViewDidZoom"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_extension_TableView_scrollViewDidZoom'", nullptr); + return 0; + } + cobj->scrollViewDidZoom(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.TableView:scrollViewDidZoom",argc, 1); + return 0; + +#if COCOS2D_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_extension_TableView_scrollViewDidZoom'.",&tolua_err); +#endif + + return 0; +} int lua_cocos2dx_extension_TableView_insertCellAtIndex(lua_State* tolua_S) { int argc = 0; @@ -10788,13 +10788,13 @@ int lua_register_cocos2dx_extension_TableView(lua_State* tolua_S) tolua_function(tolua_S,"new",lua_cocos2dx_extension_TableView_constructor); tolua_function(tolua_S,"updateCellAtIndex",lua_cocos2dx_extension_TableView_updateCellAtIndex); tolua_function(tolua_S,"setVerticalFillOrder",lua_cocos2dx_extension_TableView_setVerticalFillOrder); - tolua_function(tolua_S,"scrollViewDidZoom",lua_cocos2dx_extension_TableView_scrollViewDidZoom); tolua_function(tolua_S,"_updateContentSize",lua_cocos2dx_extension_TableView__updateContentSize); tolua_function(tolua_S,"getVerticalFillOrder",lua_cocos2dx_extension_TableView_getVerticalFillOrder); tolua_function(tolua_S,"removeCellAtIndex",lua_cocos2dx_extension_TableView_removeCellAtIndex); tolua_function(tolua_S,"initWithViewSize",lua_cocos2dx_extension_TableView_initWithViewSize); tolua_function(tolua_S,"scrollViewDidScroll",lua_cocos2dx_extension_TableView_scrollViewDidScroll); tolua_function(tolua_S,"reloadData",lua_cocos2dx_extension_TableView_reloadData); + tolua_function(tolua_S,"scrollViewDidZoom",lua_cocos2dx_extension_TableView_scrollViewDidZoom); tolua_function(tolua_S,"insertCellAtIndex",lua_cocos2dx_extension_TableView_insertCellAtIndex); tolua_function(tolua_S,"cellAtIndex",lua_cocos2dx_extension_TableView_cellAtIndex); tolua_function(tolua_S,"dequeueCell",lua_cocos2dx_extension_TableView_dequeueCell); diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_spine_auto.cpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_spine_auto.cpp index 1d7fa269f06a..5499c6474db1 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_spine_auto.cpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_spine_auto.cpp @@ -175,10 +175,9 @@ int lua_cocos2dx_spine_SkeletonRenderer_initWithData(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - spSkeletonData* arg0; + spine::SkeletonData* arg0; - #pragma warning NO CONVERSION TO NATIVE FOR spSkeletonData* - ok = false; + ok &= luaval_to_object(tolua_S, 2, "sp.SkeletonData",&arg0, "sp.SkeletonRenderer:initWithData"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_initWithData'", nullptr); @@ -190,11 +189,10 @@ int lua_cocos2dx_spine_SkeletonRenderer_initWithData(lua_State* tolua_S) } if (argc == 2) { - spSkeletonData* arg0; + spine::SkeletonData* arg0; bool arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spSkeletonData* - ok = false; + ok &= luaval_to_object(tolua_S, 2, "sp.SkeletonData",&arg0, "sp.SkeletonRenderer:initWithData"); ok &= luaval_to_boolean(tolua_S, 3,&arg1, "sp.SkeletonRenderer:initWithData"); if(!ok) @@ -329,9 +327,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_initWithJsonFile(lua_State* tolua_S) ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonRenderer:initWithJsonFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonRenderer:initWithJsonFile"); if (!ok) { break; } cobj->initWithJsonFile(arg0, arg1); @@ -346,9 +343,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_initWithJsonFile(lua_State* tolua_S) ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonRenderer:initWithJsonFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonRenderer:initWithJsonFile"); if (!ok) { break; } double arg2; @@ -481,9 +477,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_initWithBinaryFile(lua_State* tolua_S) ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonRenderer:initWithBinaryFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonRenderer:initWithBinaryFile"); if (!ok) { break; } cobj->initWithBinaryFile(arg0, arg1); @@ -498,9 +493,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_initWithBinaryFile(lua_State* tolua_S) ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonRenderer:initWithBinaryFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonRenderer:initWithBinaryFile"); if (!ok) { break; } double arg2; @@ -667,6 +661,116 @@ int lua_cocos2dx_spine_SkeletonRenderer_isTwoColorTint(lua_State* tolua_S) return 0; } +int lua_cocos2dx_spine_SkeletonRenderer_initWithSkeleton(lua_State* tolua_S) +{ + int argc = 0; + spine::SkeletonRenderer* cobj = nullptr; + bool ok = true; + +#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + +#if COCOS2D_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"sp.SkeletonRenderer",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (spine::SkeletonRenderer*)tolua_tousertype(tolua_S,1,0); + +#if COCOS2D_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_spine_SkeletonRenderer_initWithSkeleton'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + spine::Skeleton* arg0; + + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:initWithSkeleton"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_initWithSkeleton'", nullptr); + return 0; + } + cobj->initWithSkeleton(arg0); + lua_settop(tolua_S, 1); + return 1; + } + if (argc == 2) + { + spine::Skeleton* arg0; + bool arg1; + + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:initWithSkeleton"); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "sp.SkeletonRenderer:initWithSkeleton"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_initWithSkeleton'", nullptr); + return 0; + } + cobj->initWithSkeleton(arg0, arg1); + lua_settop(tolua_S, 1); + return 1; + } + if (argc == 3) + { + spine::Skeleton* arg0; + bool arg1; + bool arg2; + + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:initWithSkeleton"); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "sp.SkeletonRenderer:initWithSkeleton"); + + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "sp.SkeletonRenderer:initWithSkeleton"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_initWithSkeleton'", nullptr); + return 0; + } + cobj->initWithSkeleton(arg0, arg1, arg2); + lua_settop(tolua_S, 1); + return 1; + } + if (argc == 4) + { + spine::Skeleton* arg0; + bool arg1; + bool arg2; + bool arg3; + + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:initWithSkeleton"); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "sp.SkeletonRenderer:initWithSkeleton"); + + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "sp.SkeletonRenderer:initWithSkeleton"); + + ok &= luaval_to_boolean(tolua_S, 5,&arg3, "sp.SkeletonRenderer:initWithSkeleton"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_initWithSkeleton'", nullptr); + return 0; + } + cobj->initWithSkeleton(arg0, arg1, arg2, arg3); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "sp.SkeletonRenderer:initWithSkeleton",argc, 1); + return 0; + +#if COCOS2D_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_spine_SkeletonRenderer_initWithSkeleton'.",&tolua_err); +#endif + + return 0; +} int lua_cocos2dx_spine_SkeletonRenderer_getBlendFunc(lua_State* tolua_S) { int argc = 0; @@ -714,6 +818,59 @@ int lua_cocos2dx_spine_SkeletonRenderer_getBlendFunc(lua_State* tolua_S) return 0; } +int lua_cocos2dx_spine_SkeletonRenderer_setSlotsRange(lua_State* tolua_S) +{ + int argc = 0; + spine::SkeletonRenderer* cobj = nullptr; + bool ok = true; + +#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + +#if COCOS2D_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"sp.SkeletonRenderer",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (spine::SkeletonRenderer*)tolua_tousertype(tolua_S,1,0); + +#if COCOS2D_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_spine_SkeletonRenderer_setSlotsRange'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 2) + { + int arg0; + int arg1; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "sp.SkeletonRenderer:setSlotsRange"); + + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "sp.SkeletonRenderer:setSlotsRange"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_setSlotsRange'", nullptr); + return 0; + } + cobj->setSlotsRange(arg0, arg1); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "sp.SkeletonRenderer:setSlotsRange",argc, 2); + return 0; + +#if COCOS2D_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_spine_SkeletonRenderer_setSlotsRange'.",&tolua_err); +#endif + + return 0; +} int lua_cocos2dx_spine_SkeletonRenderer_initialize(lua_State* tolua_S) { int argc = 0; @@ -1080,10 +1237,9 @@ int lua_cocos2dx_spine_SkeletonRenderer_setVertexEffect(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - spVertexEffect* arg0; + spine::VertexEffect* arg0; - #pragma warning NO CONVERSION TO NATIVE FOR spVertexEffect* - ok = false; + ok &= luaval_to_object(tolua_S, 2, "sp.VertexEffect",&arg0, "sp.SkeletonRenderer:setVertexEffect"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_setVertexEffect'", nullptr); @@ -1130,8 +1286,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_setSkin(lua_State* tolua_S) std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "sp.SkeletonRenderer:setSkin"); arg0 = arg0_tmp.c_str(); if (!ok) { break; } - bool ret = cobj->setSkin(arg0); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setSkin(arg0); + lua_settop(tolua_S, 1); return 1; } }while(0); @@ -1142,8 +1298,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_setSkin(lua_State* tolua_S) ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonRenderer:setSkin"); if (!ok) { break; } - bool ret = cobj->setSkin(arg0); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setSkin(arg0); + lua_settop(tolua_S, 1); return 1; } }while(0); @@ -1191,8 +1347,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_getSkeleton(lua_State* tolua_S) tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_getSkeleton'", nullptr); return 0; } - spSkeleton* ret = cobj->getSkeleton(); - #pragma warning NO CONVERSION FROM NATIVE FOR spSkeleton*; + spine::Skeleton* ret = cobj->getSkeleton(); + object_to_luaval(tolua_S, "sp.Skeleton",(spine::Skeleton*)ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "sp.SkeletonRenderer:getSkeleton",argc, 0); @@ -1261,9 +1417,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_createWithFile(lua_State* tolua_S) std::string arg0; ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonRenderer:createWithFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonRenderer:createWithFile"); if (!ok) { break; } spine::SkeletonRenderer* ret = spine::SkeletonRenderer::createWithFile(arg0, arg1); object_to_luaval(tolua_S, "sp.SkeletonRenderer",(spine::SkeletonRenderer*)ret); @@ -1278,9 +1433,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_createWithFile(lua_State* tolua_S) std::string arg0; ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonRenderer:createWithFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonRenderer:createWithFile"); if (!ok) { break; } double arg2; ok &= luaval_to_number(tolua_S, 4,&arg2, "sp.SkeletonRenderer:createWithFile"); @@ -1333,6 +1487,108 @@ int lua_cocos2dx_spine_SkeletonRenderer_create(lua_State* tolua_S) #endif return 0; } +int lua_cocos2dx_spine_SkeletonRenderer_createWithSkeleton(lua_State* tolua_S) +{ + int argc = 0; + bool ok = true; + +#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; +#endif + +#if COCOS2D_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"sp.SkeletonRenderer",0,&tolua_err)) goto tolua_lerror; +#endif + + argc = lua_gettop(tolua_S) - 1; + + if (argc == 1) + { + spine::Skeleton* arg0; + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:createWithSkeleton"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_createWithSkeleton'", nullptr); + return 0; + } + spine::SkeletonRenderer* ret = spine::SkeletonRenderer::createWithSkeleton(arg0); + object_to_luaval(tolua_S, "sp.SkeletonRenderer",(spine::SkeletonRenderer*)ret); + return 1; + } + if (argc == 2) + { + spine::Skeleton* arg0; + bool arg1; + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:createWithSkeleton"); + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "sp.SkeletonRenderer:createWithSkeleton"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_createWithSkeleton'", nullptr); + return 0; + } + spine::SkeletonRenderer* ret = spine::SkeletonRenderer::createWithSkeleton(arg0, arg1); + object_to_luaval(tolua_S, "sp.SkeletonRenderer",(spine::SkeletonRenderer*)ret); + return 1; + } + if (argc == 3) + { + spine::Skeleton* arg0; + bool arg1; + bool arg2; + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:createWithSkeleton"); + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "sp.SkeletonRenderer:createWithSkeleton"); + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "sp.SkeletonRenderer:createWithSkeleton"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_createWithSkeleton'", nullptr); + return 0; + } + spine::SkeletonRenderer* ret = spine::SkeletonRenderer::createWithSkeleton(arg0, arg1, arg2); + object_to_luaval(tolua_S, "sp.SkeletonRenderer",(spine::SkeletonRenderer*)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "sp.SkeletonRenderer:createWithSkeleton",argc, 1); + return 0; +#if COCOS2D_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_spine_SkeletonRenderer_createWithSkeleton'.",&tolua_err); +#endif + return 0; +} +int lua_cocos2dx_spine_SkeletonRenderer_destroyScratchBuffers(lua_State* tolua_S) +{ + int argc = 0; + bool ok = true; + +#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; +#endif + +#if COCOS2D_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"sp.SkeletonRenderer",0,&tolua_err)) goto tolua_lerror; +#endif + + argc = lua_gettop(tolua_S) - 1; + + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonRenderer_destroyScratchBuffers'", nullptr); + return 0; + } + spine::SkeletonRenderer::destroyScratchBuffers(); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "sp.SkeletonRenderer:destroyScratchBuffers",argc, 0); + return 0; +#if COCOS2D_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_spine_SkeletonRenderer_destroyScratchBuffers'.",&tolua_err); +#endif + return 0; +} int lua_cocos2dx_spine_SkeletonRenderer_constructor(lua_State* tolua_S) { int argc = 0; @@ -1345,9 +1601,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_constructor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; do{ if (argc == 1) { - spSkeletonData* arg0; - #pragma warning NO CONVERSION TO NATIVE FOR spSkeletonData* - ok = false; + spine::Skeleton* arg0; + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:SkeletonRenderer"); if (!ok) { break; } cobj = new spine::SkeletonRenderer(arg0); @@ -1361,9 +1616,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_constructor(lua_State* tolua_S) ok = true; do{ if (argc == 2) { - spSkeletonData* arg0; - #pragma warning NO CONVERSION TO NATIVE FOR spSkeletonData* - ok = false; + spine::Skeleton* arg0; + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:SkeletonRenderer"); if (!ok) { break; } bool arg1; @@ -1379,6 +1633,56 @@ int lua_cocos2dx_spine_SkeletonRenderer_constructor(lua_State* tolua_S) } }while(0); ok = true; + do{ + if (argc == 3) { + spine::Skeleton* arg0; + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + bool arg1; + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + bool arg2; + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + cobj = new spine::SkeletonRenderer(arg0, arg1, arg2); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_ccobject(tolua_S, ID, luaID, (void*)cobj,"sp.SkeletonRenderer"); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 4) { + spine::Skeleton* arg0; + ok &= luaval_to_object(tolua_S, 2, "sp.Skeleton",&arg0, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + bool arg1; + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + bool arg2; + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + bool arg3; + ok &= luaval_to_boolean(tolua_S, 5,&arg3, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + cobj = new spine::SkeletonRenderer(arg0, arg1, arg2, arg3); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_ccobject(tolua_S, ID, luaID, (void*)cobj,"sp.SkeletonRenderer"); + return 1; + } + }while(0); + ok = true; do{ if (argc == 0) { cobj = new spine::SkeletonRenderer(); @@ -1390,15 +1694,48 @@ int lua_cocos2dx_spine_SkeletonRenderer_constructor(lua_State* tolua_S) } }while(0); ok = true; + do{ + if (argc == 1) { + spine::SkeletonData* arg0; + ok &= luaval_to_object(tolua_S, 2, "sp.SkeletonData",&arg0, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + cobj = new spine::SkeletonRenderer(arg0); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_ccobject(tolua_S, ID, luaID, (void*)cobj,"sp.SkeletonRenderer"); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 2) { + spine::SkeletonData* arg0; + ok &= luaval_to_object(tolua_S, 2, "sp.SkeletonData",&arg0, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + bool arg1; + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "sp.SkeletonRenderer:SkeletonRenderer"); + + if (!ok) { break; } + cobj = new spine::SkeletonRenderer(arg0, arg1); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_ccobject(tolua_S, ID, luaID, (void*)cobj,"sp.SkeletonRenderer"); + return 1; + } + }while(0); + ok = true; do{ if (argc == 2) { std::string arg0; ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonRenderer:SkeletonRenderer"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonRenderer:SkeletonRenderer"); if (!ok) { break; } cobj = new spine::SkeletonRenderer(arg0, arg1); @@ -1416,9 +1753,8 @@ int lua_cocos2dx_spine_SkeletonRenderer_constructor(lua_State* tolua_S) ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonRenderer:SkeletonRenderer"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonRenderer:SkeletonRenderer"); if (!ok) { break; } double arg2; @@ -1510,7 +1846,9 @@ int lua_register_cocos2dx_spine_SkeletonRenderer(lua_State* tolua_S) tolua_function(tolua_S,"setToSetupPose",lua_cocos2dx_spine_SkeletonRenderer_setToSetupPose); tolua_function(tolua_S,"setDebugMeshesEnabled",lua_cocos2dx_spine_SkeletonRenderer_setDebugMeshesEnabled); tolua_function(tolua_S,"isTwoColorTint",lua_cocos2dx_spine_SkeletonRenderer_isTwoColorTint); + tolua_function(tolua_S,"initWithSkeleton",lua_cocos2dx_spine_SkeletonRenderer_initWithSkeleton); tolua_function(tolua_S,"getBlendFunc",lua_cocos2dx_spine_SkeletonRenderer_getBlendFunc); + tolua_function(tolua_S,"setSlotsRange",lua_cocos2dx_spine_SkeletonRenderer_setSlotsRange); tolua_function(tolua_S,"initialize",lua_cocos2dx_spine_SkeletonRenderer_initialize); tolua_function(tolua_S,"setDebugBonesEnabled",lua_cocos2dx_spine_SkeletonRenderer_setDebugBonesEnabled); tolua_function(tolua_S,"getDebugBonesEnabled",lua_cocos2dx_spine_SkeletonRenderer_getDebugBonesEnabled); @@ -1523,6 +1861,8 @@ int lua_register_cocos2dx_spine_SkeletonRenderer(lua_State* tolua_S) tolua_function(tolua_S,"getSkeleton",lua_cocos2dx_spine_SkeletonRenderer_getSkeleton); tolua_function(tolua_S,"createWithFile", lua_cocos2dx_spine_SkeletonRenderer_createWithFile); tolua_function(tolua_S,"create", lua_cocos2dx_spine_SkeletonRenderer_create); + tolua_function(tolua_S,"createWithSkeleton", lua_cocos2dx_spine_SkeletonRenderer_createWithSkeleton); + tolua_function(tolua_S,"destroyScratchBuffers", lua_cocos2dx_spine_SkeletonRenderer_destroyScratchBuffers); tolua_endmodule(tolua_S); std::string typeName = typeid(spine::SkeletonRenderer).name(); g_luaType[typeName] = "sp.SkeletonRenderer"; @@ -1558,11 +1898,10 @@ int lua_cocos2dx_spine_SkeletonAnimation_setTrackCompleteListener(lua_State* tol argc = lua_gettop(tolua_S)-1; if (argc == 2) { - spTrackEntry* arg0; - std::function arg1; + spine::TrackEntry* arg0; + std::function arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spTrackEntry* - ok = false; + ok &= luaval_to_object(tolua_S, 2, "sp.TrackEntry",&arg0, "sp.SkeletonAnimation:setTrackCompleteListener"); do { // Lambda binding for lua is not supported. @@ -1624,8 +1963,8 @@ int lua_cocos2dx_spine_SkeletonAnimation_findAnimation(lua_State* tolua_S) tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonAnimation_findAnimation'", nullptr); return 0; } - spAnimation* ret = cobj->findAnimation(arg0); - #pragma warning NO CONVERSION FROM NATIVE FOR spAnimation*; + spine::Animation* ret = cobj->findAnimation(arg0); + object_to_luaval(tolua_S, "sp.Animation",(spine::Animation*)ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "sp.SkeletonAnimation:findAnimation",argc, 1); @@ -1666,7 +2005,7 @@ int lua_cocos2dx_spine_SkeletonAnimation_setCompleteListener(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::function arg0; + std::function arg0; do { // Lambda binding for lua is not supported. @@ -1776,11 +2115,10 @@ int lua_cocos2dx_spine_SkeletonAnimation_setTrackStartListener(lua_State* tolua_ argc = lua_gettop(tolua_S)-1; if (argc == 2) { - spTrackEntry* arg0; - std::function arg1; + spine::TrackEntry* arg0; + std::function arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spTrackEntry* - ok = false; + ok &= luaval_to_object(tolua_S, 2, "sp.TrackEntry",&arg0, "sp.SkeletonAnimation:setTrackStartListener"); do { // Lambda binding for lua is not supported. @@ -1845,8 +2183,8 @@ int lua_cocos2dx_spine_SkeletonAnimation_addEmptyAnimation(lua_State* tolua_S) tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonAnimation_addEmptyAnimation'", nullptr); return 0; } - spTrackEntry* ret = cobj->addEmptyAnimation(arg0, arg1); - #pragma warning NO CONVERSION FROM NATIVE FOR spTrackEntry*; + spine::TrackEntry* ret = cobj->addEmptyAnimation(arg0, arg1); + object_to_luaval(tolua_S, "sp.TrackEntry",(spine::TrackEntry*)ret); return 1; } if (argc == 3) @@ -1865,8 +2203,8 @@ int lua_cocos2dx_spine_SkeletonAnimation_addEmptyAnimation(lua_State* tolua_S) tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonAnimation_addEmptyAnimation'", nullptr); return 0; } - spTrackEntry* ret = cobj->addEmptyAnimation(arg0, arg1, arg2); - #pragma warning NO CONVERSION FROM NATIVE FOR spTrackEntry*; + spine::TrackEntry* ret = cobj->addEmptyAnimation(arg0, arg1, arg2); + object_to_luaval(tolua_S, "sp.TrackEntry",(spine::TrackEntry*)ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "sp.SkeletonAnimation:addEmptyAnimation",argc, 2); @@ -1907,7 +2245,7 @@ int lua_cocos2dx_spine_SkeletonAnimation_setDisposeListener(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::function arg0; + std::function arg0; do { // Lambda binding for lua is not supported. @@ -1961,11 +2299,10 @@ int lua_cocos2dx_spine_SkeletonAnimation_setTrackInterruptListener(lua_State* to argc = lua_gettop(tolua_S)-1; if (argc == 2) { - spTrackEntry* arg0; - std::function arg1; + spine::TrackEntry* arg0; + std::function arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spTrackEntry* - ok = false; + ok &= luaval_to_object(tolua_S, 2, "sp.TrackEntry",&arg0, "sp.SkeletonAnimation:setTrackInterruptListener"); do { // Lambda binding for lua is not supported. @@ -2019,7 +2356,7 @@ int lua_cocos2dx_spine_SkeletonAnimation_setEndListener(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::function arg0; + std::function arg0; do { // Lambda binding for lua is not supported. @@ -2073,11 +2410,10 @@ int lua_cocos2dx_spine_SkeletonAnimation_setTrackDisposeListener(lua_State* tolu argc = lua_gettop(tolua_S)-1; if (argc == 2) { - spTrackEntry* arg0; - std::function arg1; + spine::TrackEntry* arg0; + std::function arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spTrackEntry* - ok = false; + ok &= luaval_to_object(tolua_S, 2, "sp.TrackEntry",&arg0, "sp.SkeletonAnimation:setTrackDisposeListener"); do { // Lambda binding for lua is not supported. @@ -2131,7 +2467,7 @@ int lua_cocos2dx_spine_SkeletonAnimation_setEventListener(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::function arg0; + std::function arg0; do { // Lambda binding for lua is not supported. @@ -2196,8 +2532,8 @@ int lua_cocos2dx_spine_SkeletonAnimation_setEmptyAnimation(lua_State* tolua_S) tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_spine_SkeletonAnimation_setEmptyAnimation'", nullptr); return 0; } - spTrackEntry* ret = cobj->setEmptyAnimation(arg0, arg1); - #pragma warning NO CONVERSION FROM NATIVE FOR spTrackEntry*; + spine::TrackEntry* ret = cobj->setEmptyAnimation(arg0, arg1); + object_to_luaval(tolua_S, "sp.TrackEntry",(spine::TrackEntry*)ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "sp.SkeletonAnimation:setEmptyAnimation",argc, 2); @@ -2238,11 +2574,10 @@ int lua_cocos2dx_spine_SkeletonAnimation_setTrackEventListener(lua_State* tolua_ argc = lua_gettop(tolua_S)-1; if (argc == 2) { - spTrackEntry* arg0; - std::function arg1; + spine::TrackEntry* arg0; + std::function arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spTrackEntry* - ok = false; + ok &= luaval_to_object(tolua_S, 2, "sp.TrackEntry",&arg0, "sp.SkeletonAnimation:setTrackEventListener"); do { // Lambda binding for lua is not supported. @@ -2357,7 +2692,7 @@ int lua_cocos2dx_spine_SkeletonAnimation_setInterruptListener(lua_State* tolua_S argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::function arg0; + std::function arg0; do { // Lambda binding for lua is not supported. @@ -2508,11 +2843,10 @@ int lua_cocos2dx_spine_SkeletonAnimation_setTrackEndListener(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 2) { - spTrackEntry* arg0; - std::function arg1; + spine::TrackEntry* arg0; + std::function arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spTrackEntry* - ok = false; + ok &= luaval_to_object(tolua_S, 2, "sp.TrackEntry",&arg0, "sp.SkeletonAnimation:setTrackEndListener"); do { // Lambda binding for lua is not supported. @@ -2566,7 +2900,7 @@ int lua_cocos2dx_spine_SkeletonAnimation_setStartListener(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::function arg0; + std::function arg0; do { // Lambda binding for lua is not supported. @@ -2648,9 +2982,8 @@ int lua_cocos2dx_spine_SkeletonAnimation_createWithBinaryFile(lua_State* tolua_S std::string arg0; ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonAnimation:createWithBinaryFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonAnimation:createWithBinaryFile"); if (!ok) { break; } spine::SkeletonAnimation* ret = spine::SkeletonAnimation::createWithBinaryFile(arg0, arg1); object_to_luaval(tolua_S, "sp.SkeletonAnimation",(spine::SkeletonAnimation*)ret); @@ -2665,9 +2998,8 @@ int lua_cocos2dx_spine_SkeletonAnimation_createWithBinaryFile(lua_State* tolua_S std::string arg0; ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonAnimation:createWithBinaryFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonAnimation:createWithBinaryFile"); if (!ok) { break; } double arg2; ok &= luaval_to_number(tolua_S, 4,&arg2, "sp.SkeletonAnimation:createWithBinaryFile"); @@ -2776,9 +3108,8 @@ int lua_cocos2dx_spine_SkeletonAnimation_createWithJsonFile(lua_State* tolua_S) std::string arg0; ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonAnimation:createWithJsonFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonAnimation:createWithJsonFile"); if (!ok) { break; } spine::SkeletonAnimation* ret = spine::SkeletonAnimation::createWithJsonFile(arg0, arg1); object_to_luaval(tolua_S, "sp.SkeletonAnimation",(spine::SkeletonAnimation*)ret); @@ -2793,9 +3124,8 @@ int lua_cocos2dx_spine_SkeletonAnimation_createWithJsonFile(lua_State* tolua_S) std::string arg0; ok &= luaval_to_std_string(tolua_S, 2,&arg0, "sp.SkeletonAnimation:createWithJsonFile"); if (!ok) { break; } - spAtlas* arg1; - #pragma warning NO CONVERSION TO NATIVE FOR spAtlas* - ok = false; + spine::Atlas* arg1; + ok &= luaval_to_object(tolua_S, 3, "sp.Atlas",&arg1, "sp.SkeletonAnimation:createWithJsonFile"); if (!ok) { break; } double arg2; ok &= luaval_to_number(tolua_S, 4,&arg2, "sp.SkeletonAnimation:createWithJsonFile"); diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_spine_auto.hpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_spine_auto.hpp index 28ad4cb9bdfe..d409127d7180 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_spine_auto.hpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_spine_auto.hpp @@ -56,6 +56,10 @@ int register_all_cocos2dx_spine(lua_State* tolua_S); + + + + diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_studio_auto.cpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_studio_auto.cpp index e4d7dba95e4d..29481a445038 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_studio_auto.cpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_studio_auto.cpp @@ -9530,53 +9530,6 @@ int lua_register_cocos2dx_studio_ArmatureAnimation(lua_State* tolua_S) return 1; } -int lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas(lua_State* tolua_S) -{ - int argc = 0; - cocostudio::ArmatureDataManager* cobj = nullptr; - bool ok = true; - -#if COCOS2D_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if COCOS2D_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ccs.ArmatureDataManager",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (cocostudio::ArmatureDataManager*)tolua_tousertype(tolua_S,1,0); - -#if COCOS2D_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas'", nullptr); - return 0; - } - const cocos2d::Map& ret = cobj->getAnimationDatas(); - ccmap_string_key_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ccs.ArmatureDataManager:getAnimationDatas",argc, 0); - return 0; - -#if COCOS2D_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas'.",&tolua_err); -#endif - - return 0; -} int lua_cocos2dx_studio_ArmatureDataManager_removeAnimationData(lua_State* tolua_S) { int argc = 0; @@ -10350,6 +10303,53 @@ int lua_cocos2dx_studio_ArmatureDataManager_addTextureData(lua_State* tolua_S) return 0; } +int lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas(lua_State* tolua_S) +{ + int argc = 0; + cocostudio::ArmatureDataManager* cobj = nullptr; + bool ok = true; + +#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + +#if COCOS2D_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ccs.ArmatureDataManager",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (cocostudio::ArmatureDataManager*)tolua_tousertype(tolua_S,1,0); + +#if COCOS2D_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas'", nullptr); + return 0; + } + const cocos2d::Map& ret = cobj->getAnimationDatas(); + ccmap_string_key_to_luaval(tolua_S, ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ccs.ArmatureDataManager:getAnimationDatas",argc, 0); + return 0; + +#if COCOS2D_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas'.",&tolua_err); +#endif + + return 0; +} int lua_cocos2dx_studio_ArmatureDataManager_isAutoLoadSpriteFile(lua_State* tolua_S) { int argc = 0; @@ -10550,7 +10550,6 @@ int lua_register_cocos2dx_studio_ArmatureDataManager(lua_State* tolua_S) tolua_cclass(tolua_S,"ArmatureDataManager","ccs.ArmatureDataManager","cc.Ref",nullptr); tolua_beginmodule(tolua_S,"ArmatureDataManager"); - tolua_function(tolua_S,"getAnimationDatas",lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas); tolua_function(tolua_S,"removeAnimationData",lua_cocos2dx_studio_ArmatureDataManager_removeAnimationData); tolua_function(tolua_S,"addArmatureData",lua_cocos2dx_studio_ArmatureDataManager_addArmatureData); tolua_function(tolua_S,"addArmatureFileInfo",lua_cocos2dx_studio_ArmatureDataManager_addArmatureFileInfo); @@ -10565,6 +10564,7 @@ int lua_register_cocos2dx_studio_ArmatureDataManager(lua_State* tolua_S) tolua_function(tolua_S,"getArmatureDatas",lua_cocos2dx_studio_ArmatureDataManager_getArmatureDatas); tolua_function(tolua_S,"removeTextureData",lua_cocos2dx_studio_ArmatureDataManager_removeTextureData); tolua_function(tolua_S,"addTextureData",lua_cocos2dx_studio_ArmatureDataManager_addTextureData); + tolua_function(tolua_S,"getAnimationDatas",lua_cocos2dx_studio_ArmatureDataManager_getAnimationDatas); tolua_function(tolua_S,"isAutoLoadSpriteFile",lua_cocos2dx_studio_ArmatureDataManager_isAutoLoadSpriteFile); tolua_function(tolua_S,"addSpriteFrameFromFile",lua_cocos2dx_studio_ArmatureDataManager_addSpriteFrameFromFile); tolua_function(tolua_S,"destroyInstance", lua_cocos2dx_studio_ArmatureDataManager_destroyInstance); diff --git a/cocos/scripting/lua-bindings/manual/lua_module_register.cpp b/cocos/scripting/lua-bindings/manual/lua_module_register.cpp index e5743d8db6f2..54a8db28aabc 100644 --- a/cocos/scripting/lua-bindings/manual/lua_module_register.cpp +++ b/cocos/scripting/lua-bindings/manual/lua_module_register.cpp @@ -42,8 +42,7 @@ int lua_module_register(lua_State* L) register_cocostudio_module(L); register_ui_module(L); register_extension_module(L); -//TODO arnold -// register_spine_module(L); + register_spine_module(L); register_cocos3d_module(L); register_audioengine_module(L); #if CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION diff --git a/cocos/scripting/lua-bindings/manual/spine/LuaSkeletonAnimation.cpp b/cocos/scripting/lua-bindings/manual/spine/LuaSkeletonAnimation.cpp index 8049c39a5732..f090ab8ffc3d 100644 --- a/cocos/scripting/lua-bindings/manual/spine/LuaSkeletonAnimation.cpp +++ b/cocos/scripting/lua-bindings/manual/spine/LuaSkeletonAnimation.cpp @@ -48,7 +48,8 @@ LuaSkeletonAnimation::~LuaSkeletonAnimation() LuaSkeletonAnimation* LuaSkeletonAnimation::createWithFile (const char* skeletonDataFile, const char* atlasFile, float scale) { LuaSkeletonAnimation* node = new (std::nothrow) LuaSkeletonAnimation(); - spAtlas* atlas = spAtlas_createFromFile(atlasFile, nullptr); + + auto *atlas = new spine::Atlas(atlasFile, nullptr); node->initWithJsonFile(skeletonDataFile, atlas, scale); node->autorelease(); return node; diff --git a/cocos/scripting/lua-bindings/manual/spine/lua_cocos2dx_spine_manual.cpp b/cocos/scripting/lua-bindings/manual/spine/lua_cocos2dx_spine_manual.cpp index 2deee55119d5..ad4eec9109a0 100644 --- a/cocos/scripting/lua-bindings/manual/spine/lua_cocos2dx_spine_manual.cpp +++ b/cocos/scripting/lua-bindings/manual/spine/lua_cocos2dx_spine_manual.cpp @@ -99,7 +99,7 @@ static int lua_cocos2dx_CCSkeletonAnimation_createWithFile(lua_State* L) return 0; } -int executeSpineEvent(LuaSkeletonAnimation* skeletonAnimation, int handler, spEventType eventType, spTrackEntry* entry, spEvent* event = nullptr ) +int executeSpineEvent(LuaSkeletonAnimation* skeletonAnimation, int handler, spine::EventType eventType, spine::TrackEntry* entry, spine::Event* event = nullptr ) { if (nullptr == skeletonAnimation || 0 == handler) return 0; @@ -114,36 +114,36 @@ int executeSpineEvent(LuaSkeletonAnimation* skeletonAnimation, int handler, spEv int ret = 0; - std::string animationName = (entry && entry->animation) ? entry->animation->name : ""; + std::string animationName = (entry && entry->getAnimation()) ? entry->getAnimation()->getName().buffer() : ""; std::string eventTypeName = ""; switch (eventType) { - case spEventType::SP_ANIMATION_START: + case spine::EventType_Start: { eventTypeName = "start"; } break; - case spEventType::SP_ANIMATION_INTERRUPT: + case spine::EventType_Interrupt: { eventTypeName = "interrupt"; } break; - case spEventType::SP_ANIMATION_END: + case spine::EventType_End: { eventTypeName = "end"; } break; - case spEventType::SP_ANIMATION_DISPOSE: + case spine::EventType_Dispose: { eventTypeName = "dispose"; } break; - case spEventType::SP_ANIMATION_COMPLETE: + case spine::EventType_Complete: { eventTypeName = "complete"; } break; - case spEventType::SP_ANIMATION_EVENT: + case spine::EventType_Event: { eventTypeName = "event"; } @@ -155,17 +155,17 @@ int executeSpineEvent(LuaSkeletonAnimation* skeletonAnimation, int handler, spEv LuaValueDict spineEvent; spineEvent.insert(spineEvent.end(), LuaValueDict::value_type("type", LuaValue::stringValue(eventTypeName))); - spineEvent.insert(spineEvent.end(), LuaValueDict::value_type("trackIndex", LuaValue::intValue(entry->trackIndex))); + spineEvent.insert(spineEvent.end(), LuaValueDict::value_type("trackIndex", LuaValue::intValue(entry->getTrackIndex()))); spineEvent.insert(spineEvent.end(), LuaValueDict::value_type("animation", LuaValue::stringValue(animationName))); - spineEvent.insert(spineEvent.end(), LuaValueDict::value_type("loopCount", LuaValue::intValue(std::floor(entry->trackTime / entry->animationEnd)))); + spineEvent.insert(spineEvent.end(), LuaValueDict::value_type("loopCount", LuaValue::intValue(std::floor(entry->getTrackTime() / entry->getAnimationEnd())))); if (nullptr != event) { LuaValueDict eventData; - eventData.insert(eventData.end(), LuaValueDict::value_type("name", LuaValue::stringValue(event->data->name))); - eventData.insert(eventData.end(), LuaValueDict::value_type("intValue", LuaValue::intValue(event->data->intValue))); - eventData.insert(eventData.end(), LuaValueDict::value_type("floatValue", LuaValue::floatValue(event->data->floatValue))); - eventData.insert(eventData.end(), LuaValueDict::value_type("stringValue", LuaValue::stringValue(event->data->stringValue))); + eventData.insert(eventData.end(), LuaValueDict::value_type("name", LuaValue::stringValue(event->getData().getName().buffer()))); + eventData.insert(eventData.end(), LuaValueDict::value_type("intValue", LuaValue::intValue(event->getIntValue()))); + eventData.insert(eventData.end(), LuaValueDict::value_type("floatValue", LuaValue::floatValue(event->getFloatValue()))); + eventData.insert(eventData.end(), LuaValueDict::value_type("stringValue", LuaValue::stringValue(event->getStringValue().buffer()))); spineEvent.insert(spineEvent.end(), LuaValueDict::value_type("eventData", LuaValue::dictValue(eventData))); } @@ -191,52 +191,52 @@ int tolua_Cocos2d_CCSkeletonAnimation_registerSpineEventHandler00(lua_State* tol LuaSkeletonAnimation* self = (LuaSkeletonAnimation*) tolua_tousertype(tolua_S,1,0); if (NULL != self ) { int handler = ( toluafix_ref_function(tolua_S,2,0)); - spEventType eventType = static_cast((int)tolua_tonumber(tolua_S, 3, 0)); + spine::EventType eventType = static_cast((int)tolua_tonumber(tolua_S, 3, 0)); switch (eventType) { - case spEventType::SP_ANIMATION_START: + case spine::EventType_Start: { - self->setStartListener([=](spTrackEntry* entry){ + self->setStartListener([=](spine::TrackEntry* entry){ executeSpineEvent(self, handler, eventType, entry); }); ScriptHandlerMgr::getInstance()->addObjectHandler((void*)self, handler, ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_START); } break; - case spEventType::SP_ANIMATION_INTERRUPT: + case spine::EventType_Interrupt: { - self->setInterruptListener([=](spTrackEntry* entry){ + self->setInterruptListener([=](spine::TrackEntry* entry){ executeSpineEvent(self, handler, eventType, entry); }); ScriptHandlerMgr::getInstance()->addObjectHandler((void*)self, handler, ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_INTERRUPT); } break; - case spEventType::SP_ANIMATION_END: + case spine::EventType_End: { - self->setEndListener([=](spTrackEntry* entry){ + self->setEndListener([=](spine::TrackEntry* entry){ executeSpineEvent(self, handler, eventType, entry); }); ScriptHandlerMgr::getInstance()->addObjectHandler((void*)self, handler, ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_END); } break; - case spEventType::SP_ANIMATION_DISPOSE: + case spine::EventType_Dispose: { - self->setDisposeListener([=](spTrackEntry* entry){ + self->setDisposeListener([=](spine::TrackEntry* entry){ executeSpineEvent(self, handler, eventType, entry); }); ScriptHandlerMgr::getInstance()->addObjectHandler((void*)self, handler, ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_DISPOSE); } break; - case spEventType::SP_ANIMATION_COMPLETE: + case spine::EventType_Complete: { - self->setCompleteListener([=](spTrackEntry* entry){ + self->setCompleteListener([=](spine::TrackEntry* entry){ executeSpineEvent(self, handler, eventType, entry); }); ScriptHandlerMgr::getInstance()->addObjectHandler((void*)self, handler, ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_COMPLETE); } break; - case spEventType::SP_ANIMATION_EVENT: + case spine::EventType_Event: { - self->setEventListener([=](spTrackEntry* entry, spEvent* event){ + self->setEventListener([=](spine::TrackEntry* entry, spine::Event* event){ executeSpineEvent(self, handler, eventType, entry, event); }); ScriptHandlerMgr::getInstance()->addObjectHandler((void*)self, handler, ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_EVENT); @@ -270,28 +270,28 @@ int tolua_Cocos2d_CCSkeletonAnimation_unregisterSpineEventHandler00(lua_State* t { LuaSkeletonAnimation* self = (LuaSkeletonAnimation*) tolua_tousertype(tolua_S,1,0); if (NULL != self ) { - spEventType eventType = static_cast((int)tolua_tonumber(tolua_S, 2, 0)); + auto eventType = static_cast((int)tolua_tonumber(tolua_S, 2, 0)); ScriptHandlerMgr::HandlerType handlerType = ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_START; switch (eventType) { - case spEventType::SP_ANIMATION_START: + case spine::EventType_Start: handlerType = ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_START; self->setStartListener(nullptr); break; - case spEventType::SP_ANIMATION_INTERRUPT: + case spine::EventType_Interrupt: handlerType = ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_INTERRUPT; break; - case spEventType::SP_ANIMATION_END: + case spine::EventType_End: handlerType = ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_END; self->setEndListener(nullptr); break; - case spEventType::SP_ANIMATION_DISPOSE: + case spine::EventType_Dispose: handlerType = ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_DISPOSE; break; - case spEventType::SP_ANIMATION_COMPLETE: + case spine::EventType_Complete: handlerType = ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_COMPLETE; self->setCompleteListener(nullptr); break; - case spEventType::SP_ANIMATION_EVENT: + case spine::EventType_Event: handlerType = ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_EVENT; self->setEventListener(nullptr); break; diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index af87b8a9adc8..fe70570d45f5 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -156,7 +156,7 @@ list(APPEND GAME_HEADER Classes/SceneTest/SceneTest.h Classes/ReleasePoolTest/ReleasePoolTest.h Classes/InputTest/MouseTest.h -# Classes/SpineTest/SpineTest.h + Classes/SpineTest/SpineTest.h # Classes/Scene3DTest/Scene3DTest.h Classes/ParticleTest/ParticleTest.h Classes/EffectsTest/EffectsTest.h @@ -284,7 +284,7 @@ list(APPEND GAME_SOURCE Classes/SchedulerTest/SchedulerTest.cpp Classes/ShaderTest/ShaderTest.cpp Classes/ShaderTest/ShaderTest2.cpp - #Classes/SpineTest/SpineTest.cpp + Classes/SpineTest/SpineTest.cpp # Classes/Scene3DTest/Scene3DTest.cpp Classes/Sprite3DTest/DrawNode3D.cpp Classes/Sprite3DTest/Sprite3DTest.cpp diff --git a/tests/cpp-tests/Classes/SpineTest/SpineTest.cpp b/tests/cpp-tests/Classes/SpineTest/SpineTest.cpp index 340fc478012f..2aceaf992a13 100644 --- a/tests/cpp-tests/Classes/SpineTest/SpineTest.cpp +++ b/tests/cpp-tests/Classes/SpineTest/SpineTest.cpp @@ -33,6 +33,25 @@ using namespace cocos2d; using namespace std; using namespace spine; + +#define NUM_SKELETONS 50 +#define SPINNODE_SCALE_FACTOR 0.6 + +static Cocos2dTextureLoader textureLoader; + +PowInterpolation pow2(2); +PowOutInterpolation powOut2(2); +SwirlVertexEffect effect(400, powOut2); + +#if 0 +#define ENABLE_DEBUG_DRAW(skeletonNode) { \ + skeletonNode->setDebugBonesEnabled(true); \ + skeletonNode->setDebugSlotsEnabled(true); \ + } while(0) +#else +#define ENABLE_DEBUG_DRAW(skeletonNode) ((void)0) +#endif + //------------------------------------------------------------------ // // SpineTestScene @@ -65,43 +84,53 @@ bool BatchingExample::init () { _title = "BatchingExample"; // Load the texture atlas. - _atlas = spAtlas_createFromFile("spine/spineboy.atlas", 0); + // Load the texture atlas. Note that the texture loader has to live + // as long as the Atlas, as the Atlas destructor will call TextureLoader::unload. + _atlas = new (__FILE__, __LINE__) Atlas("spine/spineboy.atlas", &textureLoader); CCASSERT(_atlas, "Error reading atlas file."); - + // This attachment loader configures attachments with data needed for cocos2d-x rendering. // Do not dispose the attachment loader until the skeleton data is disposed! - _attachmentLoader = (spAttachmentLoader*)Cocos2dAttachmentLoader_create(_atlas); - + _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); + // Load the skeleton data. - spSkeletonJson* json = spSkeletonJson_createWithLoader(_attachmentLoader); - json->scale = 0.6f; // Resizes skeleton data to 60% of the size it was in Spine. - _skeletonData = spSkeletonJson_readSkeletonDataFile(json, "spine/spineboy-ess.json"); - CCASSERT(_skeletonData, json->error ? json->error : "Error reading skeleton data file."); - spSkeletonJson_dispose(json); - + SkeletonJson* json = new (__FILE__, __LINE__) SkeletonJson(_attachmentLoader); + json->setScale(SPINNODE_SCALE_FACTOR); // Resizes skeleton data to 60% of the size it was in Spine. + _skeletonData = json->readSkeletonDataFile("spine/spineboy-pro.json"); + CCASSERT(_skeletonData, json->getError().isEmpty() ? json->getError().buffer() : "Error reading skeleton data file."); + delete json; + // Setup mix times. - _stateData = spAnimationStateData_create(_skeletonData); - spAnimationStateData_setMixByName(_stateData, "walk", "jump", 0.2f); - spAnimationStateData_setMixByName(_stateData, "jump", "run", 0.2f); - + _stateData = new (__FILE__, __LINE__) AnimationStateData(_skeletonData); + _stateData->setMix("walk", "jump", 0.2f); + _stateData->setMix("jump", "run", 0.2f); + int xMin = _contentSize.width * 0.10f, xMax = _contentSize.width * 0.90f; int yMin = 0, yMax = _contentSize.height * 0.7f; - for (int i = 0; i < 50; 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", false, 3); + skeletonNode->addAnimation(0, "jump", true, RandomHelper::random_int(0, 300) / 100.0f); skeletonNode->addAnimation(0, "run", true); - + + // alternative setting two color tint for groups of 10 skeletons + // should end up with #skeletons / 10 batches + // if (j++ < 10) + // skeletonNode->setTwoColorTint(true); + // if (j == 20) j = 0; + // skeletonNode->setTwoColorTint(true); + skeletonNode->setPosition(Vec2( - RandomHelper::random_int(xMin, xMax), - RandomHelper::random_int(yMin, yMax) - )); - skeletonNode->setScale(0.8); + RandomHelper::random_int(xMin, xMax), + RandomHelper::random_int(yMin, yMax) + )); addChild(skeletonNode); } + + scheduleUpdate(); return true; } @@ -109,10 +138,10 @@ bool BatchingExample::init () { BatchingExample::~BatchingExample () { // SkeletonAnimation instances are cocos2d-x nodes and are disposed of automatically as normal, but the data created // manually to be shared across multiple SkeletonAnimations needs to be disposed of manually. - spSkeletonData_dispose(_skeletonData); - spAnimationStateData_dispose(_stateData); - spAttachmentLoader_dispose(_attachmentLoader); - spAtlas_dispose(_atlas); + delete _skeletonData; + delete _stateData; + delete _attachmentLoader; + delete _atlas; } // CoinExample @@ -122,11 +151,14 @@ bool CoinExample::init () { _title = "CoinExample"; - skeletonNode = SkeletonAnimation::createWithJsonFile("spine/coin-pro.json", "spine/coin.atlas", 1.f); - skeletonNode->setAnimation(0, "rotate", true); - - skeletonNode->setPosition(Vec2(_contentSize.width / 2, 100)); + skeletonNode = SkeletonAnimation::createWithBinaryFile("spine/coin-pro.skel", "spine/coin.atlas", 1); + skeletonNode->setAnimation(0, "animation", true); + + skeletonNode->setPosition(Vec2(_contentSize.width / 2, _contentSize.height / 2)); + skeletonNode->setScale(SPINNODE_SCALE_FACTOR); addChild(skeletonNode); + + scheduleUpdate(); return true; } @@ -143,7 +175,7 @@ bool GoblinsExample::init () { skeletonNode->setSkin("goblin"); skeletonNode->setPosition(Vec2(_contentSize.width / 2, 20)); - skeletonNode->setScale(0.6); + skeletonNode->setScale(SPINNODE_SCALE_FACTOR); addChild(skeletonNode); return true; } @@ -155,14 +187,22 @@ bool RaptorExample::init () { _title = "RaptorExample"; skeletonNode = SkeletonAnimation::createWithJsonFile("spine/raptor-pro.json", "spine/raptor.atlas", 0.5f); - skeletonNode->setAnimation(0, "walk", true); - skeletonNode->setAnimation(1, "empty", false); - skeletonNode->addAnimation(1, "gungrab", false, 2); + skeletonNode->addAnimation(1, "gun-grab", false, 2); + skeletonNode->setTwoColorTint(true); + ENABLE_DEBUG_DRAW(skeletonNode); + + effect.setCenterY(200); + swirlTime = 0; + + skeletonNode->setVertexEffect(&effect); + skeletonNode->setPosition(Vec2(_contentSize.width / 2, 20)); - skeletonNode->setScale(0.6); + skeletonNode->setScale(SPINNODE_SCALE_FACTOR); addChild(skeletonNode); + + scheduleUpdate(); return true; } @@ -172,43 +212,45 @@ bool SpineboyExample::init () { if (!SpineTestLayer::init()) return false; _title = "SpineboyExample"; - skeletonNode = SkeletonAnimation::createWithJsonFile("spine/spineboy-ess.json", "spine/spineboy.atlas", 0.6f); - skeletonNode->setStartListener( [] (spTrackEntry* entry) { - log("%d start: %s", entry->trackIndex, entry->animation->name); + + skeletonNode = SkeletonAnimation::createWithJsonFile("spine/spineboy-pro.json", "spine/spineboy.atlas", 0.6f); + + skeletonNode->setStartListener([](TrackEntry* entry) { + log("%d start: %s", entry->getTrackIndex(), entry->getAnimation()->getName().buffer()); }); - skeletonNode->setInterruptListener( [] (spTrackEntry* entry) { - log("%d interrupt", entry->trackIndex); + skeletonNode->setInterruptListener([](TrackEntry* entry) { + log("%d interrupt", entry->getTrackIndex()); }); - skeletonNode->setEndListener( [] (spTrackEntry* entry) { - log("%d end", entry->trackIndex); + skeletonNode->setEndListener([](TrackEntry* entry) { + log("%d end", entry->getTrackIndex()); }); - skeletonNode->setCompleteListener( [] (spTrackEntry* entry) { - log("%d complete", entry->trackIndex); + skeletonNode->setCompleteListener([](TrackEntry* entry) { + log("%d complete", entry->getTrackIndex()); }); - skeletonNode->setDisposeListener( [] (spTrackEntry* entry) { - log("%d dispose", entry->trackIndex); + skeletonNode->setDisposeListener([](TrackEntry* entry) { + log("%d dispose", entry->getTrackIndex()); }); - skeletonNode->setEventListener( [] (spTrackEntry* entry, spEvent* event) { - log("%d event: %s, %d, %f, %s", entry->trackIndex, event->data->name, event->intValue, event->floatValue, event->stringValue); + skeletonNode->setEventListener([](TrackEntry* entry, spine::Event* event) { + log("%d event: %s, %d, %f, %s", entry->getTrackIndex(), event->getData().getName().buffer(), event->getIntValue(), event->getFloatValue(), event->getStringValue().buffer()); }); - + skeletonNode->setMix("walk", "jump", 0.4); skeletonNode->setMix("jump", "run", 0.4); skeletonNode->setAnimation(0, "walk", true); - spTrackEntry* jumpEntry = skeletonNode->addAnimation(0, "jump", false, 1); + TrackEntry* jumpEntry = skeletonNode->addAnimation(0, "jump", false, 1); skeletonNode->addAnimation(0, "run", true); - - skeletonNode->setTrackStartListener(jumpEntry, [] (spTrackEntry* entry) { + + skeletonNode->setTrackStartListener(jumpEntry, [](TrackEntry* entry) { log("jumped!"); }); - + // skeletonNode->addAnimation(1, "test", true); // skeletonNode->runAction(RepeatForever::create(Sequence::create(FadeOut::create(1), FadeIn::create(1), DelayTime::create(5), NULL))); - + skeletonNode->setPosition(Vec2(_contentSize.width / 2, 20)); - skeletonNode->setScale(0.8); + skeletonNode->setScale(SPINNODE_SCALE_FACTOR); addChild(skeletonNode); - + scheduleUpdate(); return true; @@ -225,12 +267,16 @@ bool TankExample::init () { if (!SpineTestLayer::init()) return false; _title = "TankExample"; - skeletonNode = SkeletonAnimation::createWithJsonFile("spine/tank-pro.json", "spine/tank.atlas", 0.5f); - skeletonNode->setAnimation(0, "drive", true); - + skeletonNode = SkeletonAnimation::createWithBinaryFile("spine/tank-pro.skel", "spine/tank.atlas", 0.5f); + skeletonNode->setAnimation(0, "shoot", true); + + ENABLE_DEBUG_DRAW(skeletonNode); + skeletonNode->setPosition(Vec2(_contentSize.width / 2 + 400, 20)); - skeletonNode->setScale(0.8); + skeletonNode->setScale(SPINNODE_SCALE_FACTOR); addChild(skeletonNode); + + scheduleUpdate(); return true; } diff --git a/tests/cpp-tests/Classes/SpineTest/SpineTest.h b/tests/cpp-tests/Classes/SpineTest/SpineTest.h index d9c9b049e676..7c0bdd12baff 100644 --- a/tests/cpp-tests/Classes/SpineTest/SpineTest.h +++ b/tests/cpp-tests/Classes/SpineTest/SpineTest.h @@ -50,10 +50,10 @@ class BatchingExample: public SpineTestLayer { virtual bool init (); protected: - spAtlas* _atlas; - spAttachmentLoader* _attachmentLoader; - spSkeletonData* _skeletonData; - spAnimationStateData* _stateData; + spine::Atlas * _atlas; + spine::AttachmentLoader* _attachmentLoader; + spine::SkeletonData* _skeletonData; + spine::AnimationStateData* _stateData; }; class CoinExample: public SpineTestLayer @@ -85,6 +85,7 @@ class RaptorExample : public SpineTestLayer { private: spine::SkeletonAnimation* skeletonNode; + float swirlTime; }; class SpineboyExample : public SpineTestLayer { diff --git a/tests/cpp-tests/Classes/controller.cpp b/tests/cpp-tests/Classes/controller.cpp index 96608778fc14..6c8b21695fd6 100644 --- a/tests/cpp-tests/Classes/controller.cpp +++ b/tests/cpp-tests/Classes/controller.cpp @@ -93,7 +93,7 @@ class RootTests : public TestList addTest("Node: Physics3D", []() { return new Physics3DTests(); } ); addTest("Node: RenderTexture", [](){return new RenderTextureTests(); }); addTest("Node: Scene", [](){return new SceneTests(); }); - // addTest("Node: Spine", [](){return new SpineTests(); }); + addTest("Node: Spine", [](){return new SpineTests(); }); addTest("Node: Sprite", [](){return new SpriteTests(); }); addTest("Node: Sprite3D", [](){ return new Sprite3DTests(); }); addTest("Node: SpritePolygon", [](){return new (std::nothrow) SpritePolygonTest(); }); diff --git a/tests/cpp-tests/Resources/spine/coin-pro.skel b/tests/cpp-tests/Resources/spine/coin-pro.skel new file mode 100644 index 0000000000000000000000000000000000000000..ba4e2096a6ac872cec40f2753df8868dd8a543c2 GIT binary patch literal 2569 zcmah}4NOy46u$4Zlu?*qz!|}*L8E{v3>%$Mc=z>H7cl-zBFSRdVv)9Vuoh`M9Hder zznZOEMjSJmsgP|!B@>ek-3Ij4&15SRw`4dcCTs}=B_wKO#u>YN?xU{-%D74U-o591 z-}%nZeao}Ts&?(mvhUk$-nl*7P+Y!!&+Cq|itOtA{K7bG=Bt^THfxHF4uihU&FDWt z2t`L#+H4N;A}{Pl2!1nG>_MBwsxejCtTTM6;~eLhgXNZr3X9dugme54uia8&w1@jf z=1rg?z*$pib6QIj;gE>*i6MmDQE9N4OC1Y?RQ6Jf)kwMZ_BDg>5|Lb`mf52Me`W<> zMF@yy%T@-RXB&`&rd8HBjE5c0N+Z+Q*hrZrXUSG>GuzPOP)jsaG1J`K900X1&9-O& zz{qW3DIxijPmq+eikuw)qqK$gsSpz>CkR8zB0c2t=$aAIzmE`5FwC6vByaK9?IjfK zmg=fc2!U93PMXsprXRsZkoiS0zYr-YJY6z&#$wxhGf}d@aF~LPT&MY9-U zSTyzp;)a2hp++E;avjR``v>%Kg2(%tR;yR99ujw`bM*=yi6@5C!sLn`@mg7f@KaKa zI69IibX0VR0l_02-aFxYVR^c+d_|8hE~!R$x}wAPV(C<`;3&}t$D8ji?^a#%h$J=RB!hOQorqv3A{sG}i zPl50i$^TaSkZ>%%NO(xz^Lq;P>>np}zj})FF(y+K6LwLhrolX7|7|z?gbzu-oYI6K z#HbBc%R2^##b#v{EE@y0+6ge>EtgYp>DA2XF4A()*jtU6m%*FmHaz-(OID=H#UimoH0$Ee;2VpZS5f4nk z*eqA~_s8^L#RDt%NV|c9{1#ap`8Orc zwUd3^`nHF7>9PxWNlp_S_N6?aaab-j`UE$e){hUc?b!9igUOur5eJE8MnO;Atjp;& z@4>0p&vAoaPvCyywf~hLr5JCF?dD!9Y{eQEgIBvR;8Bx_JG-b*GJdV2cx%kgw?w6KCoH_O;&hZLx zJNve#I3=Thw*p5c#9&?gCA|M$7oU-x*f*UA%aIpY!9fY{u!>=#B&+m4#~E(AZ=%}- m=D9{vAyN5Re?%isPUl62-vB@>Ar6CBxLpV;MBbq`2>l1u^AM>3 literal 0 HcmV?d00001 diff --git a/tests/cpp-tests/Resources/spine/coin.atlas b/tests/cpp-tests/Resources/spine/coin.atlas index 4b7fd8e37739..3b688431165e 100644 --- a/tests/cpp-tests/Resources/spine/coin.atlas +++ b/tests/cpp-tests/Resources/spine/coin.atlas @@ -1,26 +1,54 @@ coin.png -size: 1024,256 +size: 1024,1024 format: RGBA8888 filter: Linear,Linear repeat: none -coin +coin-front-logo rotate: false - xy: 2, 2 - size: 259, 245 - orig: 259, 245 + xy: 2, 609 + size: 305, 302 + orig: 305, 302 + offset: 0, 0 + index: -1 +coin-front-shine-logo + rotate: false + xy: 309, 629 + size: 282, 282 + orig: 282, 282 + offset: 0, 0 + index: -1 +coin-front-shine-spineboy + rotate: false + xy: 2, 21 + size: 282, 282 + orig: 282, 282 offset: 0, 0 index: -1 -coin-invert +coin-front-spineboy rotate: false - xy: 263, 2 - size: 259, 245 - orig: 259, 245 + xy: 2, 305 + size: 305, 302 + orig: 305, 302 + offset: 0, 0 + index: -1 +coin-side-round + rotate: false + xy: 309, 345 + size: 144, 282 + orig: 144, 282 + offset: 0, 0 + index: -1 +coin-side-straight + rotate: true + xy: 2, 2 + size: 17, 282 + orig: 17, 282 offset: 0, 0 index: -1 shine rotate: false - xy: 524, 2 + xy: 593, 666 size: 72, 245 orig: 72, 245 offset: 0, 0 diff --git a/tests/cpp-tests/Resources/spine/coin.png b/tests/cpp-tests/Resources/spine/coin.png index 7d14a6b6f98ce71fe63666b0f08922ddbab13bf7..03237e9b9d1d26425486d0f62ecc290b2d012386 100644 GIT binary patch literal 402363 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

w1&aaQMdJ;~O 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 NLGp=(&M`Gcp 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⋙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