Procházet zdrojové kódy

增加cocos2dx和qt5.14头文件

xx před 2 roky
rodič
revize
4f893f4e1a
100 změnil soubory, kde provedl 45846 přidání a 0 odebrání
  1. 16 0
      3rdparty/cocos2dx/CMakeLists.txt
  2. 38 0
      3rdparty/cocos2dx/include/button.h
  3. 78 0
      3rdparty/cocos2dx/include/c2d.h
  4. 320 0
      3rdparty/cocos2dx/include/cocos/2d/CCAction.cpp
  5. 456 0
      3rdparty/cocos2dx/include/cocos/2d/CCAction.h
  6. 247 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionCamera.cpp
  7. 183 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionCamera.h
  8. 529 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionCatmullRom.cpp
  9. 348 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionCatmullRom.h
  10. 325 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionEase.cpp
  11. 558 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionEase.h
  12. 455 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionGrid.cpp
  13. 422 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionGrid.h
  14. 739 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionGrid3D.cpp
  15. 620 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionGrid3D.h
  16. 422 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionInstant.cpp
  17. 412 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionInstant.h
  18. 2820 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionInterval.cpp
  19. 1652 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionInterval.h
  20. 504 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionManager.cpp
  21. 216 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionManager.h
  22. 134 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionPageTurn3D.cpp
  23. 74 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionPageTurn3D.h
  24. 133 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionProgressTimer.cpp
  25. 134 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionProgressTimer.h
  26. 879 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionTiledGrid.cpp
  27. 610 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionTiledGrid.h
  28. 81 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionTween.cpp
  29. 128 0
      3rdparty/cocos2dx/include/cocos/2d/CCActionTween.h
  30. 203 0
      3rdparty/cocos2dx/include/cocos/2d/CCAnimation.cpp
  31. 325 0
      3rdparty/cocos2dx/include/cocos/2d/CCAnimation.h
  32. 245 0
      3rdparty/cocos2dx/include/cocos/2d/CCAnimationCache.cpp
  33. 133 0
      3rdparty/cocos2dx/include/cocos/2d/CCAnimationCache.h
  34. 284 0
      3rdparty/cocos2dx/include/cocos/2d/CCAtlasNode.cpp
  35. 155 0
      3rdparty/cocos2dx/include/cocos/2d/CCAtlasNode.h
  36. 722 0
      3rdparty/cocos2dx/include/cocos/2d/CCAutoPolygon.cpp
  37. 285 0
      3rdparty/cocos2dx/include/cocos/2d/CCAutoPolygon.h
  38. 459 0
      3rdparty/cocos2dx/include/cocos/2d/CCCamera.cpp
  39. 317 0
      3rdparty/cocos2dx/include/cocos/2d/CCCamera.h
  40. 506 0
      3rdparty/cocos2dx/include/cocos/2d/CCCameraBackgroundBrush.cpp
  41. 316 0
      3rdparty/cocos2dx/include/cocos/2d/CCCameraBackgroundBrush.h
  42. 376 0
      3rdparty/cocos2dx/include/cocos/2d/CCClippingNode.cpp
  43. 173 0
      3rdparty/cocos2dx/include/cocos/2d/CCClippingNode.h
  44. 106 0
      3rdparty/cocos2dx/include/cocos/2d/CCClippingRectangleNode.cpp
  45. 111 0
      3rdparty/cocos2dx/include/cocos/2d/CCClippingRectangleNode.h
  46. 153 0
      3rdparty/cocos2dx/include/cocos/2d/CCComponent.cpp
  47. 95 0
      3rdparty/cocos2dx/include/cocos/2d/CCComponent.h
  48. 150 0
      3rdparty/cocos2dx/include/cocos/2d/CCComponentContainer.cpp
  49. 79 0
      3rdparty/cocos2dx/include/cocos/2d/CCComponentContainer.h
  50. 771 0
      3rdparty/cocos2dx/include/cocos/2d/CCDrawNode.cpp
  51. 359 0
      3rdparty/cocos2dx/include/cocos/2d/CCDrawNode.h
  52. 914 0
      3rdparty/cocos2dx/include/cocos/2d/CCFastTMXLayer.cpp
  53. 361 0
      3rdparty/cocos2dx/include/cocos/2d/CCFastTMXLayer.h
  54. 260 0
      3rdparty/cocos2dx/include/cocos/2d/CCFastTMXTiledMap.cpp
  55. 248 0
      3rdparty/cocos2dx/include/cocos/2d/CCFastTMXTiledMap.h
  56. 24 0
      3rdparty/cocos2dx/include/cocos/2d/CCFont.cpp
  57. 51 0
      3rdparty/cocos2dx/include/cocos/2d/CCFont.h
  58. 548 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontAtlas.cpp
  59. 165 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontAtlas.h
  60. 264 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontAtlasCache.cpp
  61. 77 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontAtlasCache.h
  62. 148 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontCharMap.cpp
  63. 72 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontCharMap.h
  64. 795 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontFNT.cpp
  65. 78 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontFNT.h
  66. 669 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontFreeType.cpp
  67. 111 0
      3rdparty/cocos2dx/include/cocos/2d/CCFontFreeType.h
  68. 821 0
      3rdparty/cocos2dx/include/cocos/2d/CCGrid.cpp
  69. 307 0
      3rdparty/cocos2dx/include/cocos/2d/CCGrid.h
  70. 2418 0
      3rdparty/cocos2dx/include/cocos/2d/CCLabel.cpp
  71. 828 0
      3rdparty/cocos2dx/include/cocos/2d/CCLabel.h
  72. 278 0
      3rdparty/cocos2dx/include/cocos/2d/CCLabelAtlas.cpp
  73. 133 0
      3rdparty/cocos2dx/include/cocos/2d/CCLabelAtlas.h
  74. 439 0
      3rdparty/cocos2dx/include/cocos/2d/CCLabelTextFormatter.cpp
  75. 24 0
      3rdparty/cocos2dx/include/cocos/2d/CCLabelTextFormatter.h
  76. 1090 0
      3rdparty/cocos2dx/include/cocos/2d/CCLayer.cpp
  77. 623 0
      3rdparty/cocos2dx/include/cocos/2d/CCLayer.h
  78. 208 0
      3rdparty/cocos2dx/include/cocos/2d/CCLight.cpp
  79. 295 0
      3rdparty/cocos2dx/include/cocos/2d/CCLight.h
  80. 575 0
      3rdparty/cocos2dx/include/cocos/2d/CCMenu.cpp
  81. 195 0
      3rdparty/cocos2dx/include/cocos/2d/CCMenu.h
  82. 841 0
      3rdparty/cocos2dx/include/cocos/2d/CCMenuItem.cpp
  83. 509 0
      3rdparty/cocos2dx/include/cocos/2d/CCMenuItem.h
  84. 417 0
      3rdparty/cocos2dx/include/cocos/2d/CCMotionStreak.cpp
  85. 197 0
      3rdparty/cocos2dx/include/cocos/2d/CCMotionStreak.h
  86. 2199 0
      3rdparty/cocos2dx/include/cocos/2d/CCNode.cpp
  87. 1978 0
      3rdparty/cocos2dx/include/cocos/2d/CCNode.h
  88. 184 0
      3rdparty/cocos2dx/include/cocos/2d/CCNodeGrid.cpp
  89. 111 0
      3rdparty/cocos2dx/include/cocos/2d/CCNodeGrid.h
  90. 170 0
      3rdparty/cocos2dx/include/cocos/2d/CCParallaxNode.cpp
  91. 121 0
      3rdparty/cocos2dx/include/cocos/2d/CCParallaxNode.h
  92. 556 0
      3rdparty/cocos2dx/include/cocos/2d/CCParticleBatchNode.cpp
  93. 188 0
      3rdparty/cocos2dx/include/cocos/2d/CCParticleBatchNode.h
  94. 1236 0
      3rdparty/cocos2dx/include/cocos/2d/CCParticleExamples.cpp
  95. 456 0
      3rdparty/cocos2dx/include/cocos/2d/CCParticleExamples.h
  96. 1404 0
      3rdparty/cocos2dx/include/cocos/2d/CCParticleSystem.cpp
  97. 999 0
      3rdparty/cocos2dx/include/cocos/2d/CCParticleSystem.h
  98. 615 0
      3rdparty/cocos2dx/include/cocos/2d/CCParticleSystemQuad.cpp
  99. 186 0
      3rdparty/cocos2dx/include/cocos/2d/CCParticleSystemQuad.h
  100. 604 0
      3rdparty/cocos2dx/include/cocos/2d/CCProgressTimer.cpp

+ 16 - 0
3rdparty/cocos2dx/CMakeLists.txt

@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.5)
+
+# 获取上级目录名做为库名
+get_filename_component(CURRENT_DIR ${CMAKE_CURRENT_SOURCE_DIR} ABSOLUTE)
+get_filename_component(LIBRARY_NAME ${CURRENT_DIR} NAME)
+
+file(GLOB SOURCE_FILES "${PROJECT_SOURCE_DIR}/3rdparty/${LIBRARY_NAME}/src/*.*")
+file(GLOB INCLUDE_FILES "${PROJECT_SOURCE_DIR}/3rdparty/${LIBRARY_NAME}/include/*.*")
+
+# 创建库
+add_library(${LIBRARY_NAME} ${SOURCE_FILES} ${INCLUDE_FILES})
+ 
+# 将包含目录与目标相关联,这样只有在编译此库时才会包含这些目录
+target_include_directories(${LIBRARY_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/3rdparty/${LIBRARY_NAME}/include")
+target_include_directories(${LIBRARY_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/3rdparty/${LIBRARY_NAME}/include/cocos")
+

+ 38 - 0
3rdparty/cocos2dx/include/button.h

@@ -0,0 +1,38 @@
+#pragma once
+#include "cocos2d.h"
+#include "cocos/ui/CocosGUI.h"
+#include <functional>
+namespace ylib
+{
+	namespace c2d
+	{
+		/// <summary>
+		/// Cocos2dx按钮
+		/// </summary>
+		class Button :public cocos2d::ui::Button {
+		public:
+			~Button();
+			/// <summary>
+			/// 创建按钮
+			/// </summary>
+			/// <param name="normal"></param>
+			/// <param name="clicked"></param>
+			/// <returns></returns>
+			static ylib::c2d::Button* create(const std::string& normal, const std::string& clicked);
+			/// <summary>
+			/// 绑定点击回调
+			/// </summary>
+			/// <param name="callback"></param>
+			void on_clicked(std::function<void(ylib::c2d::Button*)> callback) { m_callback = callback; }
+		private:
+			Button(const std::string& normal, const std::string& clicked);
+			bool init();
+		private:
+			// 点击回调
+			std::function<void(ylib::c2d::Button*)> m_callback;
+
+			std::string m_image_normal;
+			std::string m_image_clicked;
+		};
+	}
+}

+ 78 - 0
3rdparty/cocos2dx/include/c2d.h

@@ -0,0 +1,78 @@
+#pragma once
+#include "cocos2d.h"
+#include "cocos/ui/CocosGUI.h"
+#include "button.h"
+#include <iostream>
+namespace ylib
+{
+	namespace c2d
+	{
+		/// <summary>
+		/// 创建精灵
+		/// </summary>
+		/// <param name="filepath"></param>
+		/// <param name="center"></param>
+		/// <returns></returns>
+		cocos2d::Sprite* createSprite(const std::string& filepath, bool center = false);
+		/// <summary>
+		/// 创建单行编辑组件
+		/// </summary>
+		/// <param name="size"></param>
+		/// <returns></returns>
+		cocos2d::ui::EditBox* createEdit(cocos2d::Size size, const std::string& normal = "");
+		/// <summary>
+		/// 创建按钮
+		/// </summary>
+		/// <param name="normal"></param>
+		/// <param name="clicked"></param>
+		/// <returns></returns>
+		ylib::c2d::Button* createButton(const std::string& normal, const std::string& clicked);
+		/// <summary>
+		/// 创建标签
+		/// </summary>
+		/// <param name="title"></param>
+		/// <param name="font"></param>
+		/// <param name="size"></param>
+		/// <returns></returns>
+		cocos2d::Label* createLabel(const std::string& title, const std::string& font, int size);
+		/// <summary>
+		/// 设置所有透明度(包括子节点)
+		/// </summary>
+		void setAllOpacity(cocos2d::Node* node, int opacity);
+		/// <summary>
+		/// 动画展示弹窗
+		/// </summary>
+		/// <param name="node"></param>
+		void showDlgAni(cocos2d::Node* node);
+		void hideDlgAni(cocos2d::Node* node);
+		/// <summary>
+		/// 居中
+		/// </summary>
+		/// <param name="node"></param>
+		void center(cocos2d::Node* node, cocos2d::Node* parent = nullptr);
+		float centerX(cocos2d::Node* node, cocos2d::Node* parent = nullptr);
+		float centerY(cocos2d::Node* node, cocos2d::Node* parent = nullptr);
+		/// <summary>
+		/// 设置调试背景色(RELEASE无效)
+		/// </summary>
+		void setDebugColor(cocos2d::Node* node, cocos2d::Color4B color = cocos2d::Color4B::BLUE);
+		/// <summary>
+		/// 设置背景色
+		/// </summary>
+		/// <param name="node"></param>
+		/// <param name="color"></param>
+		void setBGColor(cocos2d::Node* node, cocos2d::Color4B color = cocos2d::Color4B::BLUE);
+		/// <summary>
+		/// 转换为Color4B颜色
+		/// </summary>
+		/// <param name="hex"></param>
+		cocos2d::Color4B toC4B(const std::string& hex);
+		/// <summary>
+		/// 切换场景
+		/// </summary>
+		/// <param name="scene"></param>
+		void switchScene(cocos2d::Scene* scene);
+	}
+}
+
+

+ 320 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAction.cpp

@@ -0,0 +1,320 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCAction.h"
+#include "2d/CCActionInterval.h"
+#include "2d/CCNode.h"
+#include "base/CCDirector.h"
+#include "base/ccUTF8.h"
+
+NS_CC_BEGIN
+//
+// Action Base Class
+//
+
+Action::Action()
+:_originalTarget(nullptr)
+,_target(nullptr)
+,_tag(Action::INVALID_TAG)
+,_flags(0)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    ScriptEngineProtocol* engine = ScriptEngineManager::getInstance()->getScriptEngine();
+    _scriptType = engine != nullptr ? engine->getScriptType() : kScriptTypeNone;
+#endif
+}
+
+Action::~Action()
+{
+    CCLOGINFO("deallocing Action: %p - tag: %i", this, _tag);
+}
+
+std::string Action::description() const
+{
+    return StringUtils::format("<Action | Tag = %d", _tag);
+}
+
+void Action::startWithTarget(Node *aTarget)
+{
+    _originalTarget = _target = aTarget;
+}
+
+void Action::stop()
+{
+    _target = nullptr;
+}
+
+bool Action::isDone() const
+{
+    return true;
+}
+
+void Action::step(float /*dt*/)
+{
+    CCLOG("[Action step]. override me");
+}
+
+void Action::update(float /*time*/)
+{
+    CCLOG("[Action update]. override me");
+}
+
+//
+// Speed
+//
+Speed::Speed()
+: _speed(0.0)
+, _innerAction(nullptr)
+{
+}
+
+Speed::~Speed()
+{
+    CC_SAFE_RELEASE(_innerAction);
+}
+
+Speed* Speed::create(ActionInterval* action, float speed)
+{
+    Speed *ret = new (std::nothrow) Speed();
+    if (ret && ret->initWithAction(action, speed))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool Speed::initWithAction(ActionInterval *action, float speed)
+{
+    CCASSERT(action != nullptr, "action must not be NULL");
+    if (action == nullptr)
+    {
+        log("Speed::initWithAction error: action is nullptr!");
+        return false;
+    }
+    
+    action->retain();
+    _innerAction = action;
+    _speed = speed;
+    return true;
+}
+
+Speed *Speed::clone() const
+{
+    // no copy constructor
+    if (_innerAction)
+        return Speed::create(_innerAction->clone(), _speed);
+    
+    return nullptr;
+}
+
+void Speed::startWithTarget(Node* target)
+{
+    if (target && _innerAction)
+    {
+        Action::startWithTarget(target);
+        _innerAction->startWithTarget(target);
+    }
+    else
+        log("Speed::startWithTarget error: target(%p) or _innerAction(%p) is nullptr!", target, _innerAction);
+}
+
+void Speed::stop()
+{
+    if (_innerAction)
+        _innerAction->stop();
+    
+    Action::stop();
+}
+
+void Speed::step(float dt)
+{
+    _innerAction->step(dt * _speed);
+}
+
+bool Speed::isDone() const
+{
+    return _innerAction->isDone();
+}
+
+Speed *Speed::reverse() const
+{
+    if (_innerAction)
+        return Speed::create(_innerAction->reverse(), _speed);
+    
+    return nullptr;
+}
+
+void Speed::setInnerAction(ActionInterval *action)
+{
+    if (_innerAction != action)
+    {
+        CC_SAFE_RELEASE(_innerAction);
+        _innerAction = action;
+        CC_SAFE_RETAIN(_innerAction);
+    }
+}
+
+//
+// Follow
+//
+Follow::~Follow()
+{
+    CC_SAFE_RELEASE(_followedNode);
+}
+
+Follow* Follow::create(Node *followedNode, const Rect& rect/* = Rect::ZERO*/)
+{
+    return createWithOffset(followedNode, 0.0, 0.0,rect);
+}
+
+Follow* Follow::createWithOffset(Node* followedNode,float xOffset,float yOffset,const Rect& rect/*= Rect::ZERO*/){
+    
+    
+    Follow *follow = new (std::nothrow) Follow();
+    
+    bool valid;
+    
+    if(follow)
+        valid = follow->initWithTargetAndOffset(followedNode, xOffset, yOffset,rect);
+
+    if (follow && valid)
+    {
+        follow->autorelease();
+        return follow;
+    }
+    
+    delete follow;
+    return nullptr;
+    
+}
+Follow* Follow::clone() const
+{
+    // no copy constructor
+    return Follow::createWithOffset(_followedNode, _offsetX,_offsetY,_worldRect);
+    
+}
+
+Follow* Follow::reverse() const
+{
+    return clone();
+}
+
+bool Follow::initWithTargetAndOffset(Node *followedNode, float xOffset,float yOffset,const Rect& rect)
+{
+    CCASSERT(followedNode != nullptr, "FollowedNode can't be NULL");
+    if(followedNode == nullptr)
+    {
+        log("Follow::initWithTarget error: followedNode is nullptr!");
+        return false;
+    }
+ 
+    followedNode->retain();
+    _followedNode = followedNode;
+    _worldRect = rect;
+    _boundarySet = !rect.equals(Rect::ZERO);
+    _boundaryFullyCovered = false;
+
+    Size winSize = Director::getInstance()->getWinSize();
+    _fullScreenSize.set(winSize.width, winSize.height);
+    _halfScreenSize = _fullScreenSize * 0.5f;
+    _offsetX=xOffset;
+    _offsetY=yOffset;
+    _halfScreenSize.x += _offsetX;
+    _halfScreenSize.y += _offsetY;
+    
+    if (_boundarySet)
+    {
+        _leftBoundary = -((rect.origin.x+rect.size.width) - _fullScreenSize.x);
+        _rightBoundary = -rect.origin.x ;
+        _topBoundary = -rect.origin.y;
+        _bottomBoundary = -((rect.origin.y+rect.size.height) - _fullScreenSize.y);
+
+        if(_rightBoundary < _leftBoundary)
+        {
+            // screen width is larger than world's boundary width
+            //set both in the middle of the world
+            _rightBoundary = _leftBoundary = (_leftBoundary + _rightBoundary) / 2;
+        }
+        if(_topBoundary < _bottomBoundary)
+        {
+            // screen width is larger than world's boundary width
+            //set both in the middle of the world
+            _topBoundary = _bottomBoundary = (_topBoundary + _bottomBoundary) / 2;
+        }
+
+        if( (_topBoundary == _bottomBoundary) && (_leftBoundary == _rightBoundary) )
+        {
+            _boundaryFullyCovered = true;
+        }
+    }
+    
+    return true;
+}
+
+bool Follow::initWithTarget(Node *followedNode, const Rect& rect /*= Rect::ZERO*/){
+    
+    return initWithTargetAndOffset(followedNode, 0.0, 0.0,rect);
+    
+}
+void Follow::step(float /*dt*/)
+{
+    if(_boundarySet)
+    {
+        // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
+        if(_boundaryFullyCovered)
+        {
+            return;
+        }
+
+        Vec2 tempPos = _halfScreenSize - _followedNode->getPosition();
+
+        _target->setPosition(clampf(tempPos.x, _leftBoundary, _rightBoundary),
+                                   clampf(tempPos.y, _bottomBoundary, _topBoundary));
+    }
+    else
+    {
+        _target->setPosition(_halfScreenSize - _followedNode->getPosition());
+    }
+}
+
+bool Follow::isDone() const
+{
+    return ( !_followedNode->isRunning() );
+}
+
+void Follow::stop()
+{
+    _target = nullptr;
+    Action::stop();
+}
+
+NS_CC_END
+
+

+ 456 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAction.h

@@ -0,0 +1,456 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef __ACTIONS_CCACTION_H__
+#define __ACTIONS_CCACTION_H__
+
+#include "base/CCRef.h"
+#include "math/CCGeometry.h"
+#include "base/CCScriptSupport.h"
+
+NS_CC_BEGIN
+
+class Node;
+
+enum {
+    kActionUpdate
+};
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/** 
+ * @brief Base class for Action objects.
+ */
+class CC_DLL Action : public Ref, public Clonable
+{
+public:
+    /** Default tag used for all the actions. */
+    static const int INVALID_TAG = -1;
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual std::string description() const;
+
+    /** Returns a clone of action.
+     *
+     * @return A clone action.
+     */
+    virtual Action* clone() const
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+
+    /** Returns a new action that performs the exact reverse of the action. 
+     *
+     * @return A new action that performs the exact reverse of the action.
+     * @js NA
+     */
+    virtual Action* reverse() const
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+
+    /** Return true if the action has finished. 
+     * 
+     * @return Is true if the action has finished.
+     */
+    virtual bool isDone() const;
+
+    /** Called before the action start. It will also set the target. 
+     *
+     * @param target A certain target.
+     */
+    virtual void startWithTarget(Node *target);
+
+    /** 
+     * Called after the action has finished. It will set the 'target' to nil.
+     * IMPORTANT: You should never call "Action::stop()" manually. Instead, use: "target->stopAction(action);".
+     */
+    virtual void stop();
+
+    /** Called every frame with it's delta time, dt in seconds. DON'T override unless you know what you are doing. 
+     *
+     * @param dt In seconds.
+     */
+    virtual void step(float dt);
+
+    /** 
+     * Called once per frame. time a value between 0 and 1.
+
+     * For example:
+     * - 0 Means that the action just started.
+     * - 0.5 Means that the action is in the middle.
+     * - 1 Means that the action is over.
+     *
+     * @param time A value between 0 and 1.
+     */
+    virtual void update(float time);
+    /** Return certain target.
+     *
+     * @return A certain target.
+     */
+    Node* getTarget() const { return _target; }
+    /** The action will modify the target properties. 
+     *
+     * @param target A certain target.
+     */
+    void setTarget(Node *target) { _target = target; }
+    /** Return a original Target. 
+     *
+     * @return A original Target.
+     */
+    Node* getOriginalTarget() const { return _originalTarget; }
+    /** 
+     * Set the original target, since target can be nil.
+     * Is the target that were used to run the action. Unless you are doing something complex, like ActionManager, you should NOT call this method.
+     * The target is 'assigned', it is not 'retained'.
+     * @since v0.8.2
+     *
+     * @param originalTarget Is 'assigned', it is not 'retained'.
+     */
+    void setOriginalTarget(Node *originalTarget) { _originalTarget = originalTarget; }
+    /** Returns a tag that is used to identify the action easily. 
+     *
+     * @return A tag.
+     */
+    int getTag() const { return _tag; }
+    /** Changes the tag that is used to identify the action easily. 
+     *
+     * @param tag Used to identify the action easily.
+     */
+    void setTag(int tag) { _tag = tag; }
+    /** Returns a flag field that is used to group the actions easily.
+     *
+     * @return A tag.
+     */
+    unsigned int getFlags() const { return _flags; }
+    /** Changes the flag field that is used to group the actions easily.
+     *
+     * @param flags Used to group the actions easily.
+     */
+    void setFlags(unsigned int flags) { _flags = flags; }
+
+CC_CONSTRUCTOR_ACCESS:
+    Action();
+    virtual ~Action();
+
+protected:
+    Node    *_originalTarget;
+    /** 
+     * The "target".
+     * The target will be set with the 'startWithTarget' method.
+     * When the 'stop' method is called, target will be set to nil.
+     * The target is 'assigned', it is not 'retained'.
+     */
+    Node    *_target;
+    /** The action tag. An identifier of the action. */
+    int     _tag;
+    /** The action flag field. To categorize action into certain groups.*/
+    unsigned int _flags;
+
+#if CC_ENABLE_SCRIPT_BINDING
+    ccScriptType _scriptType;         ///< type of script binding, lua or javascript
+#endif
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Action);
+};
+
+/** @class FiniteTimeAction
+ * @brief
+ * Base class actions that do have a finite time duration.
+ * Possible actions:
+ * - An action with a duration of 0 seconds.
+ * - An action with a duration of 35.5 seconds.
+ * Infinite time actions are valid.
+ */
+class CC_DLL FiniteTimeAction : public Action
+{
+public:
+    /** Get duration in seconds of the action. 
+     *
+     * @return The duration in seconds of the action.
+     */
+    float getDuration() const { return _duration; }
+    /** Set duration in seconds of the action. 
+     *
+     * @param duration In seconds of the action.
+     */
+    void setDuration(float duration) { _duration = duration; }
+
+    //
+    // Overrides
+    //
+    virtual FiniteTimeAction* reverse() const override
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+    virtual FiniteTimeAction* clone() const override
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+
+CC_CONSTRUCTOR_ACCESS:
+    FiniteTimeAction()
+    : _duration(0)
+    {}
+    virtual ~FiniteTimeAction(){}
+
+protected:
+    //! Duration in seconds.
+    float _duration;
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FiniteTimeAction);
+};
+
+class ActionInterval;
+class RepeatForever;
+
+/** @class Speed
+ * @brief Changes the speed of an action, making it take longer (speed>1)
+ * or shorter (speed<1) time.
+ * Useful to simulate 'slow motion' or 'fast forward' effect.
+ * @warning This action can't be Sequenceable because it is not an IntervalAction.
+ */
+class CC_DLL Speed : public Action
+{
+public:
+    /** Create the action and set the speed.
+     *
+     * @param action An action.
+     * @param speed The action speed.
+     */
+    static Speed* create(ActionInterval* action, float speed);
+    /** Return the speed.
+     *
+     * @return The action speed.
+     */
+    float getSpeed() const { return _speed; }
+    /** Alter the speed of the inner function in runtime. 
+     *
+     * @param speed Alter the speed of the inner function in runtime.
+     */
+    void setSpeed(float speed) { _speed = speed; }
+
+    /** Replace the interior action.
+     *
+     * @param action The new action, it will replace the running action.
+     */
+    void setInnerAction(ActionInterval *action);
+    /** Return the interior action.
+     *
+     * @return The interior action.
+     */
+    ActionInterval* getInnerAction() const { return _innerAction; }
+
+    //
+    // Override
+    //
+    virtual Speed* clone() const override;
+    virtual Speed* reverse() const override;
+    virtual void startWithTarget(Node* target) override;
+    virtual void stop() override;
+    /**
+     * @param dt in seconds.
+     */
+    virtual void step(float dt) override;
+    /** Return true if the action has finished.
+     *
+     * @return Is true if the action has finished.
+     */
+    virtual bool isDone() const  override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Speed();
+    virtual ~Speed();
+    /** Initializes the action. */
+    bool initWithAction(ActionInterval *action, float speed);
+
+protected:
+    float _speed;
+    ActionInterval *_innerAction;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Speed);
+};
+
+/** @class Follow
+ * @brief Follow is an action that "follows" a node.
+ * Eg:
+ * @code
+ * layer->runAction(Follow::create(hero));
+ * @endcode
+ * Instead of using Camera as a "follower", use this action instead.
+ * @since v0.99.2
+ */
+class CC_DLL Follow : public Action
+{
+public:
+    /**
+     * Creates the action with a set boundary or with no boundary.
+     *
+     * @param followedNode  The node to be followed.
+     * @param rect  The boundary. If \p rect is equal to Rect::ZERO, it'll work
+     *              with no boundary.
+    */
+    
+    static Follow* create(Node *followedNode, const Rect& rect = Rect::ZERO);
+    
+    /**
+     * Creates the action with a set boundary or with no boundary with offsets.
+     *
+     * @param followedNode  The node to be followed.
+     * @param rect  The boundary. If \p rect is equal to Rect::ZERO, it'll work
+     *              with no boundary.
+     * @param xOffset The horizontal offset from the center of the screen from which the
+     *               node  is to be followed.It can be positive,negative or zero.If
+     *               set to zero the node will be horizontally centered followed.
+     *  @param yOffset The vertical offset from the center of the screen from which the
+     *                 node is to be followed.It can be positive,negative or zero.
+     *                 If set to zero the node will be vertically centered followed.
+     *   If both xOffset and yOffset are set to zero,then the node will be horizontally and vertically centered followed.
+     */
+
+    static Follow* createWithOffset(Node* followedNode,float xOffset,float yOffset,const Rect& rect = Rect::ZERO);
+    
+    /** Return boundarySet.
+     *
+     * @return Return boundarySet.
+     */
+    bool isBoundarySet() const { return _boundarySet; }
+    /** Alter behavior - turn on/off boundary. 
+     *
+     * @param value Turn on/off boundary.
+     */
+    void setBoundarySet(bool value) { _boundarySet = value; }
+
+    //
+    // Override
+    //
+    virtual Follow* clone() const override;
+    virtual Follow* reverse() const override;
+    /**
+     * @param dt in seconds.
+     * @js NA
+     */
+    virtual void step(float dt) override;
+    virtual bool isDone() const override;
+    virtual void stop() override;
+
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    Follow()
+    : _followedNode(nullptr)
+    , _boundarySet(false)
+    , _boundaryFullyCovered(false)
+    , _leftBoundary(0.0)
+    , _rightBoundary(0.0)
+    , _topBoundary(0.0)
+    , _bottomBoundary(0.0)
+    , _offsetX(0.0)
+    , _offsetY(0.0)
+    , _worldRect(Rect::ZERO)
+    {}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~Follow();
+    
+    /**
+     * Initializes the action with a set boundary or with no boundary.
+     *
+     * @param followedNode  The node to be followed.
+     * @param rect  The boundary. If \p rect is equal to Rect::ZERO, it'll work
+     *              with no boundary.
+    */
+    bool initWithTarget(Node *followedNode, const Rect& rect = Rect::ZERO);
+    
+    
+    /**
+     * Initializes the action with a set boundary or with no boundary with offsets.
+     *
+     * @param followedNode  The node to be followed.
+     * @param rect  The boundary. If \p rect is equal to Rect::ZERO, it'll work
+     *              with no boundary.
+     * @param xOffset The horizontal offset from the center of the screen from which the
+     *                node  is to be followed.It can be positive,negative or zero.If
+     *                set to zero the node will be horizontally centered followed.
+     * @param yOffset The vertical offset from the center of the screen from which the
+     *                node is to be followed.It can be positive,negative or zero.
+     *                If set to zero the node will be vertically centered followed.
+     *   If both xOffset and yOffset are set to zero,then the node will be horizontally and vertically centered followed.
+
+     */
+    bool initWithTargetAndOffset(Node *followedNode,float xOffset,float yOffset,const Rect& rect = Rect::ZERO);
+
+protected:
+    /** Node to follow. */
+    Node *_followedNode;
+
+    /** Whether camera should be limited to certain area. */
+    bool _boundarySet;
+
+    /** If screen size is bigger than the boundary - update not needed. */
+    bool _boundaryFullyCovered;
+
+    /** Fast access to the screen dimensions. */
+    Vec2 _halfScreenSize;
+    Vec2 _fullScreenSize;
+
+    /** World boundaries. */
+    float _leftBoundary;
+    float _rightBoundary;
+    float _topBoundary;
+    float _bottomBoundary;
+    
+    /** Horizontal (x) and vertical (y) offset values. */
+    float _offsetX;
+    float _offsetY;
+    
+    Rect _worldRect;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Follow);
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif // __ACTIONS_CCACTION_H__

+ 247 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionCamera.cpp

@@ -0,0 +1,247 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCActionCamera.h"
+#include "2d/CCNode.h"
+#include "platform/CCStdC.h"
+
+NS_CC_BEGIN
+//
+// CameraAction
+//
+ActionCamera::ActionCamera()
+: _center(0, 0, 0)
+, _eye(0, 0, FLT_EPSILON)
+, _up(0, 1, 0)
+{
+}
+void ActionCamera::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+}
+
+ActionCamera* ActionCamera::clone() const
+{
+    auto action = new (std::nothrow) ActionCamera();
+    if (action)
+    {
+        action->autorelease();
+        return action;
+    }
+    
+    delete action;
+    return nullptr;
+}
+
+ActionCamera * ActionCamera::reverse() const
+{
+    // FIXME: This conversion isn't safe.
+    return (ActionCamera*)ReverseTime::create(const_cast<ActionCamera*>(this));
+}
+
+void ActionCamera::restore()
+{
+    _center.setZero();
+    _eye.set(0.0f, 0.0f, FLT_EPSILON);
+    _up.set(0.0f, 1.0f, 0.0f);
+}
+
+void ActionCamera::setEye(const Vec3& eye)
+{
+    _eye = eye;
+    updateTransform();
+}
+
+void ActionCamera::setEye(float x, float y, float z)
+{
+    _eye.set(x, y, z);
+    updateTransform();
+}
+
+void ActionCamera::setCenter(const Vec3& center)
+{
+    _center = center;
+    updateTransform();
+}
+
+void ActionCamera::setUp(const Vec3& up)
+{
+    _up = up;
+    updateTransform();
+}
+
+void ActionCamera::updateTransform()
+{
+    Mat4 lookupMatrix;
+    Mat4::createLookAt(_eye.x, _eye.y, _eye.z, _center.x, _center.y, _center.z, _up.x, _up.y, _up.z, &lookupMatrix);
+
+    Vec2 anchorPoint = _target->getAnchorPointInPoints();
+
+    bool needsTranslation = !anchorPoint.isZero();
+
+    Mat4 mv = Mat4::IDENTITY;
+
+    if(needsTranslation)
+    {
+        Mat4 t;
+        Mat4::createTranslation(anchorPoint.x, anchorPoint.y, 0, &t);
+        mv = mv * t;
+    }
+    
+    mv = mv * lookupMatrix;
+
+    if(needsTranslation)
+    {
+        Mat4 t;
+        Mat4::createTranslation(-anchorPoint.x, -anchorPoint.y, 0, &t);
+        mv = mv * t;
+    }
+
+    // FIXME: Using the AdditionalTransform is a complete hack.
+    // This should be done by multiplying the lookup-Matrix with the Node's MV matrix
+    // And then setting the result as the new MV matrix
+    // But that operation needs to be done after all the 'updates'.
+    // So the Director should emit an 'director_after_update' event.
+    // And this object should listen to it
+    _target->setAdditionalTransform(&mv);
+}
+
+//
+// OrbitCamera
+//
+
+OrbitCamera::OrbitCamera()
+: _radius(0.0)
+, _deltaRadius(0.0)
+, _angleZ(0.0)
+, _deltaAngleZ(0.0)
+, _angleX(0.0)
+, _deltaAngleX(0.0)
+, _radZ(0.0)
+, _radDeltaZ(0.0)
+, _radX(0.0)
+, _radDeltaX(0.0)
+{
+}
+OrbitCamera::~OrbitCamera()
+{
+}
+
+OrbitCamera * OrbitCamera::create(float t, float radius, float deltaRadius, float angleZ, float deltaAngleZ, float angleX, float deltaAngleX)
+{
+    OrbitCamera * obitCamera = new (std::nothrow) OrbitCamera();
+    if(obitCamera && obitCamera->initWithDuration(t, radius, deltaRadius, angleZ, deltaAngleZ, angleX, deltaAngleX))
+    {
+        obitCamera->autorelease();
+        return obitCamera;
+    }
+    
+    delete obitCamera;
+    return nullptr;
+}
+
+OrbitCamera* OrbitCamera::clone() const
+{
+    // no copy constructor
+    return OrbitCamera::create(_duration, _radius, _deltaRadius, _angleZ, _deltaAngleZ, _angleX, _deltaAngleX);
+}
+
+bool OrbitCamera::initWithDuration(float t, float radius, float deltaRadius, float angleZ, float deltaAngleZ, float angleX, float deltaAngleX)
+{
+    if ( ActionInterval::initWithDuration(t) )
+    {
+        _radius = radius;
+        _deltaRadius = deltaRadius;
+        _angleZ = angleZ;
+        _deltaAngleZ = deltaAngleZ;
+        _angleX = angleX;
+        _deltaAngleX = deltaAngleX;
+
+        _radDeltaZ = (float)CC_DEGREES_TO_RADIANS(deltaAngleZ);
+        _radDeltaX = (float)CC_DEGREES_TO_RADIANS(deltaAngleX);
+        return true;
+    }
+    return false;
+}
+
+void OrbitCamera::startWithTarget(Node *target)
+{
+    ActionCamera::startWithTarget(target);
+
+    float r, zenith, azimuth;
+    this->sphericalRadius(&r, &zenith, &azimuth);
+    if( std::isnan(_radius) )
+        _radius = r;
+    if( std::isnan(_angleZ) )
+        _angleZ = (float)CC_RADIANS_TO_DEGREES(zenith);
+    if( std::isnan(_angleX) )
+        _angleX = (float)CC_RADIANS_TO_DEGREES(azimuth);
+
+    _radZ = (float)CC_DEGREES_TO_RADIANS(_angleZ);
+    _radX = (float)CC_DEGREES_TO_RADIANS(_angleX);
+}
+
+void OrbitCamera::update(float dt)
+{
+    float r = (_radius + _deltaRadius * dt) * FLT_EPSILON;
+    float za = _radZ + _radDeltaZ * dt;
+    float xa = _radX + _radDeltaX * dt;
+
+    float i = sinf(za) * cosf(xa) * r + _center.x;
+    float j = sinf(za) * sinf(xa) * r + _center.y;
+    float k = cosf(za) * r + _center.z;
+
+    setEye(i,j,k);
+}
+
+void OrbitCamera::sphericalRadius(float *newRadius, float *zenith, float *azimuth)
+{
+    float r; // radius
+    float s;
+
+    float x = _eye.x - _center.x;
+    float y = _eye.y - _center.y;
+    float z = _eye.z - _center.z;
+
+    r = sqrtf( powf(x,2) + powf(y,2) + powf(z,2));
+    s = sqrtf( powf(x,2) + powf(y,2));
+    if( s == 0.0f )
+        s = FLT_EPSILON;
+    if(r==0.0f)
+        r = FLT_EPSILON;
+
+    *zenith = acosf( z/r);
+    if( x < 0 )
+        *azimuth= (float)M_PI - asinf(y/s);
+    else
+        *azimuth = asinf(y/s);
+
+    *newRadius = r / FLT_EPSILON;
+}
+
+NS_CC_END

+ 183 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionCamera.h

@@ -0,0 +1,183 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef __CCCAMERA_ACTION_H__
+#define __CCCAMERA_ACTION_H__
+
+#include "2d/CCActionInterval.h"
+#include "math/CCMath.h"
+
+NS_CC_BEGIN
+
+class Camera;
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/**
+ *@brief Base class for Camera actions.
+ *@ingroup Actions
+ */
+class CC_DLL ActionCamera : public ActionInterval
+{
+public:
+    /**
+     * @js ctor
+     * @lua new
+     */
+    ActionCamera();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ActionCamera(){};
+
+    // Overrides
+    virtual void startWithTarget(Node *target) override;
+    virtual ActionCamera * reverse() const override;
+    virtual ActionCamera *clone() const override;
+
+    /* Sets the Eye value of the Camera. 
+     * 
+     * @param eye The Eye value of the Camera.
+     * @js NA
+     */
+    void setEye(const Vec3 &eye);
+    void setEye(float x, float y, float z);
+    /* Returns the Eye value of the Camera. 
+     *
+     * @return The Eye value of the Camera.
+     * @js NA
+     */
+    const Vec3& getEye() const { return _eye; }
+    /* Sets the Center value of the Camera. 
+     *
+     * @param center The Center value of the Camera.
+     * @js NA
+     */
+    void setCenter(const Vec3 &center);
+    /* Returns the Center value of the Camera. 
+     *
+     * @return The Center value of the Camera.
+     * @js NA
+     */
+    const Vec3& getCenter() const { return _center; }
+    /* Sets the Up value of the Camera. 
+     *
+     * @param up The Up value of the Camera.
+     * @js NA
+     */
+    void setUp(const Vec3 &up);
+    /* Returns the Up value of the Camera. 
+     *
+     * @return The Up value of the Camera.
+     * @js NA
+     */
+    const Vec3& getUp() const { return _up; }
+
+protected:
+
+    void restore();
+    void updateTransform();
+
+    Vec3 _center;
+    Vec3 _eye;
+    Vec3 _up;
+};
+
+/** @class OrbitCamera
+ *
+ * @brief OrbitCamera action.
+ * Orbits the camera around the center of the screen using spherical coordinates.
+ * @ingroup Actions
+ */
+class CC_DLL OrbitCamera : public ActionCamera
+{
+public:
+    /** Creates a OrbitCamera action with radius, delta-radius,  z, deltaZ, x, deltaX. 
+     *
+     * @param t Duration in seconds.
+     * @param radius The start radius.
+     * @param deltaRadius The delta radius.
+     * @param angleZ The start angle in Z.
+     * @param deltaAngleZ The delta angle in Z.
+     * @param angleX The start angle in X.
+     * @param deltaAngleX The delta angle in X.
+     * @return An OrbitCamera.
+     */
+    static OrbitCamera* create(float t, float radius, float deltaRadius, float angleZ, float deltaAngleZ, float angleX, float deltaAngleX);
+    
+    /** Positions the camera according to spherical coordinates. 
+     *
+     * @param r The spherical radius.
+     * @param zenith The spherical zenith.
+     * @param azimuth The spherical azimuth.
+     */
+    void sphericalRadius(float *r, float *zenith, float *azimuth);
+
+    // Overrides
+    OrbitCamera *clone() const override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    OrbitCamera();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~OrbitCamera();
+    
+    /** Initializes a OrbitCamera action with radius, delta-radius,  z, deltaZ, x, deltaX. */
+    bool initWithDuration(float t, float radius, float deltaRadius, float angleZ, float deltaAngleZ, float angleX, float deltaAngleX);
+
+protected:
+    float _radius;
+    float _deltaRadius;
+    float _angleZ;
+    float _deltaAngleZ;
+    float _angleX;
+    float _deltaAngleX;
+
+    float _radZ;
+    float _radDeltaZ;
+    float _radX;
+    float _radDeltaX;
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif //__CCCAMERA_ACTION_H__

+ 529 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionCatmullRom.cpp

@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 2008 Radu Gruian
+ * Copyright (c) 2011 Vit Valentin
+ * Copyright (c) 2012 cocos2d-x.org
+ * Copyright (c) 2013-2016 Chukong Technologies Inc.
+ * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ *
+ * Permission is hereby granted, free 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.
+ *
+ *
+ * Original code by Radu Gruian: http://www.codeproject.com/Articles/30838/Overhauser-Catmull-Rom-Splines-for-Camera-Animatio.So
+ *
+ * Adapted to cocos2d-x by Vit Valentin
+ *
+ * Adapted from cocos2d-x to cocos2d-iphone by Ricardo Quesada
+ */
+#include "base/ccMacros.h"
+#include "2d/CCActionCatmullRom.h"
+#include "2d/CCNode.h"
+
+#include <iterator>
+
+using namespace std;
+
+NS_CC_BEGIN;
+
+/*
+ *  Implementation of PointArray
+ */
+
+PointArray* PointArray::create(ssize_t capacity)
+{
+    PointArray* pointArray = new (std::nothrow) PointArray();
+    if (pointArray && pointArray->initWithCapacity(capacity))
+    {
+        pointArray->autorelease();
+        return pointArray;
+    }
+
+    delete pointArray;
+    return nullptr;
+}
+
+bool PointArray::initWithCapacity(ssize_t capacity)
+{
+    _controlPoints.reserve(capacity);
+    
+    return true;
+}
+
+PointArray* PointArray::clone() const
+{
+    vector<Vec2> newArray = _controlPoints;
+    
+    PointArray *points = new (std::nothrow) PointArray();
+    points->initWithCapacity(10);
+    points->setControlPoints(std::move(newArray));
+
+    points->autorelease();
+    return points;
+}
+
+PointArray::~PointArray()
+{
+    CCLOGINFO("deallocing PointArray: %p", this);
+}
+
+PointArray::PointArray() {}
+
+const std::vector<Vec2>& PointArray::getControlPoints() const
+{
+    return _controlPoints;
+}
+
+void PointArray::setControlPoints(vector<Vec2> controlPoints)
+{
+    _controlPoints = std::move(controlPoints);
+}
+
+void PointArray::addControlPoint(const Vec2& controlPoint)
+{    
+    _controlPoints.push_back(controlPoint);
+}
+
+void PointArray::insertControlPoint(const Vec2& controlPoint, ssize_t index)
+{
+    _controlPoints.insert(std::next(_controlPoints.begin(), index), controlPoint);
+}
+
+const Vec2& PointArray::getControlPointAtIndex(ssize_t index) const
+{
+    index = MIN(static_cast<ssize_t>(_controlPoints.size())-1, MAX(index, 0));
+    return _controlPoints.at(index);
+}
+
+void PointArray::replaceControlPoint(const Vec2& controlPoint, ssize_t index)
+{
+    _controlPoints.at(index) = controlPoint;
+}
+
+void PointArray::removeControlPointAtIndex(ssize_t index)
+{
+    vector<Vec2>::iterator iter = std::next(_controlPoints.begin(), index);
+    _controlPoints.erase(iter);
+}
+
+ssize_t PointArray::count() const
+{
+    return _controlPoints.size();
+}
+
+PointArray* PointArray::reverse() const
+{
+    vector<Vec2> newArray;
+    newArray.reserve(_controlPoints.size());
+    for (auto iter = _controlPoints.rbegin(), iterRend = _controlPoints.rend(); iter != iterRend; ++iter)
+    {
+        newArray.push_back(*iter);
+    }
+    PointArray *config = PointArray::create(0);
+    config->setControlPoints(std::move(newArray));
+    
+    return config;
+}
+
+void PointArray::reverseInline()
+{
+    const size_t l = _controlPoints.size();
+    Vec2 *p1 = nullptr;
+    Vec2 *p2 = nullptr;
+    float x, y;
+    for (size_t i = 0; i < l/2; ++i)
+    {
+        p1 = &_controlPoints.at(i);
+        p2 = &_controlPoints.at(l-i-1);
+        
+        x = p1->x;
+        y = p1->y;
+        
+        p1->x = p2->x;
+        p1->y = p2->y;
+        
+        p2->x = x;
+        p2->y = y;
+    }
+}
+
+// CatmullRom Spline formula:
+Vec2 ccCardinalSplineAt(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, float tension, float t)
+{
+    float t2 = t * t;
+    float t3 = t2 * t;
+    
+    /*
+     * Formula: s(-ttt + 2tt - t)P1 + s(-ttt + tt)P2 + (2ttt - 3tt + 1)P2 + s(ttt - 2tt + t)P3 + (-2ttt + 3tt)P3 + s(ttt - tt)P4
+     */
+    float s = (1 - tension) / 2;
+
+    float b1 = s * ((-t3 + (2 * t2)) - t);                      // s(-t3 + 2 t2 - t)P1
+    float b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1);          // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2
+    float b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2);      // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3
+    float b4 = s * (t3 - t2);                                   // s(t3 - t2)P4
+    
+    float x = (p0.x*b1 + p1.x*b2 + p2.x*b3 + p3.x*b4);
+    float y = (p0.y*b1 + p1.y*b2 + p2.y*b3 + p3.y*b4);
+
+    return Vec2(x,y);
+}
+
+/* Implementation of CardinalSplineTo
+ */
+
+CardinalSplineTo* CardinalSplineTo::create(float duration, PointArray *points, float tension)
+{
+    CardinalSplineTo *ret = new (std::nothrow) CardinalSplineTo();
+    if (ret)
+    {
+        if (ret->initWithDuration(duration, points, tension))
+        {
+            ret->autorelease();
+        }
+        else 
+        {
+            CC_SAFE_RELEASE_NULL(ret);
+        }
+    }
+
+    return ret;
+}
+
+bool CardinalSplineTo::initWithDuration(float duration, PointArray *points, float tension)
+{
+    CCASSERT(points->count() > 0, "Invalid configuration. It must at least have one control point");
+
+    if (ActionInterval::initWithDuration(duration))
+    {
+        this->setPoints(points);
+        this->_tension = tension;
+        
+        return true;
+    }
+    
+    return false;
+}
+
+CardinalSplineTo::~CardinalSplineTo()
+{
+    CC_SAFE_RELEASE_NULL(_points);
+}
+
+CardinalSplineTo::CardinalSplineTo()
+: _points(nullptr)
+, _deltaT(0.f)
+, _tension(0.f)
+{
+}
+
+void CardinalSplineTo::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+
+//    _deltaT = (float) 1 / _points->count();
+    
+    // Issue #1441
+    _deltaT = (float) 1 / (_points->count() - 1);
+
+    _previousPosition = target->getPosition();
+    _accumulatedDiff.setZero();
+}
+
+CardinalSplineTo* CardinalSplineTo::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) CardinalSplineTo();
+    a->initWithDuration(this->_duration, this->_points->clone(), this->_tension);
+    a->autorelease();
+    return a;
+}
+
+void CardinalSplineTo::update(float time)
+{
+    ssize_t p;
+    float lt;
+
+    // eg.
+    // p..p..p..p..p..p..p
+    // 1..2..3..4..5..6..7
+    // want p to be 1, 2, 3, 4, 5, 6
+    if (time == 1)
+    {
+        p = _points->count() - 1;
+        lt = 1;
+    }
+    else 
+    {
+        p = (ssize_t)(time / _deltaT);
+        lt = (time - _deltaT * p) / _deltaT;
+    }
+    
+    // Interpolate
+    Vec2 pp0 = _points->getControlPointAtIndex(p-1);
+    Vec2 pp1 = _points->getControlPointAtIndex(p+0);
+    Vec2 pp2 = _points->getControlPointAtIndex(p+1);
+    Vec2 pp3 = _points->getControlPointAtIndex(p+2);
+
+    Vec2 newPos = ccCardinalSplineAt(pp0, pp1, pp2, pp3, _tension, lt);
+
+#if CC_ENABLE_STACKABLE_ACTIONS
+    // Support for stacked actions
+    Node *node = _target;
+    Vec2 diff = node->getPosition() - _previousPosition;
+    if( diff.x !=0 || diff.y != 0 )
+    {
+        _accumulatedDiff = _accumulatedDiff + diff;
+        newPos = newPos + _accumulatedDiff;
+    }
+#endif
+    
+    this->updatePosition(newPos);
+}
+
+void CardinalSplineTo::updatePosition(const Vec2 &newPos)
+{
+    _target->setPosition(newPos);
+    _previousPosition = newPos;
+}
+
+CardinalSplineTo* CardinalSplineTo::reverse() const
+{
+    PointArray *pReverse = _points->reverse();
+    
+    return CardinalSplineTo::create(_duration, pReverse, _tension);
+}
+
+/* CardinalSplineBy
+ */
+
+CardinalSplineBy* CardinalSplineBy::create(float duration, PointArray *points, float tension)
+{
+    CardinalSplineBy *ret = new (std::nothrow) CardinalSplineBy();
+    if (ret)
+    {
+        if (ret->initWithDuration(duration, points, tension))
+        {
+            ret->autorelease();
+        }
+        else 
+        {
+            CC_SAFE_RELEASE_NULL(ret);
+        }
+    }
+
+    return ret;
+}
+
+CardinalSplineBy::CardinalSplineBy() : _startPosition(0,0)
+{
+}
+
+void CardinalSplineBy::updatePosition(const Vec2 &newPos)
+{
+    Vec2 p = newPos + _startPosition;
+    _target->setPosition(p);
+    _previousPosition = p;
+}
+
+CardinalSplineBy* CardinalSplineBy::reverse() const
+{
+    PointArray *copyConfig = _points->clone();
+
+    //
+    // convert "absolutes" to "diffs"
+    //
+    Vec2 p = copyConfig->getControlPointAtIndex(0);
+    for (ssize_t i = 1; i < copyConfig->count(); ++i)
+    {
+        Vec2 current = copyConfig->getControlPointAtIndex(i);
+        Vec2 diff = current - p;
+        copyConfig->replaceControlPoint(diff, i);
+        
+        p = current;
+    }
+
+    // convert to "diffs" to "reverse absolute"
+
+    PointArray *pReverse = copyConfig->reverse();
+
+    // 1st element (which should be 0,0) should be here too
+    
+    p = pReverse->getControlPointAtIndex(pReverse->count()-1);
+    pReverse->removeControlPointAtIndex(pReverse->count()-1);
+    
+    p = -p;
+    pReverse->insertControlPoint(p, 0);
+    
+    for (ssize_t i = 1; i < pReverse->count(); ++i)
+    {
+        Vec2 current = pReverse->getControlPointAtIndex(i);
+        current = -current;
+        Vec2 abs = current + p;
+        pReverse->replaceControlPoint(abs, i);
+        
+        p = abs;
+    }
+
+    return CardinalSplineBy::create(_duration, pReverse, _tension);
+}
+
+void CardinalSplineBy::startWithTarget(Node *target)
+{    
+    CardinalSplineTo::startWithTarget(target);
+    _startPosition = target->getPosition();
+}
+
+CardinalSplineBy* CardinalSplineBy::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) CardinalSplineBy();
+    a->initWithDuration(this->_duration, this->_points->clone(), this->_tension);
+    a->autorelease();
+    return a;
+}
+
+/* CatmullRomTo
+ */
+
+CatmullRomTo* CatmullRomTo::create(float dt, PointArray *points)
+{
+    CatmullRomTo *ret = new (std::nothrow) CatmullRomTo();
+    if (ret)
+    {
+        if (ret->initWithDuration(dt, points))
+        {
+            ret->autorelease();
+        }
+        else 
+        {
+            CC_SAFE_RELEASE_NULL(ret);
+        }
+    }
+
+    return ret;
+}
+
+bool CatmullRomTo::initWithDuration(float dt, PointArray *points)
+{
+    if (CardinalSplineTo::initWithDuration(dt, points, 0.5f))
+    {
+        return true;
+    }
+    
+    return false;
+}
+
+CatmullRomTo* CatmullRomTo::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) CatmullRomTo();
+    a->initWithDuration(this->_duration, this->_points->clone());
+    a->autorelease();
+    return a;
+}
+
+CatmullRomTo* CatmullRomTo::reverse() const
+{
+    PointArray *reverse = _points->reverse();
+    return CatmullRomTo::create(_duration, reverse);
+}
+
+
+/* CatmullRomBy
+ */
+
+CatmullRomBy* CatmullRomBy::create(float dt, PointArray *points)
+{
+    CatmullRomBy *ret = new (std::nothrow) CatmullRomBy();
+    if (ret)
+    {
+        if (ret->initWithDuration(dt, points))
+        {
+            ret->autorelease();
+        }
+        else 
+        {
+            CC_SAFE_RELEASE_NULL(ret);
+        }
+    }
+
+    return ret;
+}
+
+bool CatmullRomBy::initWithDuration(float dt, PointArray *points)
+{
+    if (CardinalSplineTo::initWithDuration(dt, points, 0.5f))
+    {
+        return true;
+    }
+    
+    return false;
+}
+
+CatmullRomBy* CatmullRomBy::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) CatmullRomBy();
+    a->initWithDuration(this->_duration, this->_points->clone());
+    a->autorelease();
+    return a;
+}
+
+CatmullRomBy* CatmullRomBy::reverse() const
+{
+    PointArray *copyConfig = _points->clone();
+
+    //
+    // convert "absolutes" to "diffs"
+    //
+    Vec2 p = copyConfig->getControlPointAtIndex(0);
+    for (ssize_t i = 1; i < copyConfig->count(); ++i)
+    {
+        Vec2 current = copyConfig->getControlPointAtIndex(i);
+        Vec2 diff = current - p;
+        copyConfig->replaceControlPoint(diff, i);
+
+        p = current;
+    }
+
+    // convert to "diffs" to "reverse absolute"
+
+    PointArray *reverse = copyConfig->reverse();
+
+    // 1st element (which should be 0,0) should be here too
+
+    p = reverse->getControlPointAtIndex(reverse->count()-1);
+    reverse->removeControlPointAtIndex(reverse->count()-1);
+
+    p = -p;
+    reverse->insertControlPoint(p, 0);
+
+    for (ssize_t i = 1; i < reverse->count(); ++i)
+    {
+        Vec2 current = reverse->getControlPointAtIndex(i);
+        current = -current;
+        Vec2 abs = current + p;
+        reverse->replaceControlPoint(abs, i);
+
+        p = abs;
+    }
+
+    return CatmullRomBy::create(_duration, reverse);
+}
+
+NS_CC_END;

+ 348 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionCatmullRom.h

@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2008 Radu Gruian
+ * Copyright (c) 2011 Vit Valentin
+ * Copyright (c) 2012 cocos2d-x.org
+ * Copyright (c) 2013-2016 Chukong Technologies Inc.
+ * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ *
+ *
+ * Permission is hereby granted, free 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.
+ *
+ *
+ * Original code by Radu Gruian: http://www.codeproject.com/Articles/30838/Overhauser-Catmull-Rom-Splines-for-Camera-Animatio.So
+ *
+ * Adapted to cocos2d-x by Vit Valentin
+ *
+ * Adapted from cocos2d-x to cocos2d-iphone by Ricardo Quesada
+ */
+
+
+#ifndef __CCACTION_CATMULLROM_H__
+#define __CCACTION_CATMULLROM_H__
+
+#include <vector>
+
+#include "2d/CCActionInterval.h"
+#include "math/CCGeometry.h"
+
+NS_CC_BEGIN;
+
+class Node;
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/** An Array that contain control points.
+ * Used by CardinalSplineTo and (By) and CatmullRomTo (and By) actions.
+ * @ingroup Actions
+ * @js NA
+ */
+class CC_DLL PointArray : public Ref, public Clonable
+{
+public:
+
+    /** Creates and initializes a Points array with capacity.
+     * @js NA
+     * @param capacity The size of the array.
+     */
+    static PointArray* create(ssize_t capacity);
+
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~PointArray();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    PointArray();
+
+    /** Initializes a Catmull Rom config with a capacity hint.
+     *
+     * @js NA
+     * @param capacity The size of the array.
+     * @return True.
+     */
+    bool initWithCapacity(ssize_t capacity);
+
+    /** Appends a control point.
+     *
+     * @js NA
+     * @param controlPoint A control point.
+     */
+    void addControlPoint(const Vec2& controlPoint);
+
+    /** Inserts a controlPoint at index.
+     *
+     * @js NA
+     * @param controlPoint A control point.
+     * @param index Insert the point to array in index.
+     */
+    void insertControlPoint(const Vec2& controlPoint, ssize_t index);
+
+    /** Replaces an existing controlPoint at index.
+     *
+     * @js NA
+     * @param controlPoint A control point.
+     * @param index Replace the point to array in index.
+     */
+    void replaceControlPoint(const Vec2& controlPoint, ssize_t index);
+
+    /** Get the value of a controlPoint at a given index.
+     *
+     * @js NA
+     * @param index Get the point in index.
+     * @return A Vec2.
+     */
+    const Vec2& getControlPointAtIndex(ssize_t index) const;
+
+    /** Deletes a control point at a given index
+     *
+     * @js NA
+     * @param index Remove the point in index.
+     */
+    void removeControlPointAtIndex(ssize_t index);
+
+    /** Returns the number of objects of the control point array.
+     *
+     * @js NA
+     * @return The number of objects of the control point array.
+     */
+    ssize_t count() const;
+
+    /** Returns a new copy of the array reversed. User is responsible for releasing this copy.
+     *
+     * @js NA
+     * @return A new copy of the array reversed.
+     */
+    PointArray* reverse() const;
+
+    /** Reverse the current control point array inline, without generating a new one.
+     * @js NA
+     */
+    void reverseInline();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual PointArray* clone() const;
+    /**
+     * @js NA
+     */
+    const std::vector<Vec2>& getControlPoints() const;
+    /**
+     * @js NA
+     */
+    void setControlPoints(std::vector<Vec2> controlPoints);
+private:
+    /** Array that contains the control points. */
+    std::vector<Vec2> _controlPoints;
+};
+
+/** @class CardinalSplineTo
+ * Cardinal Spline path.
+ * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
+ * @ingroup Actions
+ */
+class CC_DLL CardinalSplineTo : public ActionInterval
+{
+public:
+
+    /** Creates an action with a Cardinal Spline array of points and tension.
+     * @param duration In seconds.
+     * @param points An PointArray.
+     * @param tension Goodness of fit.
+     * @code
+     * When this function bound to js or lua,the input params are changed.
+     * In js: var create(var t,var table)
+     * In lua: local create(local t, local table)
+     * @endcode
+     */
+    static CardinalSplineTo* create(float duration, PointArray* points, float tension);
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~CardinalSplineTo();
+    /**
+     * @js ctor
+     * @lua NA
+     */
+    CardinalSplineTo();
+
+    /** 
+     * Initializes the action with a duration and an array of points.
+     *
+     * @param duration In seconds.
+     * @param points An PointArray.
+     * @param tension Goodness of fit.
+     */
+    bool initWithDuration(float duration, PointArray* points, float tension);
+    /** It will update the target position and change the _previousPosition to newPos
+     *
+     * @param newPos The new position.
+     */
+    virtual void updatePosition(const Vec2 &newPos);
+    /** Return a PointArray.
+     *
+     * @return A PointArray.
+     */
+    PointArray* getPoints() { return _points; }
+    /**
+     * @js NA
+     * @lua NA
+     */
+    void setPoints(PointArray* points)
+    {
+        CC_SAFE_RETAIN(points);
+        CC_SAFE_RELEASE(_points);
+        _points = points;
+    }
+
+    // Overrides
+    virtual CardinalSplineTo *clone() const override;
+    virtual CardinalSplineTo* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+
+protected:
+    /** Array of control points */
+    PointArray *_points;
+    float _deltaT;
+    float _tension;
+    Vec2 _previousPosition;
+    Vec2 _accumulatedDiff;
+};
+
+/** @class CardinalSplineBy
+ * Cardinal Spline path.
+ * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
+ * @ingroup Actions
+ */
+class CC_DLL CardinalSplineBy : public CardinalSplineTo
+{
+public:
+
+    /** Creates an action with a Cardinal Spline array of points and tension.
+     * @code
+     * When this function bound to js or lua,the input params are changed.
+     * In js: var create(var t,var table).
+     * In lua: local create(local t, local table).
+     * @param duration In seconds.
+     * @param point An PointArray.
+     * @param tension Goodness of fit.
+     * @endcode
+     */
+    static CardinalSplineBy* create(float duration, PointArray* points, float tension);
+
+    CardinalSplineBy();
+
+    // Overrides
+    virtual void startWithTarget(Node *target) override;
+    virtual void updatePosition(const Vec2 &newPos) override;
+    virtual CardinalSplineBy *clone() const override;
+    virtual CardinalSplineBy* reverse() const override;
+
+protected:
+    Vec2 _startPosition;
+};
+
+/** @class CatmullRomTo
+ * An action that moves the target with a CatmullRom curve to a destination point.
+ * A Catmull Rom is a Cardinal Spline with a tension of 0.5.
+ * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
+ * @ingroup Actions
+ */
+class CC_DLL CatmullRomTo : public CardinalSplineTo
+{
+public:
+
+    /** Creates an action with a Cardinal Spline array of points and tension.
+     * @param dt In seconds.
+     * @param points An PointArray.
+     * @code
+     * When this function bound to js or lua,the input params are changed.
+     * In js: var create(var dt,var table).
+     * In lua: local create(local dt, local table).
+     * @endcode
+     */
+    static CatmullRomTo* create(float dt, PointArray* points);
+
+    /** 
+     * Initializes the action with a duration and an array of points.
+     *
+     * @param dt In seconds.
+     * @param points An PointArray.
+     */
+    bool initWithDuration(float dt, PointArray* points);
+
+    // Override
+    virtual CatmullRomTo *clone() const override;
+    virtual CatmullRomTo *reverse() const override;
+};
+
+/** @class CatmullRomBy
+ * An action that moves the target with a CatmullRom curve by a certain distance.
+ * A Catmull Rom is a Cardinal Spline with a tension of 0.5.
+ * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
+ * @ingroup Actions
+ */
+class CC_DLL CatmullRomBy : public CardinalSplineBy
+{
+public:
+    /** Creates an action with a Cardinal Spline array of points and tension.
+     * @param dt In seconds.
+     * @param points An PointArray.
+     * @code
+     * When this function bound to js or lua,the input params are changed.
+     * In js: var create(var dt,var table).
+     * In lua: local create(local dt, local table).
+     * @endcode
+     */
+    static CatmullRomBy* create(float dt, PointArray* points);
+
+    /** Initializes the action with a duration and an array of points.
+     *
+     * @param dt In seconds.
+     * @param points An PointArray.
+     */
+    bool initWithDuration(float dt, PointArray* points);
+
+    // Override
+    virtual CatmullRomBy *clone() const override;
+    virtual CatmullRomBy *reverse() const override;
+
+};
+
+/** Returns the Cardinal Spline position for a given set of control points, tension and time */
+extern CC_DLL Vec2 ccCardinalSplineAt(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, float tension, float t);
+
+// end of actions group
+/// @}
+
+NS_CC_END;
+
+#endif // __CCACTION_CATMULLROM_H__

+ 325 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionEase.cpp

@@ -0,0 +1,325 @@
+/****************************************************************************
+Copyright (c) 2008-2009 Jason Booth
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+ http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+/*
+ * Elastic, Back and Bounce actions based on code from:
+ * http://github.com/NikhilK/silverlightfx/
+ *
+ * by http://github.com/NikhilK
+ */
+
+#include "2d/CCActionEase.h"
+#include "2d/CCTweenFunction.h"
+
+NS_CC_BEGIN
+
+#ifndef M_PI_X_2
+#define M_PI_X_2 (float)M_PI * 2.0f
+#endif
+
+//
+// EaseAction
+//
+
+bool ActionEase::initWithAction(ActionInterval *action)
+{
+    CCASSERT(action != nullptr, "action couldn't be nullptr!");
+    if (action == nullptr)
+    {
+        return false;
+    }
+
+    if (ActionInterval::initWithDuration(action->getDuration()))
+    {
+        _inner = action;
+        action->retain();
+
+        return true;
+    }
+
+    return false;
+}
+
+ActionEase::~ActionEase()
+{
+    CC_SAFE_RELEASE(_inner);
+}
+
+void ActionEase::startWithTarget(Node *target)
+{
+    if (target && _inner)
+    {
+        ActionInterval::startWithTarget(target);
+        _inner->startWithTarget(_target);
+    }
+    else
+    {
+        log("ActionEase::startWithTarget error: target or _inner is nullptr!");
+    }
+}
+
+void ActionEase::stop()
+{
+    if (_inner)
+        _inner->stop();
+    
+    ActionInterval::stop();
+}
+
+void ActionEase::update(float time)
+{
+    _inner->update(time);
+}
+
+ActionInterval* ActionEase::getInnerAction()
+{
+    return _inner;
+}
+
+//
+// EaseRateAction
+//
+
+EaseRateAction* EaseRateAction::create(ActionInterval* action, float rate)
+{
+    CCASSERT(action != nullptr, "action cannot be nullptr!");
+
+    EaseRateAction *easeRateAction = new (std::nothrow) EaseRateAction();
+    if (easeRateAction && easeRateAction->initWithAction(action, rate))
+    {
+        easeRateAction->autorelease();
+        return easeRateAction;
+    }
+
+    CC_SAFE_DELETE(easeRateAction);
+    return nullptr;
+}
+
+bool EaseRateAction::initWithAction(ActionInterval *action, float rate)
+{
+    if (ActionEase::initWithAction(action))
+    {
+        _rate = rate;
+        return true;
+    }
+
+    return false;
+}
+
+//
+// NOTE: Converting these macros into Templates is desirable, but please see
+// issue #16159 [https://github.com/cocos2d/cocos2d-x/pull/16159] for further info
+//
+#define EASE_TEMPLATE_IMPL(CLASSNAME, TWEEN_FUNC, REVERSE_CLASSNAME) \
+CLASSNAME* CLASSNAME::create(cocos2d::ActionInterval *action) \
+{ \
+    CLASSNAME *ease = new (std::nothrow) CLASSNAME(); \
+    if (ease) \
+    { \
+        if (ease->initWithAction(action)) \
+            ease->autorelease(); \
+        else \
+            CC_SAFE_RELEASE_NULL(ease); \
+    } \
+    return ease; \
+} \
+CLASSNAME* CLASSNAME::clone() const \
+{ \
+    if(_inner) return CLASSNAME::create(_inner->clone()); \
+    return nullptr; \
+} \
+void CLASSNAME::update(float time) { \
+    _inner->update(TWEEN_FUNC(time)); \
+} \
+ActionEase* CLASSNAME::reverse() const { \
+    return REVERSE_CLASSNAME::create(_inner->reverse()); \
+}
+
+EASE_TEMPLATE_IMPL(EaseExponentialIn, tweenfunc::expoEaseIn, EaseExponentialOut);
+EASE_TEMPLATE_IMPL(EaseExponentialOut, tweenfunc::expoEaseOut, EaseExponentialIn);
+EASE_TEMPLATE_IMPL(EaseExponentialInOut, tweenfunc::expoEaseInOut, EaseExponentialInOut);
+EASE_TEMPLATE_IMPL(EaseSineIn, tweenfunc::sineEaseIn, EaseSineOut);
+EASE_TEMPLATE_IMPL(EaseSineOut, tweenfunc::sineEaseOut, EaseSineIn);
+EASE_TEMPLATE_IMPL(EaseSineInOut, tweenfunc::sineEaseInOut, EaseSineInOut);
+EASE_TEMPLATE_IMPL(EaseBounceIn, tweenfunc::bounceEaseIn, EaseBounceOut);
+EASE_TEMPLATE_IMPL(EaseBounceOut, tweenfunc::bounceEaseOut, EaseBounceIn);
+EASE_TEMPLATE_IMPL(EaseBounceInOut, tweenfunc::bounceEaseInOut, EaseBounceInOut);
+EASE_TEMPLATE_IMPL(EaseBackIn, tweenfunc::backEaseIn, EaseBackOut);
+EASE_TEMPLATE_IMPL(EaseBackOut, tweenfunc::backEaseOut, EaseBackIn);
+EASE_TEMPLATE_IMPL(EaseBackInOut, tweenfunc::backEaseInOut, EaseBackInOut);
+EASE_TEMPLATE_IMPL(EaseQuadraticActionIn, tweenfunc::quadraticIn, EaseQuadraticActionIn);
+EASE_TEMPLATE_IMPL(EaseQuadraticActionOut, tweenfunc::quadraticOut, EaseQuadraticActionOut);
+EASE_TEMPLATE_IMPL(EaseQuadraticActionInOut, tweenfunc::quadraticInOut, EaseQuadraticActionInOut);
+EASE_TEMPLATE_IMPL(EaseQuarticActionIn, tweenfunc::quartEaseIn, EaseQuarticActionIn);
+EASE_TEMPLATE_IMPL(EaseQuarticActionOut, tweenfunc::quartEaseOut, EaseQuarticActionOut);
+EASE_TEMPLATE_IMPL(EaseQuarticActionInOut, tweenfunc::quartEaseInOut, EaseQuarticActionInOut);
+EASE_TEMPLATE_IMPL(EaseQuinticActionIn, tweenfunc::quintEaseIn, EaseQuinticActionIn);
+EASE_TEMPLATE_IMPL(EaseQuinticActionOut, tweenfunc::quintEaseOut, EaseQuinticActionOut);
+EASE_TEMPLATE_IMPL(EaseQuinticActionInOut, tweenfunc::quintEaseInOut, EaseQuinticActionInOut);
+EASE_TEMPLATE_IMPL(EaseCircleActionIn, tweenfunc::circEaseIn, EaseCircleActionIn);
+EASE_TEMPLATE_IMPL(EaseCircleActionOut, tweenfunc::circEaseOut, EaseCircleActionOut);
+EASE_TEMPLATE_IMPL(EaseCircleActionInOut, tweenfunc::circEaseInOut, EaseCircleActionInOut);
+EASE_TEMPLATE_IMPL(EaseCubicActionIn, tweenfunc::cubicEaseIn, EaseCubicActionIn);
+EASE_TEMPLATE_IMPL(EaseCubicActionOut, tweenfunc::cubicEaseOut, EaseCubicActionOut);
+EASE_TEMPLATE_IMPL(EaseCubicActionInOut, tweenfunc::cubicEaseInOut, EaseCubicActionInOut);
+
+//
+// NOTE: Converting these macros into Templates is desirable, but please see
+// issue #16159 [https://github.com/cocos2d/cocos2d-x/pull/16159] for further info
+//
+#define EASERATE_TEMPLATE_IMPL(CLASSNAME, TWEEN_FUNC) \
+CLASSNAME* CLASSNAME::create(cocos2d::ActionInterval *action, float rate) \
+{ \
+    CLASSNAME *ease = new (std::nothrow) CLASSNAME(); \
+    if (ease) \
+    { \
+        if (ease->initWithAction(action, rate)) \
+            ease->autorelease(); \
+        else \
+            CC_SAFE_RELEASE_NULL(ease); \
+    } \
+    return ease; \
+} \
+CLASSNAME* CLASSNAME::clone() const \
+{ \
+    if(_inner) return CLASSNAME::create(_inner->clone(), _rate); \
+    return nullptr; \
+} \
+void CLASSNAME::update(float time) { \
+    _inner->update(TWEEN_FUNC(time, _rate)); \
+} \
+EaseRateAction* CLASSNAME::reverse() const { \
+    return CLASSNAME::create(_inner->reverse(), 1.f / _rate); \
+}
+
+// NOTE: the original code used the same class for the `reverse()` method
+EASERATE_TEMPLATE_IMPL(EaseIn, tweenfunc::easeIn);
+EASERATE_TEMPLATE_IMPL(EaseOut, tweenfunc::easeOut);
+EASERATE_TEMPLATE_IMPL(EaseInOut, tweenfunc::easeInOut);
+
+//
+// EaseElastic
+//
+
+bool EaseElastic::initWithAction(ActionInterval *action, float period /* = 0.3f*/)
+{
+    if (ActionEase::initWithAction(action))
+    {
+        _period = period;
+        return true;
+    }
+
+    return false;
+}
+
+//
+// NOTE: Converting these macros into Templates is desirable, but please see
+// issue #16159 [https://github.com/cocos2d/cocos2d-x/pull/16159] for further info
+//
+#define EASEELASTIC_TEMPLATE_IMPL(CLASSNAME, TWEEN_FUNC, REVERSE_CLASSNAME) \
+CLASSNAME* CLASSNAME::create(cocos2d::ActionInterval *action, float period /* = 0.3f*/) \
+{ \
+    CLASSNAME *ease = new (std::nothrow) CLASSNAME(); \
+    if (ease) \
+    { \
+        if (ease->initWithAction(action, period)) \
+            ease->autorelease(); \
+        else \
+            CC_SAFE_RELEASE_NULL(ease); \
+    } \
+    return ease; \
+} \
+CLASSNAME* CLASSNAME::clone() const \
+{ \
+    if(_inner) return CLASSNAME::create(_inner->clone(), _period); \
+    return nullptr; \
+} \
+void CLASSNAME::update(float time) { \
+    _inner->update(TWEEN_FUNC(time, _period)); \
+} \
+EaseElastic* CLASSNAME::reverse() const { \
+    return REVERSE_CLASSNAME::create(_inner->reverse(), _period); \
+}
+
+EASEELASTIC_TEMPLATE_IMPL(EaseElasticIn, tweenfunc::elasticEaseIn, EaseElasticOut);
+EASEELASTIC_TEMPLATE_IMPL(EaseElasticOut, tweenfunc::elasticEaseOut, EaseElasticIn);
+EASEELASTIC_TEMPLATE_IMPL(EaseElasticInOut, tweenfunc::elasticEaseInOut, EaseElasticInOut);
+
+//
+// EaseBezierAction
+//
+
+EaseBezierAction* EaseBezierAction::create(cocos2d::ActionInterval* action)
+{
+    EaseBezierAction *ret = new (std::nothrow) EaseBezierAction();
+    if (ret && ret->initWithAction(action))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    delete ret;
+    return nullptr;
+}
+
+void EaseBezierAction::setBezierParamer( float p0, float p1, float p2, float p3)
+{
+    _p0 = p0;
+    _p1 = p1;
+    _p2 = p2;
+    _p3 = p3;
+}
+
+EaseBezierAction* EaseBezierAction::clone() const
+{
+    // no copy constructor
+    if (_inner)
+    {
+        auto ret = EaseBezierAction::create(_inner->clone());
+        if (ret)
+        {
+            ret->setBezierParamer(_p0,_p1,_p2,_p3);
+        }
+        return ret;
+    }
+
+    return nullptr;
+}
+
+void EaseBezierAction::update(float time)
+{
+    _inner->update(tweenfunc::bezieratFunction(_p0,_p1,_p2,_p3,time));
+}
+
+EaseBezierAction* EaseBezierAction::reverse() const
+{
+    EaseBezierAction* reverseAction = EaseBezierAction::create(_inner->reverse());
+    reverseAction->setBezierParamer(_p3,_p2,_p1,_p0);
+    return reverseAction;
+}
+
+NS_CC_END

+ 558 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionEase.h

@@ -0,0 +1,558 @@
+/****************************************************************************
+Copyright (c) 2008-2009 Jason Booth
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef __ACTION_CCEASE_ACTION_H__
+#define __ACTION_CCEASE_ACTION_H__
+
+#include "2d/CCActionInterval.h"
+#include "2d/CCTweenFunction.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/**
+ @class ActionEase
+ @brief Base class for Easing actions.
+ @details Ease actions are created from other interval actions.
+ The ease action will change the timeline of the inner action.
+ @ingroup Actions
+ */
+class CC_DLL ActionEase : public ActionInterval
+{
+public:
+    /**
+     @brief Get the pointer of the inner action.
+     @return The pointer of the inner action.
+    */
+    virtual ActionInterval* getInnerAction();
+
+    //
+    // Overrides
+    //
+    virtual void startWithTarget(Node *target) override;
+    virtual void stop() override;
+    virtual void update(float time) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    ActionEase()
+    : _inner(nullptr)
+    {}
+    
+    virtual ~ActionEase();
+    /**
+     @brief Initializes the action.
+     @return Return true when the initialization success, otherwise return false.
+    */
+    bool initWithAction(ActionInterval *action);
+
+protected:
+    /** The inner action */
+    ActionInterval *_inner;
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ActionEase);
+};
+
+/**
+ @class EaseRateAction
+ @brief Base class for Easing actions with rate parameters
+ @details Ease the inner action with specified rate.
+ @ingroup Actions
+ */
+class CC_DLL EaseRateAction : public ActionEase
+{
+public:
+    static EaseRateAction* create(ActionInterval* action, float rate);
+    /**
+    @brief Set the rate value for the ease rate action.
+    @param rate The value will be set.
+    */
+    void setRate(float rate) { _rate = rate; }
+    /**
+    @brief Get the rate value of the ease rate action.
+    @return Return the rate value of the ease rate action.
+    */
+    float getRate() const { return _rate; }
+
+CC_CONSTRUCTOR_ACCESS:
+    EaseRateAction() {}
+    virtual ~EaseRateAction() {}
+    /**
+     @brief Initializes the action with the inner action and the rate parameter.
+     @param pAction The pointer of the inner action.
+     @param fRate The value of the rate parameter.
+     @return Return true when the initialization success, otherwise return false.
+    */
+    bool initWithAction(ActionInterval *pAction, float fRate);
+
+protected:
+    float _rate;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(EaseRateAction);
+};
+
+//
+// NOTE: Converting these macros into Templates is desirable, but please see
+// issue #16159 [https://github.com/cocos2d/cocos2d-x/pull/16159] for further info
+//
+#define EASE_TEMPLATE_DECL_CLASS(CLASSNAME) \
+class CC_DLL CLASSNAME : public ActionEase \
+{ \
+CC_CONSTRUCTOR_ACCESS: \
+    virtual ~CLASSNAME() { } \
+    CLASSNAME() { } \
+public: \
+    static CLASSNAME* create(ActionInterval* action); \
+    virtual CLASSNAME* clone() const override; \
+    virtual void update(float time) override; \
+    virtual ActionEase* reverse() const override; \
+private: \
+    CC_DISALLOW_COPY_AND_ASSIGN(CLASSNAME); \
+};
+
+/**
+ @class EaseExponentialIn
+ @brief Ease Exponential In action.
+ @details The timeline of inner action will be changed by:
+ \f${ 2 }^{ 10*(time-1) }-1*0.001\f$.
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseExponentialIn);
+
+/**
+ @class EaseExponentialOut
+ @brief Ease Exponential Out
+ @details The timeline of inner action will be changed by:
+ \f$1-{ 2 }^{ -10*(time-1) }\f$.
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseExponentialOut);
+
+/**
+ @class EaseExponentialInOut
+ @brief Ease Exponential InOut
+ @details If time * 2 < 1, the timeline of inner action will be changed by:
+ \f$0.5*{ 2 }^{ 10*(time-1) }\f$.
+ else, the timeline of inner action will be changed by:
+ \f$0.5*(2-{ 2 }^{ -10*(time-1) })\f$.
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseExponentialInOut);
+
+/**
+ @class EaseSineIn
+ @brief Ease Sine In
+ @details The timeline of inner action will be changed by:
+ \f$1-cos(time*\frac { \pi  }{ 2 } )\f$.
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseSineIn);
+
+/**
+ @class EaseSineOut
+ @brief Ease Sine Out
+ @details The timeline of inner action will be changed by:
+ \f$sin(time*\frac { \pi  }{ 2 } )\f$.
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseSineOut);
+
+/**
+ @class EaseSineInOut
+ @brief Ease Sine InOut
+ @details The timeline of inner action will be changed by:
+ \f$-0.5*(cos(\pi *time)-1)\f$.
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseSineInOut);
+
+/**
+ @class EaseBounce
+ @brief EaseBounce abstract class.
+ @since v0.8.2
+ @ingroup Actions
+ */
+class CC_DLL EaseBounce : public ActionEase {};
+
+/**
+ @class EaseBounceIn
+ @brief EaseBounceIn action.
+ @warning This action doesn't use a bijective function.
+ Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseBounceIn);
+
+/**
+ @class EaseBounceOut
+ @brief EaseBounceOut action.
+ @warning This action doesn't use a bijective function.
+ Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseBounceOut);
+
+/**
+ @class EaseBounceInOut
+ @brief EaseBounceInOut action.
+ @warning This action doesn't use a bijective function.
+ Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseBounceInOut);
+
+/**
+ @class EaseBackIn
+ @brief EaseBackIn action.
+ @warning This action doesn't use a bijective function.
+ Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseBackIn);
+
+/**
+ @class EaseBackOut
+ @brief EaseBackOut action.
+ @warning This action doesn't use a bijective function.
+ Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseBackOut);
+
+/**
+ @class EaseBackInOut
+ @brief EaseBackInOut action.
+ @warning This action doesn't use a bijective function.
+ Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseBackInOut);
+
+/**
+ @class EaseQuadraticActionIn
+ @brief Ease Quadratic In
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseQuadraticActionIn);
+
+/**
+ @class EaseQuadraticActionOut
+ @brief Ease Quadratic Out
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseQuadraticActionOut);
+
+/**
+ @class EaseQuadraticActionInOut
+ @brief Ease Quadratic InOut
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseQuadraticActionInOut);
+
+/**
+ @class EaseQuarticActionIn
+ @brief Ease Quartic In
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseQuarticActionIn);
+
+/**
+ @class EaseQuarticActionOut
+ @brief Ease Quartic Out
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseQuarticActionOut);
+
+/**
+ @class EaseQuarticActionInOut
+ @brief Ease Quartic InOut
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseQuarticActionInOut);
+
+/**
+ @class EaseQuinticActionIn
+ @brief Ease Quintic In
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseQuinticActionIn);
+
+/**
+ @class EaseQuinticActionOut
+ @brief Ease Quintic Out
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseQuinticActionOut);
+
+/**
+ @class EaseQuinticActionInOut
+ @brief Ease Quintic InOut
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseQuinticActionInOut);
+
+/**
+ @class EaseCircleActionIn
+ @brief Ease Circle In
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseCircleActionIn);
+
+/**
+ @class EaseCircleActionOut
+ @brief Ease Circle Out
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseCircleActionOut);
+
+/**
+ @class EaseCircleActionInOut
+ @brief Ease Circle InOut
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseCircleActionInOut);
+
+/**
+ @class EaseCubicActionIn
+ @brief Ease Cubic In
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseCubicActionIn);
+
+/**
+ @class EaseCubicActionOut
+ @brief Ease Cubic Out
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseCubicActionOut);
+
+/**
+ @class EaseCubicActionInOut
+ @brief Ease Cubic InOut
+ @ingroup Actions
+ */
+EASE_TEMPLATE_DECL_CLASS(EaseCubicActionInOut);
+
+//
+// NOTE: Converting these macros into Templates is desirable, but please see
+// issue #16159 [https://github.com/cocos2d/cocos2d-x/pull/16159] for further info
+//
+
+#define EASERATE_TEMPLATE_DECL_CLASS(CLASSNAME) \
+class CC_DLL CLASSNAME : public EaseRateAction \
+{ \
+CC_CONSTRUCTOR_ACCESS: \
+    virtual ~CLASSNAME() { } \
+    CLASSNAME() { } \
+public: \
+    static CLASSNAME* create(ActionInterval* action, float rate); \
+    virtual CLASSNAME* clone() const override; \
+    virtual void update(float time) override; \
+    virtual EaseRateAction* reverse() const override; \
+private: \
+    CC_DISALLOW_COPY_AND_ASSIGN(CLASSNAME); \
+};
+
+/**
+ @class EaseIn
+ @brief EaseIn action with a rate.
+ @details The timeline of inner action will be changed by:
+ \f${ time }^{ rate }\f$.
+ @ingroup Actions
+ */
+EASERATE_TEMPLATE_DECL_CLASS(EaseIn);
+
+/**
+ @class EaseOut
+ @brief EaseOut action with a rate.
+ @details The timeline of inner action will be changed by:
+ \f${ time }^ { (1/rate) }\f$.
+ @ingroup Actions
+ */
+EASERATE_TEMPLATE_DECL_CLASS(EaseOut);
+
+/**
+ @class EaseInOut
+ @brief EaseInOut action with a rate
+ @details If time * 2 < 1, the timeline of inner action will be changed by:
+ \f$0.5*{ time }^{ rate }\f$.
+ Else, the timeline of inner action will be changed by:
+ \f$1.0-0.5*{ 2-time }^{ rate }\f$.
+ @ingroup Actions
+ */
+EASERATE_TEMPLATE_DECL_CLASS(EaseInOut);
+
+/**
+ @class EaseElastic
+ @brief Ease Elastic abstract class
+ @since v0.8.2
+ @ingroup Actions
+ */
+class CC_DLL EaseElastic : public ActionEase
+{
+public:
+
+    /**
+     @brief Get period of the wave in radians. Default value is 0.3.
+     @return Return the period of the wave in radians.
+    */
+    float getPeriod() const { return _period; }
+    /**
+     @brief Set period of the wave in radians.
+     @param fPeriod The value will be set.
+    */
+    void setPeriod(float fPeriod) { _period = fPeriod; }
+
+CC_CONSTRUCTOR_ACCESS:
+    EaseElastic() {}
+    virtual ~EaseElastic() {}
+    /**
+     @brief Initializes the action with the inner action and the period in radians.
+     @param action The pointer of the inner action.
+     @param period Period of the wave in radians. Default is 0.3.
+     @return Return true when the initialization success, otherwise return false.
+    */
+    bool initWithAction(ActionInterval *action, float period = 0.3f);
+
+protected:
+    float _period;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(EaseElastic);
+};
+
+//
+// NOTE: Converting these macros into Templates is desirable, but please see
+// issue #16159 [https://github.com/cocos2d/cocos2d-x/pull/16159] for further info
+//
+#define EASEELASTIC_TEMPLATE_DECL_CLASS(CLASSNAME) \
+class CC_DLL CLASSNAME : public EaseElastic \
+{ \
+CC_CONSTRUCTOR_ACCESS: \
+    virtual ~CLASSNAME() { } \
+    CLASSNAME() { } \
+public: \
+    static CLASSNAME* create(ActionInterval* action, float rate = 0.3f); \
+    virtual CLASSNAME* clone() const override; \
+    virtual void update(float time) override; \
+    virtual EaseElastic* reverse() const override; \
+private: \
+    CC_DISALLOW_COPY_AND_ASSIGN(CLASSNAME); \
+};
+
+/**
+ @class EaseElasticIn
+ @brief Ease Elastic In action.
+ @details If time == 0 or time == 1, the timeline of inner action will not be changed.
+ Else, the timeline of inner action will be changed by:
+ \f$-{ 2 }^{ 10*(time-1) }*sin((time-1-\frac { period }{ 4 } )*\pi *2/period)\f$.
+
+ @warning This action doesn't use a bijective function.
+ Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ @ingroup Actions
+ */
+EASEELASTIC_TEMPLATE_DECL_CLASS(EaseElasticIn);
+
+/**
+ @class EaseElasticOut
+ @brief Ease Elastic Out action.
+ @details If time == 0 or time == 1, the timeline of inner action will not be changed.
+ Else, the timeline of inner action will be changed by:
+ \f${ 2 }^{ -10*time }*sin((time-\frac { period }{ 4 } )*\pi *2/period)+1\f$.
+ @warning This action doesn't use a bijective function.
+ Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ @ingroup Actions
+ */
+EASEELASTIC_TEMPLATE_DECL_CLASS(EaseElasticOut);
+
+/**
+ @class EaseElasticInOut
+ @brief Ease Elastic InOut action.
+ @warning This action doesn't use a bijective function.
+ Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ @ingroup Actions
+ */
+EASEELASTIC_TEMPLATE_DECL_CLASS(EaseElasticInOut);
+
+
+/**
+ @class EaseBezierAction
+ @brief Ease Bezier
+ @ingroup Actions
+ */
+class CC_DLL EaseBezierAction : public cocos2d::ActionEase
+{
+public:
+    /**
+     @brief Create the action with the inner action.
+     @param action The pointer of the inner action.
+     @return A pointer of EaseBezierAction action. If creation failed, return nil.
+    */
+    static EaseBezierAction* create(cocos2d::ActionInterval* action);
+
+    virtual void update(float time) override;
+    virtual EaseBezierAction* clone() const override;
+    virtual EaseBezierAction* reverse() const override;
+
+    /**
+     @brief Set the bezier parameters.
+    */
+    virtual void setBezierParamer( float p0, float p1, float p2, float p3);
+
+CC_CONSTRUCTOR_ACCESS:
+    EaseBezierAction() {}
+    virtual ~EaseBezierAction() {}
+
+protected:
+    float _p0;
+    float _p1;
+    float _p2;
+    float _p3;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(EaseBezierAction);
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif // __ACTION_CCEASE_ACTION_H__

+ 455 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionGrid.cpp

@@ -0,0 +1,455 @@
+/****************************************************************************
+Copyright (c) 2009      On-Core
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCActionGrid.h"
+
+#include "2d/CCGrid.h"
+#include "2d/CCNodeGrid.h"
+#include "base/CCDirector.h"
+
+NS_CC_BEGIN
+// implementation of GridAction
+
+bool GridAction::initWithDuration(float duration, const Size& gridSize)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _gridSize = gridSize;
+
+        return true;
+    }
+
+    return false;
+}
+
+void GridAction::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    cacheTargetAsGridNode();
+
+    GridBase *targetGrid = _gridNodeTarget->getGrid();
+
+    if (targetGrid && targetGrid->getReuseGrid() > 0)
+    {
+        if (targetGrid->isActive() && targetGrid->getGridSize().width == _gridSize.width
+            && targetGrid->getGridSize().height == _gridSize.height)
+        {
+            targetGrid->reuse();
+        }
+        else
+        {
+            CCASSERT(0, "Invalid grid parameters!");
+        }
+    }
+    else
+    {
+        if (targetGrid && targetGrid->isActive())
+        {
+            targetGrid->setActive(false);
+        }
+
+        auto newgrid = this->getGrid();
+        _gridNodeTarget->setGrid(newgrid);
+        _gridNodeTarget->getGrid()->setActive(true);
+    }
+}
+
+void GridAction::cacheTargetAsGridNode()
+{
+    _gridNodeTarget = dynamic_cast<NodeGrid*> (_target);
+    CCASSERT(_gridNodeTarget, "GridActions can only used on NodeGrid");
+}
+
+GridAction* GridAction::reverse() const
+{
+    // FIXME: This conversion isn't safe.
+    return (GridAction*)ReverseTime::create( this->clone() );
+}
+
+GridBase* GridAction::getGrid()
+{
+    // Abstract class needs implementation
+    CCASSERT(0, "Subclass should implement this method!");
+
+    return nullptr;
+}
+
+// implementation of Grid3DAction
+
+GridBase* Grid3DAction::getGrid()
+{
+    return Grid3D::create(_gridSize, _gridNodeTarget->getGridRect());
+}
+
+Vec3 Grid3DAction::getVertex(const Vec2& position) const
+{
+    Grid3D *g = (Grid3D*)_gridNodeTarget->getGrid();
+    return g->getVertex(position);
+}
+
+Vec3 Grid3DAction::getOriginalVertex(const Vec2& position) const
+{
+    Grid3D *g = (Grid3D*)_gridNodeTarget->getGrid();
+    return g->getOriginalVertex(position);
+}
+
+void Grid3DAction::setVertex(const Vec2& position, const Vec3& vertex)
+{
+    Grid3D *g = (Grid3D*)_gridNodeTarget->getGrid();
+    g->setVertex(position, vertex);
+}
+
+Rect Grid3DAction::getGridRect() const
+{
+    Grid3D *g = (Grid3D*)_gridNodeTarget->getGrid();
+    return g->getGridRect();
+}
+
+// implementation of TiledGrid3DAction
+TiledGrid3DAction* TiledGrid3DAction::create(float duration, const Size &gridSize)
+{
+    TiledGrid3DAction *ret = new (std::nothrow) TiledGrid3DAction();
+    if (ret && ret->initWithDuration(duration, gridSize))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    delete ret;
+    return nullptr;
+}
+
+GridBase* TiledGrid3DAction::getGrid()
+{
+    return TiledGrid3D::create(_gridSize, _gridNodeTarget->getGridRect());
+}
+
+Quad3 TiledGrid3DAction::getTile(const Vec2& pos) const
+{
+    TiledGrid3D *g = (TiledGrid3D*)_gridNodeTarget->getGrid();
+    return g->getTile(pos);
+}
+
+Quad3 TiledGrid3DAction::getOriginalTile(const Vec2& pos) const
+{
+    TiledGrid3D *g = (TiledGrid3D*)_gridNodeTarget->getGrid();
+    return g->getOriginalTile(pos);
+}
+
+void TiledGrid3DAction::setTile(const Vec2& pos, const Quad3& coords)
+{
+    TiledGrid3D *g = (TiledGrid3D*)_gridNodeTarget->getGrid();
+    return g->setTile(pos, coords);
+}
+
+// implementation AccelDeccelAmplitude
+
+AccelDeccelAmplitude* AccelDeccelAmplitude::create(Action *action, float duration)
+{
+    AccelDeccelAmplitude *ret = new (std::nothrow) AccelDeccelAmplitude();
+    if (ret && ret->initWithAction(action, duration))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    delete ret;
+    return nullptr;
+}
+
+bool AccelDeccelAmplitude::initWithAction(Action *action, float duration)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _rate = 1.0f;
+        _other = (ActionInterval*)(action);
+        action->retain();
+
+        return true;
+    }
+
+    return false;
+}
+
+AccelDeccelAmplitude* AccelDeccelAmplitude::clone() const
+{
+    // no copy constructor
+    if (_other)
+        return AccelDeccelAmplitude::create(_other->clone(), _rate);
+    
+    return nullptr;
+}
+
+AccelDeccelAmplitude::~AccelDeccelAmplitude()
+{
+    CC_SAFE_RELEASE(_other);
+}
+
+void AccelDeccelAmplitude::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _other->startWithTarget(target);
+}
+
+void AccelDeccelAmplitude::update(float time)
+{
+    float f = time * 2;
+
+    if (f > 1)
+    {
+        f -= 1;
+        f = 1 - f;
+    }
+
+    ((AccelDeccelAmplitude*)(_other))->setAmplitudeRate(powf(f, _rate));
+}
+
+AccelDeccelAmplitude* AccelDeccelAmplitude::reverse() const
+{
+    if (_other)
+        return AccelDeccelAmplitude::create(_other->reverse(), _duration);
+    
+    return nullptr;
+}
+
+// implementation of AccelAmplitude
+
+AccelAmplitude* AccelAmplitude::create(Action *action, float duration)
+{
+    AccelAmplitude *ret = new (std::nothrow) AccelAmplitude();
+    if (ret && ret->initWithAction(action, duration))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    delete ret;
+    return nullptr;
+}
+
+bool AccelAmplitude::initWithAction(Action *action, float duration)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _rate = 1.0f;
+        _other = (ActionInterval*)(action);
+        action->retain();
+
+        return true;
+    }
+
+    return false;
+}
+
+AccelAmplitude* AccelAmplitude::clone() const
+{
+    // no copy constructor
+    if (_other)
+        return AccelAmplitude::create(_other->clone(), _duration);
+    
+    return nullptr;
+}
+
+AccelAmplitude::~AccelAmplitude()
+{
+    CC_SAFE_DELETE(_other);
+}
+
+void AccelAmplitude::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _other->startWithTarget(target);
+}
+
+void AccelAmplitude::update(float time)
+{
+    ((AccelAmplitude*)(_other))->setAmplitudeRate(powf(time, _rate));
+    _other->update(time);
+}
+
+AccelAmplitude* AccelAmplitude::reverse() const
+{
+    if (_other)
+        return AccelAmplitude::create(_other->reverse(), _duration);
+    
+    return nullptr;
+}
+
+// DeccelAmplitude
+
+DeccelAmplitude* DeccelAmplitude::create(Action *action, float duration)
+{
+    DeccelAmplitude *ret = new (std::nothrow) DeccelAmplitude();
+    if (ret && ret->initWithAction(action, duration))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    delete ret;
+    return nullptr;
+}
+
+bool DeccelAmplitude::initWithAction(Action *action, float duration)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _rate = 1.0f;
+        _other = (ActionInterval*)(action);
+        action->retain();
+
+        return true;
+    }
+
+    return false;
+}
+
+DeccelAmplitude::~DeccelAmplitude()
+{
+    CC_SAFE_RELEASE(_other);
+}
+
+void DeccelAmplitude::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _other->startWithTarget(target);
+}
+
+void DeccelAmplitude::update(float time)
+{
+    ((DeccelAmplitude*)(_other))->setAmplitudeRate(powf((1 - time), _rate));
+    _other->update(time);
+}
+
+DeccelAmplitude* DeccelAmplitude::clone() const
+{
+    // no copy constructor
+    if (_other)
+        return DeccelAmplitude::create(_other->clone(), _duration);
+    
+    return nullptr;
+}
+
+DeccelAmplitude* DeccelAmplitude::reverse() const
+{
+    return DeccelAmplitude::create(_other->reverse(), _duration);
+}
+
+// implementation of StopGrid
+
+void StopGrid::startWithTarget(Node *target)
+{
+    ActionInstant::startWithTarget(target);
+    cacheTargetAsGridNode();
+    GridBase *grid = _gridNodeTarget->getGrid();
+    if (grid && grid->isActive())
+    {
+        grid->setActive(false);
+    }
+}
+
+void StopGrid::cacheTargetAsGridNode()
+{
+    _gridNodeTarget = dynamic_cast<NodeGrid*> (_target);
+    CCASSERT(_gridNodeTarget, "GridActions can only used on NodeGrid");
+}
+
+StopGrid* StopGrid::create()
+{
+    StopGrid* action = new (std::nothrow) StopGrid();
+    if (action)
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+StopGrid* StopGrid::clone() const
+{
+    return StopGrid::create();
+}
+
+StopGrid* StopGrid::reverse() const
+{
+    // no reverse, just clone it
+    return this->clone();
+}
+
+// implementation of ReuseGrid
+
+ReuseGrid* ReuseGrid::create(int times)
+{
+    ReuseGrid *action = new (std::nothrow) ReuseGrid();
+    if (action && action->initWithTimes(times))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool ReuseGrid::initWithTimes(int times)
+{
+    _times = times;
+
+    return true;
+}
+
+void ReuseGrid::startWithTarget(Node *target)
+{
+    ActionInstant::startWithTarget(target);
+    cacheTargetAsGridNode();
+
+    if (_gridNodeTarget->getGrid() && _gridNodeTarget->getGrid()->isActive())
+    {
+        _gridNodeTarget->getGrid()->setReuseGrid(_gridNodeTarget->getGrid()->getReuseGrid() + _times);
+    }
+}
+
+void ReuseGrid::cacheTargetAsGridNode()
+{
+    _gridNodeTarget = dynamic_cast<NodeGrid*> (_target);
+    CCASSERT(_gridNodeTarget, "GridActions can only used on NodeGrid");
+}
+
+ReuseGrid* ReuseGrid::clone() const
+{
+    return ReuseGrid::create(_times);
+}
+
+ReuseGrid* ReuseGrid::reverse() const
+{
+    // no reverse, just clone it
+    return this->clone();
+}
+
+NS_CC_END

+ 422 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionGrid.h

@@ -0,0 +1,422 @@
+/****************************************************************************
+Copyright (c) 2009      On-Core
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __ACTION_CCGRID_ACTION_H__
+#define __ACTION_CCGRID_ACTION_H__
+
+#include "2d/CCActionInterval.h"
+#include "2d/CCActionInstant.h"
+
+NS_CC_BEGIN
+
+class GridBase;
+class NodeGrid;
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/** 
+@class GridAction
+@brief Base class for Grid actions.
+@details Grid actions are the actions take effect on GridBase.
+*/
+class CC_DLL GridAction : public ActionInterval
+{
+public:
+
+    /** 
+    @brief Get the pointer of GridBase.
+    @return The pointer of GridBase.
+    */
+    virtual GridBase* getGrid();
+
+    // overrides
+    virtual GridAction * clone() const override
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+    virtual GridAction* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    GridAction() {}
+    virtual ~GridAction() {}
+    /** 
+     * @brief Initializes the action with size and duration.
+     * @param duration The duration of the GridAction. It's a value in seconds.
+     * @param gridSize The size of the GridAction should be.
+     * @return Return true when the initialization success, otherwise return false.
+     */
+    bool initWithDuration(float duration, const Size& gridSize);
+
+protected:
+    Size _gridSize;
+    
+    NodeGrid* _gridNodeTarget;
+    
+    void cacheTargetAsGridNode();
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(GridAction);
+};
+
+/** 
+ @brief Base class for Grid3D actions.
+ @details Grid3D actions can modify a non-tiled grid.
+ */
+class CC_DLL Grid3DAction : public GridAction
+{
+public:
+
+
+    virtual GridBase* getGrid() override;
+    /**
+     * @brief Get the vertex that belongs to certain position in the grid.
+     * @param position The position of the grid.
+     * @return Return a pointer of vertex.
+     * @js vertex
+     * @lua NA
+     */
+    Vec3 getVertex(const Vec2& position) const;
+
+    /** 
+     * @brief Get the non-transformed vertex that belongs to certain position in the grid.
+     * @param position The position of the grid.
+     * @return Return a pointer of vertex.
+     * @js originalVertex
+     * @lua NA
+     */
+    Vec3 getOriginalVertex(const Vec2& position) const;
+
+    /** 
+     * @brief Set a new vertex to a certain position of the grid.
+     * @param position The position of the grid.
+     * @param vertex The vertex will be used on the certain position of grid.
+     * @js setVertex
+     * @lua NA
+     */
+    void setVertex(const Vec2& position, const Vec3& vertex);
+
+    // Overrides
+    virtual Grid3DAction * clone() const override
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+    
+    /**
+     * @brief Get the effect grid rect.
+     * @return Return the effect grid rect.
+     */
+    Rect getGridRect() const;
+};
+
+/**
+@brief Base class for TiledGrid3D actions.
+*/
+class CC_DLL TiledGrid3DAction : public GridAction
+{
+public:
+    /** 
+     * @brief Create the action with size and duration.
+     * @param duration The duration of the action. It's a value in seconds.
+     * @param gridSize Specify the grid size of the action.
+     * @return A pointer of TiledGrid3DAction. If creation failed, return nil.
+     * @lua NA
+     */
+    static TiledGrid3DAction* create(float duration, const Size& gridSize);
+
+    /** 
+     * @brief Get the tile that belongs to a certain position of the grid.
+     * @param position The position of the tile want to get.
+     * @return A quadrilateral of the tile. 
+     * @js tile
+     * @lua NA
+     */
+    Quad3 getTile(const Vec2& position) const;
+
+    /** 
+     * @brief Get the non-transformed tile that belongs to a certain position of the grid.
+     * @param position The position of the tile want to get.
+     * @return A quadrilateral of the tile. 
+     * @js originalTile
+     * @lua NA
+     */
+    Quad3 getOriginalTile(const Vec2& position) const;
+
+    /** 
+     * @brief Set a new tile to a certain position of the grid.
+     * @param position The position of the tile.
+     * @param coords The quadrilateral of the new tile.
+     * @lua NA
+     */
+    void setTile(const Vec2& position, const Quad3& coords);
+
+    /** returns the grid */
+    virtual GridBase* getGrid() override;
+
+    // Override
+    virtual TiledGrid3DAction * clone() const override
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+};
+
+/**
+@brief AccelDeccelAmplitude action.
+@js NA
+*/
+class CC_DLL AccelDeccelAmplitude : public ActionInterval
+{
+public:
+    /**
+    @brief Create the action with an inner action that has the amplitude property, and a duration time.
+    @param action A pointer of the inner action.
+    @param duration Specify the duration of the AccelDeccelAmplitude action.
+    @return Return a pointer of AccelDeccelAmplitude action. When the creation failed, return nil.
+    */
+    static AccelDeccelAmplitude* create(Action *action, float duration);
+
+    /** 
+    @brief Get the value of amplitude rate.
+    @return the value of amplitude rate.
+    */
+    float getRate() const { return _rate; }
+    /**
+    @brief Set the value of amplitude rate.
+    @param rate Specify the value of amplitude rate.
+    */
+    void setRate(float rate) { _rate = rate; }
+
+    // Overrides
+    virtual void startWithTarget(Node *target) override;
+    virtual void update(float time) override;
+    virtual AccelDeccelAmplitude* clone() const override;
+    virtual AccelDeccelAmplitude* reverse() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    AccelDeccelAmplitude() {}
+    virtual ~AccelDeccelAmplitude();
+    
+    /** 
+    @brief Initializes the action with an inner action that has the amplitude property, and a duration time.
+    @param action A pointer of the inner action.
+    @param duration Specify the duration of the AccelDeccelAmplitude action.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithAction(Action *action, float duration);
+
+protected:
+    float _rate;
+    ActionInterval *_other;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(AccelDeccelAmplitude);
+};
+
+/**
+@brief AccelAmplitude action.
+@js NA
+*/
+class CC_DLL AccelAmplitude : public ActionInterval
+{
+public:
+    /** 
+    @brief Create the action with an inner action that has the amplitude property, and a duration time.
+    @param action A pointer of the inner action.
+    @param duration Specify the duration of the AccelAmplitude action.
+    @return Return a pointer of AccelAmplitude action. When the creation failed, return nil.
+     */
+    static AccelAmplitude* create(Action *action, float duration);
+
+    /** 
+    @brief Get the value of amplitude rate.
+    @return The value of amplitude rate.
+    */
+    float getRate() const { return _rate; }
+    /**
+    @brief Set the value of amplitude rate.
+    @param rate Specify the value of amplitude rate.
+    */
+    void setRate(float rate) { _rate = rate; }
+
+    // Overrides
+    virtual void startWithTarget(Node *target) override;
+    virtual void update(float time) override;
+    virtual AccelAmplitude* clone() const override;
+    virtual AccelAmplitude* reverse() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    AccelAmplitude() {}
+    virtual ~AccelAmplitude();
+
+    bool initWithAction(Action *action, float duration);
+
+protected:
+    float _rate;
+    ActionInterval *_other;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(AccelAmplitude);
+};
+
+/**
+@brief DeccelAmplitude action.
+@js NA
+*/
+class CC_DLL DeccelAmplitude : public ActionInterval
+{
+public:
+    /** 
+    @brief Creates the action with an inner action that has the amplitude property, and a duration time.
+    @param action A pointer of the inner action.
+    @param duration Specify the duration of the DeccelAmplitude action.
+    @return Return a pointer of DeccelAmplitude. When the creation failed, return nil.
+    */
+    static DeccelAmplitude* create(Action *action, float duration);
+
+    /** 
+    @brief Get the value of amplitude rate.
+    @return The value of amplitude rate.
+    */
+    float getRate() const { return _rate; }
+    /**
+    @brief Set the value of amplitude rate.
+    @param rate Specify the value.
+    */
+    void setRate(float rate) { _rate = rate; }
+
+    // overrides
+    virtual void startWithTarget(Node *target) override;
+    virtual void update(float time) override;
+    virtual DeccelAmplitude* clone() const override;
+    virtual DeccelAmplitude* reverse() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    DeccelAmplitude() {}
+    virtual ~DeccelAmplitude();
+
+    /** 
+    @brief Initializes the action with an inner action that has the amplitude property, and a duration time.
+    @param action The pointer of inner action.
+    @param duration The duration of the DeccelAmplitude action.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithAction(Action *action, float duration);
+
+protected:
+    float _rate;
+    ActionInterval *_other;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(DeccelAmplitude);
+};
+
+/**
+ @brief StopGrid action.
+ @warning Don't call this action if another grid action is active.
+ Call if you want to remove the grid effect. Example:
+ @code
+ Sequence::create(Lens3D::create(...), StopGrid::create(), nullptr);
+ @endcode
+ */
+class CC_DLL StopGrid : public ActionInstant
+{
+public:
+    /** 
+    @brief Create a StopGrid Action.
+    @return Return a pointer of StopGrid. When the creation failed, return nil.
+    */
+    static StopGrid* create();
+
+    // Overrides
+    virtual void startWithTarget(Node *target) override;
+    virtual StopGrid* clone() const override;
+    virtual StopGrid* reverse() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    StopGrid() {}
+    virtual ~StopGrid() {}
+    
+protected:
+    NodeGrid* _gridNodeTarget;
+    
+    void cacheTargetAsGridNode();
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(StopGrid);
+};
+
+/** 
+@brief ReuseGrid action.
+*/
+class CC_DLL ReuseGrid : public ActionInstant
+{
+public:
+    /** 
+    @brief Create an action with the number of times that the current grid will be reused.
+    @param times Specify times the grid will be reused.
+    @return Return a pointer of ReuseGrid. When the creation failed, return nil.
+    */
+    static ReuseGrid* create(int times);
+
+    // Override
+    virtual void startWithTarget(Node *target) override;
+    virtual ReuseGrid* clone() const override;
+    virtual ReuseGrid* reverse() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    ReuseGrid() {}
+    virtual ~ReuseGrid() {}
+    
+    /** 
+    @brief Initializes an action with the number of times that the current grid will be reused.
+    @param times Specify times the grid will be reused.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithTimes(int times);
+
+protected:
+    NodeGrid* _gridNodeTarget;
+    
+    void cacheTargetAsGridNode();
+    
+    int _times;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ReuseGrid);
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif // __ACTION_CCGRID_ACTION_H__

+ 739 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionGrid3D.cpp

@@ -0,0 +1,739 @@
+/****************************************************************************
+Copyright (c) 2009      On-Core
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCActionGrid3D.h"
+#include "base/CCDirector.h"
+
+NS_CC_BEGIN
+// implementation of Waves3D
+
+Waves3D* Waves3D::create(float duration, const Size& gridSize, unsigned int waves, float amplitude)
+{
+    Waves3D *action = new (std::nothrow) Waves3D();
+
+    if (action && action->initWithDuration(duration, gridSize, waves, amplitude))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool Waves3D::initWithDuration(float duration, const Size& gridSize, unsigned int waves, float amplitude)
+{
+    if (Grid3DAction::initWithDuration(duration, gridSize))
+    {
+        _waves = waves;
+        _amplitude = amplitude;
+        _amplitudeRate = 1.0f;
+
+        return true;
+    }
+
+    return false;
+}
+
+Waves3D* Waves3D::clone() const
+{
+    // no copy constructor
+    return Waves3D::create(_duration, _gridSize, _waves, _amplitude);
+}
+
+void Waves3D::update(float time)
+{
+    int i, j;
+    for (i = 0; i < _gridSize.width + 1; ++i)
+    {
+        for (j = 0; j < _gridSize.height + 1; ++j)
+        {
+            Vec2 pos((float)i, (float)j);
+            Vec3 v = getOriginalVertex(pos);
+            v.z += (sinf((float)M_PI * time * _waves * 2 + (v.y+v.x) * 0.01f) * _amplitude * _amplitudeRate);
+            //CCLOG("v.z offset is %f\n", (sinf((float)M_PI * time * _waves * 2 + (v.y+v.x) * .01f) * _amplitude * _amplitudeRate));
+            setVertex(pos, v);
+        }
+    }
+}
+
+// implementation of FlipX3D
+
+FlipX3D* FlipX3D::create(float duration)
+{
+    FlipX3D *action = new (std::nothrow) FlipX3D();
+
+    if (action && action->initWithDuration(duration))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool FlipX3D::initWithDuration(float duration)
+{
+    return Grid3DAction::initWithDuration(duration, Size(1, 1));
+}
+
+bool FlipX3D::initWithSize(const Size& gridSize, float duration)
+{
+    if (gridSize.width != 1 || gridSize.height != 1)
+    {
+        // Grid size must be (1,1)
+        CCASSERT(0, "Grid size must be (1,1)");
+
+        return false;
+    }
+
+    return Grid3DAction::initWithDuration(duration, gridSize);
+}
+
+FlipX3D* FlipX3D::clone() const
+{
+    // no copy constructor    
+    auto a = new (std::nothrow) FlipX3D();
+    a->initWithSize(_gridSize, _duration);
+    a->autorelease();
+    return a;
+}
+
+void FlipX3D::update(float time)
+{
+    float angle = (float)M_PI * time; // 180 degrees
+    float mz = sinf(angle);
+    angle = angle / 2.0f; // x calculates degrees from 0 to 90
+    float mx = cosf(angle);
+
+    Vec3 v0, v1, v, diff;
+
+    v0 = getOriginalVertex(Vec2(1.0f, 1.0f));
+    v1 = getOriginalVertex(Vec2());
+
+    float    x0 = v0.x;
+    float    x1 = v1.x;
+    float    x;
+    Vec2    a, b, c, d;
+
+    if ( x0 > x1 )
+    {
+        // Normal Grid
+        a.setZero();
+        b.set(0.0f, 1.0f);
+        c.set(1.0f, 0.0f);
+        d.set(1.0f, 1.0f);
+        x = x0;
+    }
+    else
+    {
+        // Reversed Grid
+        c.setZero();
+        d.set(0.0f, 1.0f);
+        a.set(1.0f, 0.0f);
+        b.set(1.0f, 1.0f);
+        x = x1;
+    }
+    
+    diff.x = ( x - x * mx );
+    diff.z = fabsf( floorf( (x * mz) / 4.0f ) );
+
+    // bottom-left
+    v = getOriginalVertex(a);
+    v.x = diff.x;
+    v.z += diff.z;
+    setVertex(a, v);
+    
+    // upper-left
+    v = getOriginalVertex(b);
+    v.x = diff.x;
+    v.z += diff.z;
+    setVertex(b, v);
+    
+    // bottom-right
+    v = getOriginalVertex(c);
+    v.x -= diff.x;
+    v.z -= diff.z;
+    setVertex(c, v);
+    
+    // upper-right
+    v = getOriginalVertex(d);
+    v.x -= diff.x;
+    v.z -= diff.z;
+    setVertex(d, v);
+}
+
+// implementation of FlipY3D
+
+FlipY3D* FlipY3D::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) FlipY3D();
+    a->initWithSize(_gridSize, _duration);
+    a->autorelease();
+    return a;
+}
+
+FlipY3D* FlipY3D::create(float duration)
+{
+    FlipY3D *action = new (std::nothrow) FlipY3D();
+
+    if (action)
+    {
+        if (action->initWithDuration(duration))
+        {
+            action->autorelease();
+        }
+        else
+        {
+            CC_SAFE_RELEASE_NULL(action);
+        }
+    }
+
+    return action;
+}
+
+void FlipY3D::update(float time)
+{
+    float angle = (float)M_PI * time; // 180 degrees
+    float mz = sinf( angle );
+    angle = angle / 2.0f;     // x calculates degrees from 0 to 90
+    float my = cosf(angle);
+    
+    Vec3    v0, v1, v, diff;
+    
+    v0 = getOriginalVertex(Vec2(1.0f, 1.0f));
+    v1 = getOriginalVertex(Vec2());
+    
+    float    y0 = v0.y;
+    float    y1 = v1.y;
+    float y;
+    Vec2    a, b, c, d;
+    
+    if (y0 > y1)
+    {
+        // Normal Grid
+        a.setZero();
+        b.set(0.0f, 1.0f);
+        c.set(1.0f, 0.0f);
+        d.set(1.0f, 1.0f);
+        y = y0;
+    }
+    else
+    {
+        // Reversed Grid
+        b.setZero();
+        a.set(0.0f, 1.0f);
+        d.set(1.0f, 0.0f);
+        c.set(1.0f, 1.0f);
+        y = y1;
+    }
+    
+    diff.y = y - y * my;
+    diff.z = fabsf(floorf((y * mz) / 4.0f));
+    
+    // bottom-left
+    v = getOriginalVertex(a);
+    v.y = diff.y;
+    v.z += diff.z;
+    setVertex(a, v);
+    
+    // upper-left
+    v = getOriginalVertex(b);
+    v.y -= diff.y;
+    v.z -= diff.z;
+    setVertex(b, v);
+    
+    // bottom-right
+    v = getOriginalVertex(c);
+    v.y = diff.y;
+    v.z += diff.z;
+    setVertex(c, v);
+    
+    // upper-right
+    v = getOriginalVertex(d);
+    v.y -= diff.y;
+    v.z -= diff.z;
+    setVertex(d, v);
+}
+
+
+// implementation of Lens3D
+
+Lens3D* Lens3D::create(float duration, const Size& gridSize, const Vec2& position, float radius)
+{
+    Lens3D *action = new (std::nothrow) Lens3D();
+
+    if (action)
+    {
+        if (action->initWithDuration(duration, gridSize, position, radius))
+        {
+            action->autorelease();
+        }
+        else
+        {
+            CC_SAFE_RELEASE_NULL(action);
+        }
+    }
+
+    return action;
+}
+
+bool Lens3D::initWithDuration(float duration, const Size& gridSize, const Vec2& position, float radius)
+{
+    if (Grid3DAction::initWithDuration(duration, gridSize))
+    {
+        _position.set(-1.0f, -1.0f);
+        setPosition(position);
+        _radius = radius;
+        _lensEffect = 0.7f;
+        _concave = false;
+        _dirty = true;
+
+        return true;
+    }
+
+    return false;
+}
+
+Lens3D* Lens3D::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) Lens3D();
+    a->initWithDuration(_duration, _gridSize, _position, _radius);
+    a->autorelease();
+    return a;
+}
+
+void Lens3D::setPosition(const Vec2& pos)
+{
+    if( !pos.equals(_position))
+    {
+        _position = pos;
+        _dirty = true;
+    }
+}
+
+void Lens3D::update(float /*time*/)
+{
+    if (_dirty)
+    {
+        int i, j;
+        
+        for (i = 0; i < _gridSize.width + 1; ++i)
+        {
+            for (j = 0; j < _gridSize.height + 1; ++j)
+            {
+                Vec2 pos((float)i, (float)j);
+                Vec3 v = getOriginalVertex(pos);
+                Vec2 vect = _position - Vec2(v.x, v.y);
+                float r = vect.getLength();
+                
+                if (r < _radius)
+                {
+                    r = _radius - r;
+                    float pre_log = r / _radius;
+                    if ( pre_log == 0 ) 
+                    {
+                        pre_log = 0.001f;
+                    }
+
+                    float l = logf(pre_log) * _lensEffect;
+                    float new_r = expf( l ) * _radius;
+                    
+                    if (vect.getLength() > 0)
+                    {
+                        vect.normalize();
+                        Vec2 new_vect = vect * new_r;
+                        v.z += (_concave ? -1.0f : 1.0f) * new_vect.getLength() * _lensEffect;
+                    }
+                }
+                
+                setVertex(pos, v);
+            }
+        }
+        
+        _dirty = false;
+    }
+}
+
+// implementation of Ripple3D
+
+Ripple3D* Ripple3D::create(float duration, const Size& gridSize, const Vec2& position, float radius, unsigned int waves, float amplitude)
+{
+    Ripple3D *action = new (std::nothrow) Ripple3D();
+
+    if (action)
+    {
+        if (action->initWithDuration(duration, gridSize, position, radius, waves, amplitude))
+        {
+            action->autorelease();
+        }
+        else
+        {
+            CC_SAFE_RELEASE_NULL(action);
+        }
+    }
+
+    return action;
+}
+
+bool Ripple3D::initWithDuration(float duration, const Size& gridSize, const Vec2& position, float radius, unsigned int waves, float amplitude)
+{
+    if (Grid3DAction::initWithDuration(duration, gridSize))
+    {
+        setPosition(position);
+        _radius = radius;
+        _waves = waves;
+        _amplitude = amplitude;
+        _amplitudeRate = 1.0f;
+
+        return true;
+    }
+
+    return false;
+}
+
+void Ripple3D::setPosition(const Vec2& position)
+{
+    _position = position;
+}
+
+
+Ripple3D* Ripple3D::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) Ripple3D();
+    a->initWithDuration(_duration, _gridSize, _position, _radius, _waves, _amplitude);
+    a->autorelease();
+    return a;
+}
+
+void Ripple3D::update(float time)
+{
+    int i, j;
+
+    for (i = 0; i < (_gridSize.width+1); ++i)
+    {
+        for (j = 0; j < (_gridSize.height+1); ++j)
+        {
+            Vec2 pos((float)i, (float)j);
+            Vec3 v = getOriginalVertex(pos);
+            Vec2 vect = _position - Vec2(v.x,v.y);
+            float r = vect.getLength();
+            
+            if (r < _radius)
+            {
+                r = _radius - r;
+                float rate = powf(r / _radius, 2);
+                v.z += (sinf( time*(float)M_PI * _waves * 2 + r * 0.1f) * _amplitude * _amplitudeRate * rate);
+            }
+            
+            setVertex(pos, v);
+        }
+    }
+}
+
+// implementation of Shaky3D
+
+Shaky3D* Shaky3D::create(float duration, const Size& gridSize, int range, bool shakeZ)
+{
+    Shaky3D *action = new (std::nothrow) Shaky3D();
+
+    if (action)
+    {
+        if (action->initWithDuration(duration, gridSize, range, shakeZ))
+        {
+            action->autorelease();
+        }
+        else
+        {
+            CC_SAFE_RELEASE_NULL(action);
+        }
+    }
+
+    return action;
+}
+
+bool Shaky3D::initWithDuration(float duration, const Size& gridSize, int range, bool shakeZ)
+{
+    if (Grid3DAction::initWithDuration(duration, gridSize))
+    {
+        _randrange = range;
+        _shakeZ = shakeZ;
+
+        return true;
+    }
+
+    return false;
+}
+
+Shaky3D* Shaky3D::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) Shaky3D();
+    a->initWithDuration(_duration, _gridSize, _randrange, _shakeZ);
+    a->autorelease();
+    return a;
+}
+
+void Shaky3D::update(float /*time*/)
+{
+    int i, j;
+
+    for (i = 0; i < (_gridSize.width+1); ++i)
+    {
+        for (j = 0; j < (_gridSize.height+1); ++j)
+        {
+            Vec2 pos((float)i, (float)j);
+            Vec3 v = getOriginalVertex(pos);
+            v.x += (rand() % (_randrange*2)) - _randrange;
+            v.y += (rand() % (_randrange*2)) - _randrange;
+            if (_shakeZ)
+            {
+                v.z += (rand() % (_randrange*2)) - _randrange;
+            }
+            
+            setVertex(pos, v);
+        }
+    }
+}
+
+// implementation of Liquid
+
+Liquid* Liquid::create(float duration, const Size& gridSize, unsigned int waves, float amplitude)
+{
+    Liquid *action = new (std::nothrow) Liquid();
+
+    if (action)
+    {
+        if (action->initWithDuration(duration, gridSize, waves, amplitude))
+        {
+            action->autorelease();
+        }
+        else
+        {
+            CC_SAFE_RELEASE_NULL(action);
+        }
+    }
+
+    return action;
+}
+
+bool Liquid::initWithDuration(float duration, const Size& gridSize, unsigned int waves, float amplitude)
+{
+    if (Grid3DAction::initWithDuration(duration, gridSize))
+    {
+        _waves = waves;
+        _amplitude = amplitude;
+        _amplitudeRate = 1.0f;
+
+        return true;
+    }
+
+    return false;
+}
+
+Liquid* Liquid::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) Liquid();
+    a->initWithDuration(_duration, _gridSize, _waves, _amplitude);
+    a->autorelease();
+    return a;
+}
+
+void Liquid::update(float time)
+{
+    int i, j;
+
+    for (i = 1; i < _gridSize.width; ++i)
+    {
+        for (j = 1; j < _gridSize.height; ++j)
+        {
+            Vec2 pos((float)i, (float)j);
+            Vec3 v = getOriginalVertex(pos);
+            v.x = (v.x + (sinf(time * (float)M_PI * _waves * 2 + v.x * .01f) * _amplitude * _amplitudeRate));
+            v.y = (v.y + (sinf(time * (float)M_PI * _waves * 2 + v.y * .01f) * _amplitude * _amplitudeRate));
+            setVertex(pos, v);
+        }
+    }
+}
+
+// implementation of Waves
+
+Waves* Waves::create(float duration, const Size& gridSize, unsigned int waves, float amplitude, bool horizontal, bool vertical)
+{
+    Waves *action = new (std::nothrow) Waves();
+
+    if (action)
+    {
+        if (action->initWithDuration(duration, gridSize, waves, amplitude, horizontal, vertical))
+        {
+            action->autorelease();
+        }
+        else
+        {
+            CC_SAFE_RELEASE_NULL(action);
+        }
+    }
+
+    return action;
+}
+
+bool Waves::initWithDuration(float duration, const Size& gridSize, unsigned int waves, float amplitude, bool horizontal, bool vertical)
+{
+    if (Grid3DAction::initWithDuration(duration, gridSize))
+    {
+        _waves = waves;
+        _amplitude = amplitude;
+        _amplitudeRate = 1.0f;
+        _horizontal = horizontal;
+        _vertical = vertical;
+
+        return true;
+    }
+
+    return false;
+}
+
+Waves* Waves::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) Waves();
+    a->initWithDuration(_duration, _gridSize, _waves, _amplitude, _horizontal, _vertical);
+    a->autorelease();
+    return a;
+}
+
+void Waves::update(float time)
+{
+    int i, j;
+
+    for (i = 0; i < _gridSize.width + 1; ++i)
+    {
+        for (j = 0; j < _gridSize.height + 1; ++j)
+        {
+            Vec2 pos((float)i, (float)j);
+            Vec3 v = getOriginalVertex(pos);
+
+            if (_vertical)
+            {
+                v.x = (v.x + (sinf(time * (float)M_PI * _waves * 2 + v.y * .01f) * _amplitude * _amplitudeRate));
+            }
+
+            if (_horizontal)
+            {
+                v.y = (v.y + (sinf(time * (float)M_PI * _waves * 2 + v.x * .01f) * _amplitude * _amplitudeRate));
+            }
+
+            setVertex(pos, v);
+        }
+    }
+}
+
+// implementation of Twirl
+
+Twirl* Twirl::create(float duration, const Size& gridSize, const Vec2& position, unsigned int twirls, float amplitude)
+{
+    Twirl *action = new (std::nothrow) Twirl();
+
+    if (action)
+    {
+        if (action->initWithDuration(duration, gridSize, position, twirls, amplitude))
+        {
+            action->autorelease();
+        }
+        else
+        {
+            CC_SAFE_RELEASE_NULL(action);
+        }
+    }
+
+    return action;
+}
+
+bool Twirl::initWithDuration(float duration, const Size& gridSize, const Vec2& position, unsigned int twirls, float amplitude)
+{
+    if (Grid3DAction::initWithDuration(duration, gridSize))
+    {
+        setPosition(position);
+        _twirls = twirls;
+        _amplitude = amplitude;
+        _amplitudeRate = 1.0f;
+
+        return true;
+    }
+
+    return false;
+}
+
+void Twirl::setPosition(const Vec2& position)
+{
+    _position = position;
+}
+
+Twirl *Twirl::clone() const
+{
+    // no copy constructor    
+    auto a = new (std::nothrow) Twirl();
+    a->initWithDuration(_duration, _gridSize, _position, _twirls, _amplitude);
+    a->autorelease();
+    return a;
+}
+
+void Twirl::update(float time)
+{
+    int i, j;
+    Vec2    c = _position;
+    
+    for (i = 0; i < (_gridSize.width+1); ++i)
+    {
+        for (j = 0; j < (_gridSize.height+1); ++j)
+        {
+            Vec2 pos((float)i, (float)j);
+            Vec3 v = getOriginalVertex(pos);
+            
+            Vec2 avg(i-(_gridSize.width/2.0f), j-(_gridSize.height/2.0f));
+            float r = avg.getLength();
+            
+            float amp = 0.1f * _amplitude * _amplitudeRate;
+            float a = r * cosf( (float)M_PI/2.0f + time * (float)M_PI * _twirls * 2 ) * amp;
+            
+            Vec2 d(
+                sinf(a) * (v.y-c.y) + cosf(a) * (v.x-c.x),
+                cosf(a) * (v.y-c.y) - sinf(a) * (v.x-c.x));
+            
+            v.x = c.x + d.x;
+            v.y = c.y + d.y;
+
+            setVertex(pos, v);
+        }
+    }
+}
+
+NS_CC_END

+ 620 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionGrid3D.h

@@ -0,0 +1,620 @@
+/****************************************************************************
+Copyright (c) 2009      On-Core
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __ACTION_CCGRID3D_ACTION_H__
+#define __ACTION_CCGRID3D_ACTION_H__
+
+#include "2d/CCActionGrid.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/** 
+@brief Waves3D action.
+@details This action is used for take effect on the target node as 3D waves.
+        You can control the effect by these parameters:
+        duration, grid size, waves count, amplitude.
+*/
+class CC_DLL Waves3D : public Grid3DAction
+{
+public:
+    /**
+    @brief Create an action with duration, grid size, waves and amplitude.
+    @param duration Specify the duration of the Waves3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param waves Specify the waves count of the Waves3D action.
+    @param amplitude Specify the amplitude of the Waves3D action.
+    @return If the creation success, return a pointer of Waves3D action; otherwise, return nil.
+    */
+    static Waves3D* create(float duration, const Size& gridSize, unsigned int waves, float amplitude);
+
+    /**
+    @brief Get the amplitude of the effect.
+    @return Return the amplitude of the effect.
+    */
+    float getAmplitude() const { return _amplitude; }
+    /**
+    @brief Set the amplitude to the effect.
+    @param amplitude The value of amplitude will be set.
+    */
+    void setAmplitude(float amplitude) { _amplitude = amplitude; }
+
+    /**
+    @brief Get the amplitude rate of the effect.
+    @return Return the amplitude rate of the effect.
+    */
+    float getAmplitudeRate() const { return _amplitudeRate; }
+    /**
+    @brief Set the amplitude rate of the effect.
+    @param amplitudeRate The value of amplitude rate will be set.
+    */
+    void setAmplitudeRate(float amplitudeRate) { _amplitudeRate = amplitudeRate; }
+
+    // Overrides
+    virtual Waves3D* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Waves3D() {}
+    virtual ~Waves3D() {}
+
+    /** 
+    @brief Initializes an action with duration, grid size, waves and amplitude.
+    @param duration Specify the duration of the Waves3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param waves Specify the waves count of the Waves3D action.
+    @param amplitude Specify the amplitude of the Waves3D action.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, unsigned int waves, float amplitude);
+
+protected:
+    unsigned int _waves;
+    float _amplitude;
+    float _amplitudeRate;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Waves3D);
+};
+
+/**
+@brief FlipX3D action.
+@details This action is used for flipping the target node on the x axis.
+*/
+class CC_DLL FlipX3D : public Grid3DAction
+{
+public:
+    /**
+    @brief Create the action with duration.
+    @param duration Specify the duration of the FilpX3D action. It's a value in seconds.
+    @return If the creation success, return a pointer of FilpX3D action; otherwise, return nil.
+    */
+    static FlipX3D* create(float duration);
+
+    // Override
+    virtual FlipX3D* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    FlipX3D() {}
+    virtual ~FlipX3D() {}
+
+    /** 
+    @brief Initializes an action with duration.
+    @param duration Specify the duration of the FlipX3D action. It's a value in seconds.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration);
+
+    /** 
+    @brief Initializes an action with duration and grid size.
+    @param gridSize Specify the grid size of the FlipX3D action.
+    @param duration Specify the duration of the FlipX3D action. It's a value in seconds.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    virtual bool initWithSize(const Size& gridSize, float duration);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FlipX3D);
+};
+
+/**
+@brief FlipY3D action.
+@details This action is used for flipping the target node on the y axis.
+*/
+class CC_DLL FlipY3D : public FlipX3D
+{
+public:
+    /**
+    @brief Create the action with duration.
+    @param duration Specify the duration of the FlipY3D action. It's a value in seconds.
+    @return If the creation success, return a pointer of FlipY3D action; otherwise, return nil.
+    */
+    static FlipY3D* create(float duration);
+
+    // Overrides
+    virtual void update(float time) override;
+    virtual FlipY3D* clone() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    FlipY3D() {}
+    virtual ~FlipY3D() {}
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FlipY3D);
+};
+
+/**
+@brief Lens3D action.
+@details This action is used for take effect on the target node as lens.
+        You can create the action by these parameters:
+        duration, grid size, center position of lens, radius of lens.
+        Also you can change the lens effect value & whether effect is concave by the setter methods.
+*/
+class CC_DLL Lens3D : public Grid3DAction
+{
+public:
+    /**
+    @brief Create the action with center position, radius, a grid size and duration.
+    @param duration Specify the duration of the Lens3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param position Specify the center position of the lens.
+    @param radius Specify the radius of the lens.
+    @return If the creation success, return a pointer of Lens3D action; otherwise, return nil.
+    */
+    static Lens3D* create(float duration, const Size& gridSize, const Vec2& position, float radius);
+
+    /**
+    @brief Get the value of lens effect. Default value is 0.7.
+    @return The value of lens effect.
+    */
+    float getLensEffect() const { return _lensEffect; }
+
+    /**
+    @brief Set the value of lens effect.
+    @param lensEffect The value of lens effect will be set.
+    */
+    void setLensEffect(float lensEffect) { _lensEffect = lensEffect; }
+
+    /**
+    @brief Set whether lens is concave.
+    @param concave Whether lens is concave.
+    */
+    void setConcave(bool concave) { _concave = concave; }
+
+    /**
+    @brief Get the center position of lens effect.
+    @return The center position of lens effect.
+    */
+    const Vec2& getPosition() const { return _position; }
+
+    /**
+    @brief Set the center position of lens effect.
+    @param position The center position will be set.
+    */
+    void setPosition(const Vec2& position);
+
+    // Overrides
+    virtual Lens3D* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Lens3D() {}
+    virtual ~Lens3D() {}
+
+    /**
+    @brief Initializes the action with center position, radius, grid size and duration.
+    @param duration Specify the duration of the Lens3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param position Specify the center position of the lens effect.
+    @param radius Specify the radius of the lens effect.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, const Vec2& position, float radius);
+
+protected:
+    /* lens center position */
+    Vec2 _position;
+    float _radius;
+    /** lens effect. Defaults to 0.7 - 0 means no effect, 1 is very strong effect */
+    float _lensEffect;
+    /** lens is concave. (true = concave, false = convex) default is convex i.e. false */
+    bool _concave;
+
+    bool _dirty;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Lens3D);
+};
+
+/**
+@brief Ripple3D action.
+@details This action is used for take effect on the target node as ripple.
+        You can create the action by these parameters:
+        duration, grid size, center position of ripple,
+        radius of ripple, waves count, amplitude.
+*/
+class CC_DLL Ripple3D : public Grid3DAction
+{
+public:
+    /**
+    @brief Create the action with center position, radius, number of waves, amplitude, a grid size and duration.
+    @param duration Specify the duration of the Ripple3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param position Specify the center position of the ripple effect.
+    @param radius Specify the radius of the ripple effect.
+    @param waves Specify the waves count of the ripple effect.
+    @param amplitude Specify the amplitude of the ripple effect.
+    @return If the creation success, return a pointer of Ripple3D action; otherwise, return nil.
+    */
+    static Ripple3D* create(float duration, const Size& gridSize, const Vec2& position, float radius, unsigned int waves, float amplitude);
+
+    /**
+    @brief Get the center position of ripple effect.
+    @return The center position of ripple effect.
+    */
+    const Vec2& getPosition() const { return _position; }
+    /**
+    @brief Set the center position of ripple effect.
+    @param position The center position of ripple effect will be set.
+    */
+    void setPosition(const Vec2& position);
+
+    /**
+    @brief Get the amplitude of ripple effect.
+    @return The amplitude of ripple effect.
+    */
+    float getAmplitude() const { return _amplitude; }
+    /**
+    @brief Set the amplitude of ripple effect.
+    @param fAmplitude The amplitude of ripple effect.
+    */
+    void setAmplitude(float fAmplitude) { _amplitude = fAmplitude; }
+
+    /**
+    @brief Get the amplitude rate of ripple effect.
+    @return The amplitude rate of ripple effect.
+    */
+    float getAmplitudeRate() const { return _amplitudeRate; }
+    /**
+    @brief Set the amplitude rate of ripple effect.
+    @param fAmplitudeRate The amplitude rate of ripple effect.
+    */
+    void setAmplitudeRate(float fAmplitudeRate) { _amplitudeRate = fAmplitudeRate; }
+
+    // Override
+    virtual Ripple3D* clone() const override;
+    virtual void update(float time) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    Ripple3D() {}
+    virtual ~Ripple3D() {}
+
+    /**
+    @brief Initializes the action with center position, radius, number of waves, amplitude, a grid size and duration.
+    @param duration Specify the duration of the Ripple3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param position Specify the center position of the ripple effect.
+    @param radius Specify the radius of the ripple effect.
+    @param waves Specify the waves count of the ripple effect.
+    @param amplitude Specify the amplitude of the ripple effect.
+    @return If the initialization success, return true; otherwise, return false.
+     */
+    bool initWithDuration(float duration, const Size& gridSize, const Vec2& position, float radius, unsigned int waves, float amplitude);
+
+protected:
+    /* center position */
+    Vec2 _position;
+    float _radius;
+    unsigned int _waves;
+    float _amplitude;
+    float _amplitudeRate;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Ripple3D);
+};
+
+/**
+@brief Shaky3D action.
+@details This action is used for take effect on the target node as shaky.
+        You can create the action by these parameters:
+        duration, grid size, range, whether shake on the z axis.
+*/
+class CC_DLL Shaky3D : public Grid3DAction
+{
+public:
+    /** 
+    @brief Create the action with a range, shake Z vertices, a grid and duration.
+    @param initWithDuration Specify the duration of the Shaky3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param range Specify the range of the shaky effect.
+    @param shakeZ Specify whether shake on the z axis.
+    @return If the creation success, return a pointer of Shaky3D action; otherwise, return nil.
+    */
+    static Shaky3D* create(float initWithDuration, const Size& gridSize, int range, bool shakeZ);
+
+    // Overrides
+    virtual Shaky3D* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Shaky3D() {}
+    virtual ~Shaky3D() {}
+    
+    /** 
+    @brief Initializes the action with a range, shake Z vertices, grid size and duration.
+    @param duration Specify the duration of the Shaky3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param range Specify the range of the shaky effect.
+    @param shakeZ Specify whether shake on the z axis.
+    @return If the Initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, int range, bool shakeZ);
+
+protected:
+    int _randrange;
+    bool _shakeZ;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Shaky3D);
+};
+
+/**
+@brief Liquid action.
+@details This action is used for take effect on the target node as liquid.
+        You can create the action by these parameters:
+        duration, grid size, waves count, amplitude of the liquid effect.
+*/
+class CC_DLL Liquid : public Grid3DAction
+{
+public:
+    /**
+    @brief Create the action with amplitude, grid size, waves count and duration.
+    @param duration Specify the duration of the Liquid action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param waves Specify the waves count of the Liquid action.
+    @param amplitude Specify the amplitude of the Liquid action.
+    @return If the creation success, return a pointer of Liquid action; otherwise, return nil.
+    */
+    static Liquid* create(float duration, const Size& gridSize, unsigned int waves, float amplitude);
+
+    /**
+    @brief Get the amplitude of the effect.
+    @return Return the amplitude of the effect.
+    */
+    float getAmplitude() const { return _amplitude; }
+    /**
+    @brief Set the amplitude to the effect.
+    @param amplitude The value of amplitude will be set.
+    */
+    void setAmplitude(float amplitude) { _amplitude = amplitude; }
+
+    /**
+    @brief Get the amplitude rate of the effect.
+    @return Return the amplitude rate of the effect.
+    */
+    float getAmplitudeRate() const { return _amplitudeRate; }
+    /**
+    @brief Set the amplitude rate of the effect.
+    @param amplitudeRate The value of amplitude rate will be set.
+    */
+    void setAmplitudeRate(float amplitudeRate) { _amplitudeRate = amplitudeRate; }
+
+    // Overrides
+    virtual Liquid* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Liquid() {}
+    virtual ~Liquid() {}
+    
+    /**
+    @brief Initializes the action with amplitude, grid size, waves count and duration.
+    @param duration Specify the duration of the Liquid action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param waves Specify the waves count of the Liquid action.
+    @param amplitude Specify the amplitude of the Liquid action.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, unsigned int waves, float amplitude);
+
+protected:
+    unsigned int _waves;
+    float _amplitude;
+    float _amplitudeRate;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Liquid);
+};
+
+/**
+@brief Waves action.
+@details This action is used for take effect on the target node as waves.
+        You can control the effect by these parameters:
+        duration, grid size, waves count, amplitude,
+        whether waves on horizontal and whether waves on vertical.
+*/
+class CC_DLL Waves : public Grid3DAction
+{
+public:
+    /**
+    @brief Create the action with amplitude, horizontal sin, vertical sin, grid size, waves count and duration.
+    @param duration Specify the duration of the Waves action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param waves Specify the waves count of the Waves action.
+    @param amplitude Specify the amplitude of the Waves action.
+    @param horizontal Specify whether waves on horizontal.
+    @param vertical Specify whether waves on vertical.
+    @return If the creation success, return a pointer of Waves action; otherwise, return nil.
+    */
+    static Waves* create(float duration, const Size& gridSize, unsigned int waves, float amplitude, bool horizontal, bool vertical);
+
+    /**
+    @brief Get the amplitude of the effect.
+    @return Return the amplitude of the effect.
+    */
+    float getAmplitude() const { return _amplitude; }
+    /**
+    @brief Set the amplitude to the effect.
+    @param amplitude The value of amplitude will be set.
+    */
+    void setAmplitude(float amplitude) { _amplitude = amplitude; }
+
+    /**
+    @brief Get the amplitude rate of the effect.
+    @return Return the amplitude rate of the effect.
+    */
+    float getAmplitudeRate() const { return _amplitudeRate; }
+    /**
+    @brief Set the amplitude rate of the effect.
+    @param amplitudeRate The value of amplitude rate will be set.
+    */
+    void setAmplitudeRate(float amplitudeRate) { _amplitudeRate = amplitudeRate; }
+
+    // Overrides
+    virtual Waves* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Waves() {}
+    virtual ~Waves() {}
+    
+    /**
+    @brief Initializes the action with amplitude, horizontal sin, vertical sin, grid size, waves count and duration.
+    @param duration Specify the duration of the Waves action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param waves Specify the waves count of the Waves action.
+    @param amplitude Specify the amplitude of the Waves action.
+    @param horizontal Specify whether waves on horizontal.
+    @param vertical Specify whether waves on vertical.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, unsigned int waves, float amplitude, bool horizontal, bool vertical);
+
+protected:
+    unsigned int _waves;
+    float _amplitude;
+    float _amplitudeRate;
+    bool _vertical;
+    bool _horizontal;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Waves);
+};
+
+/**
+@brief Twirl action.
+@details This action is used for take effect on the target node as twirl.
+        You can control the effect by these parameters:
+        duration, grid size, center position, twirls count, amplitude.
+*/
+class CC_DLL Twirl : public Grid3DAction
+{
+public:
+    /**
+    @brief Create the action with center position, number of twirls, amplitude, a grid size and duration.
+    @param duration Specify the duration of the Twirl action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param position Specify the center position of the twirl action.
+    @param twirls Specify the twirls count of the Twirl action.
+    @param amplitude Specify the amplitude of the Twirl action.
+    @return If the creation success, return a pointer of Twirl action; otherwise, return nil.
+    */
+    static Twirl* create(float duration, const Size& gridSize, const Vec2& position, unsigned int twirls, float amplitude);
+
+    /**
+    @brief Get the center position of twirl action.
+    @return The center position of twirl action.
+    */
+    const Vec2& getPosition() const { return _position; }
+    /**
+    @brief Set the center position of twirl action.
+    @param position The center position of twirl action will be set.
+    */
+    void setPosition(const Vec2& position);
+
+    /**
+    @brief Get the amplitude of the effect.
+    @return Return the amplitude of the effect.
+    */
+    float getAmplitude() const { return _amplitude; }
+    /**
+    @brief Set the amplitude to the effect.
+    @param amplitude The value of amplitude will be set.
+    */
+    void setAmplitude(float amplitude) { _amplitude = amplitude; }
+
+    /**
+    @brief Get the amplitude rate of the effect.
+    @return Return the amplitude rate of the effect.
+    */
+    float getAmplitudeRate() const { return _amplitudeRate; }
+    /**
+    @brief Set the amplitude rate of the effect.
+    @param amplitudeRate The value of amplitude rate will be set.
+    */
+    void setAmplitudeRate(float amplitudeRate) { _amplitudeRate = amplitudeRate; }
+
+
+    // Overrides
+    virtual Twirl* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Twirl() {}
+    virtual ~Twirl() {}
+
+    /**
+    @brief Initializes the action with center position, number of twirls, amplitude, a grid size and duration.
+    @param duration Specify the duration of the Twirl action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param position Specify the center position of the twirl action.
+    @param twirls Specify the twirls count of the Twirl action.
+    @param amplitude Specify the amplitude of the Twirl action.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, const Vec2& position, unsigned int twirls, float amplitude);
+
+protected:
+    /* twirl center */
+    Vec2 _position;
+    unsigned int _twirls;
+    float _amplitude;
+    float _amplitudeRate;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Twirl);
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif // __ACTION_CCGRID3D_ACTION_H__

+ 422 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionInstant.cpp

@@ -0,0 +1,422 @@
+/****************************************************************************
+ Copyright (c) 2008-2010 Ricardo Quesada
+ Copyright (c) 2010-2012 cocos2d-x.org
+ Copyright (c) 2011      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#include "2d/CCActionInstant.h"
+#include "2d/CCNode.h"
+#include "2d/CCSprite.h"
+
+NS_CC_BEGIN
+//
+// InstantAction
+//
+void ActionInstant::startWithTarget(Node *target)
+{
+    FiniteTimeAction::startWithTarget(target);
+    _done = false;
+}
+
+bool ActionInstant::isDone() const
+{
+    return _done;
+}
+
+void ActionInstant::step(float /*dt*/)
+{
+    float updateDt = 1;
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendActionEventToJS(this, kActionUpdate, (void *)&updateDt))
+            return;
+    }
+#endif
+    update(updateDt);
+}
+
+void ActionInstant::update(float /*time*/)
+{
+    _done = true;
+}
+
+//
+// Show
+//
+
+Show* Show::create() 
+{
+    Show* ret = new (std::nothrow) Show();
+
+    if (ret)
+    {
+        ret->autorelease();
+    }
+
+    return ret;
+}
+
+void Show::update(float time)
+{
+    ActionInstant::update(time);
+    _target->setVisible(true);
+}
+
+ActionInstant* Show::reverse() const
+{
+    return Hide::create();
+}
+
+Show* Show::clone() const
+{
+    // no copy constructor
+    return Show::create();
+}
+
+//
+// Hide
+//
+Hide * Hide::create() 
+{
+    Hide *ret = new (std::nothrow) Hide();
+
+    if (ret)
+    {
+        ret->autorelease();
+    }
+
+    return ret;
+}
+
+void Hide::update(float time)
+{
+    ActionInstant::update(time);
+    _target->setVisible(false);
+}
+
+ActionInstant *Hide::reverse() const
+{
+    return Show::create();
+}
+
+Hide* Hide::clone() const
+{
+    // no copy constructor
+    return Hide::create();
+}
+
+//
+// ToggleVisibility
+//
+ToggleVisibility * ToggleVisibility::create()
+{
+    ToggleVisibility *ret = new (std::nothrow) ToggleVisibility();
+
+    if (ret)
+    {
+        ret->autorelease();
+    }
+
+    return ret;
+}
+
+void ToggleVisibility::update(float time)
+{
+    ActionInstant::update(time);
+    _target->setVisible(!_target->isVisible());
+}
+
+ToggleVisibility * ToggleVisibility::reverse() const
+{
+    return ToggleVisibility::create();
+}
+
+ToggleVisibility * ToggleVisibility::clone() const
+{
+    // no copy constructor
+    return ToggleVisibility::create();
+}
+
+//
+// Remove Self
+//
+RemoveSelf * RemoveSelf::create(bool isNeedCleanUp /*= true*/) 
+{
+    RemoveSelf *ret = new (std::nothrow) RemoveSelf();
+
+    if (ret && ret->init(isNeedCleanUp))
+    {
+        ret->autorelease();
+    }
+
+    return ret;
+}
+
+bool RemoveSelf::init(bool isNeedCleanUp)
+{
+    _isNeedCleanUp = isNeedCleanUp;
+    return true;
+}
+
+void RemoveSelf::update(float time)
+{
+    ActionInstant::update(time);
+    _target->removeFromParentAndCleanup(_isNeedCleanUp);
+}
+
+RemoveSelf *RemoveSelf::reverse() const
+{
+    return RemoveSelf::create(_isNeedCleanUp);
+}
+
+RemoveSelf * RemoveSelf::clone() const
+{
+    // no copy constructor
+    return RemoveSelf::create(_isNeedCleanUp);
+}
+
+//
+// FlipX
+//
+
+FlipX *FlipX::create(bool x)
+{
+    FlipX *ret = new (std::nothrow) FlipX();
+
+    if (ret && ret->initWithFlipX(x))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool FlipX::initWithFlipX(bool x)
+{
+    _flipX = x;
+    return true;
+}
+
+void FlipX::update(float time)
+{
+    ActionInstant::update(time);
+    static_cast<Sprite*>(_target)->setFlippedX(_flipX);
+}
+
+FlipX* FlipX::reverse() const
+{
+    return FlipX::create(!_flipX);
+}
+
+FlipX * FlipX::clone() const
+{
+    // no copy constructor
+    return FlipX::create(_flipX);
+}
+//
+// FlipY
+//
+
+FlipY * FlipY::create(bool y)
+{
+    FlipY *ret = new (std::nothrow) FlipY();
+
+    if (ret && ret->initWithFlipY(y))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool FlipY::initWithFlipY(bool y)
+{
+    _flipY = y;
+    return true;
+}
+
+void FlipY::update(float time)
+{
+    ActionInstant::update(time);
+    static_cast<Sprite*>(_target)->setFlippedY(_flipY);
+}
+
+FlipY* FlipY::reverse() const
+{
+    return FlipY::create(!_flipY);
+}
+
+FlipY * FlipY::clone() const
+{
+    // no copy constructor
+    return FlipY::create(_flipY);
+}
+
+//
+// Place
+//
+
+Place* Place::create(const Vec2& pos)
+{
+    Place *ret = new (std::nothrow) Place();
+
+    if (ret && ret->initWithPosition(pos))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    delete ret;
+    return nullptr;
+}
+
+bool Place::initWithPosition(const Vec2& pos)
+{
+    _position = pos;
+    return true;
+}
+
+Place * Place::clone() const
+{
+    // no copy constructor
+    return Place::create(_position);
+}
+
+Place * Place::reverse() const
+{
+    // no reverse, just clone
+    return this->clone();
+}
+
+void Place::update(float time)
+{
+    ActionInstant::update(time);
+    _target->setPosition(_position);
+}
+
+//
+// CallFunc
+//
+
+CallFunc * CallFunc::create(const std::function<void()> &func)
+{
+    CallFunc *ret = new (std::nothrow) CallFunc();
+
+    if (ret && ret->initWithFunction(func) )
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool CallFunc::initWithFunction(const std::function<void()> &func)
+{
+    _function = func;
+    return true;
+}
+
+CallFunc * CallFunc::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) CallFunc();
+    if( _function )
+        a->initWithFunction(_function);
+
+    a->autorelease();
+    return a;
+}
+
+CallFunc * CallFunc::reverse() const
+{
+    // no reverse here, just return a clone
+    return this->clone();
+}
+
+void CallFunc::update(float time)
+{
+    ActionInstant::update(time);
+    this->execute();
+}
+
+void CallFunc::execute()
+{
+    if( _function )
+    {
+        _function();
+    }
+}
+
+//
+// CallFuncN
+//
+
+CallFuncN * CallFuncN::create(const std::function<void(Node*)> &func)
+{
+    auto ret = new (std::nothrow) CallFuncN();
+
+    if (ret && ret->initWithFunction(func) )
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+void CallFuncN::execute()
+{
+    if (_functionN)
+    {
+        _functionN(_target);
+    }
+}
+
+bool CallFuncN::initWithFunction(const std::function<void (Node *)> &func)
+{
+    _functionN = func;
+    return true;
+}
+
+CallFuncN * CallFuncN::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) CallFuncN();
+    if( _functionN)
+        a->initWithFunction(_functionN);
+
+    a->autorelease();
+    return a;
+}
+
+NS_CC_END

+ 412 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionInstant.h

@@ -0,0 +1,412 @@
+/****************************************************************************
+ Copyright (c) 2008-2010 Ricardo Quesada
+ Copyright (c) 2010-2012 cocos2d-x.org
+ Copyright (c) 2011      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef __CCINSTANT_ACTION_H__
+#define __CCINSTANT_ACTION_H__
+
+#include <functional>
+#include "2d/CCAction.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/** @class ActionInstant
+* @brief Instant actions are immediate actions. They don't have a duration like the IntervalAction actions.
+**/
+class CC_DLL ActionInstant : public FiniteTimeAction
+{
+public:
+    //
+    // Overrides
+    //
+    virtual ActionInstant* clone() const override
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+    
+    virtual ActionInstant * reverse() const override
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+
+    virtual void startWithTarget(Node *target) override;
+    
+    virtual bool isDone() const override;
+    /**
+     * @param dt In seconds.
+     */
+    virtual void step(float dt) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+
+private:
+    bool _done;
+};
+
+/** @class Show
+* @brief Show the node.
+**/
+class CC_DLL Show : public ActionInstant
+{
+public:
+    /** Allocates and initializes the action.
+     *
+     * @return  An autoreleased Show object.
+     */
+    static Show * create();
+
+    //
+    // Overrides
+    //
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual ActionInstant* reverse() const override;
+    virtual Show* clone() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    Show(){}
+    virtual ~Show(){}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Show);
+};
+
+/** @class Hide
+* @brief Hide the node.
+*/
+class CC_DLL Hide : public ActionInstant
+{
+public:
+    /** Allocates and initializes the action.
+     *
+     * @return An autoreleased Hide object.
+     */
+    static Hide * create();
+
+    //
+    // Overrides
+    //
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual ActionInstant* reverse() const override;
+    virtual Hide* clone() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    Hide(){}
+    virtual ~Hide(){}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Hide);
+};
+
+/** @class ToggleVisibility
+* @brief Toggles the visibility of a node.
+*/
+class CC_DLL ToggleVisibility : public ActionInstant
+{
+public:
+    /** Allocates and initializes the action.
+     *
+     * @return An autoreleased ToggleVisibility object.
+     */
+    static ToggleVisibility * create();
+
+    //
+    // Overrides
+    //
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual ToggleVisibility* reverse() const override;
+    virtual ToggleVisibility* clone() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    ToggleVisibility(){}
+    virtual ~ToggleVisibility(){}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ToggleVisibility);
+};
+
+/** @class RemoveSelf
+* @brief Remove the node.
+*/
+class CC_DLL RemoveSelf : public ActionInstant
+{
+public:
+    /** Create the action.
+     *
+     * @param isNeedCleanUp Is need to clean up, the default value is true.
+     * @return An autoreleased RemoveSelf object.
+     */
+    static RemoveSelf * create(bool isNeedCleanUp = true);
+
+    //
+    // Override
+    //
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual RemoveSelf* clone() const override;
+    virtual RemoveSelf* reverse() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    RemoveSelf() : _isNeedCleanUp(true){}
+    virtual ~RemoveSelf(){}
+
+    /** init the action */
+    bool init(bool isNeedCleanUp);
+
+protected:
+    bool _isNeedCleanUp;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(RemoveSelf);
+};
+
+/** @class FlipX
+* @brief Flips the sprite horizontally.
+* @since v0.99.0
+*/
+class CC_DLL FlipX : public ActionInstant
+{
+public:
+    /** Create the action.
+     *
+     * @param x Flips the sprite horizontally if true.
+     * @return  An autoreleased FlipX object.
+     */
+    static FlipX * create(bool x);
+
+    //
+    // Overrides
+    //
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual FlipX* reverse() const override;
+    virtual FlipX* clone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    FlipX() :_flipX(false) {}
+    virtual ~FlipX() {}
+
+    /** init the action */
+    bool initWithFlipX(bool x);
+
+protected:
+    bool    _flipX;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FlipX);
+};
+
+/** @class FlipY
+* @brief Flips the sprite vertically.
+* @since v0.99.0
+*/
+class CC_DLL FlipY : public ActionInstant
+{
+public:
+    /** Create the action.
+     *
+     * @param y Flips the sprite vertically if true.
+     * @return An autoreleased FlipY object.
+     */
+    static FlipY * create(bool y);
+
+    //
+    // Overrides
+    //
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual FlipY* reverse() const override;
+    virtual FlipY* clone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    FlipY() :_flipY(false) {}
+    virtual ~FlipY() {}
+
+    /** init the action */
+    bool initWithFlipY(bool y);
+
+protected:
+    bool    _flipY;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FlipY);
+};
+
+/** @class Place
+* @brief Places the node in a certain position.
+*/
+class CC_DLL Place : public ActionInstant
+{
+public:
+
+    /** Creates a Place action with a position.
+     *
+     * @param pos  A certain position.
+     * @return  An autoreleased Place object.
+     */
+    static Place * create(const Vec2& pos);
+
+    //
+    // Overrides
+    //
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual Place* reverse() const override;
+    virtual Place* clone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Place(){}
+    virtual ~Place(){}
+
+    /** Initializes a Place action with a position */
+    bool initWithPosition(const Vec2& pos);
+
+protected:
+    Vec2 _position;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Place);
+};
+
+
+/** @class CallFunc
+* @brief Calls a 'callback'.
+*/
+class CC_DLL CallFunc : public ActionInstant
+{
+public:
+    /** Creates the action with the callback of type std::function<void()>.
+     This is the preferred way to create the callback.
+     * When this function bound in js or lua ,the input param will be changed.
+     * In js: var create(var func, var this, var [data]) or var create(var func).
+     * In lua:local create(local funcID).
+     *
+     * @param func  A callback function need to be executed.
+     * @return  An autoreleased CallFunc object.
+     */
+    static CallFunc * create(const std::function<void()>& func);
+
+public:
+    /** Executes the callback.
+     */
+    virtual void execute();
+
+    //
+    // Overrides
+    //
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual CallFunc* reverse() const override;
+    virtual CallFunc* clone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    CallFunc() = default;
+    virtual ~CallFunc() = default;
+    
+    /** initializes the action with the std::function<void()>
+     * @lua NA
+     */
+    bool initWithFunction(const std::function<void()>& func);
+
+protected:
+    
+    /** function that will be called */
+    std::function<void()> _function;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(CallFunc);
+};
+
+/** @class CallFuncN
+* @brief Calls a 'callback' with the node as the first argument. N means Node.
+* @js NA
+*/
+class CC_DLL CallFuncN : public CallFunc
+{
+public:
+    /** Creates the action with the callback of type std::function<void()>.
+     This is the preferred way to create the callback.
+     *
+     * @param func  A callback function need to be executed.
+     * @return  An autoreleased CallFuncN object.
+     */
+    static CallFuncN * create(const std::function<void(Node*)>& func);
+
+    //
+    // Overrides
+    //
+    virtual CallFuncN* clone() const override;
+    virtual void execute() override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    CallFuncN():_functionN(nullptr){}
+    virtual ~CallFuncN(){}
+
+    /** initializes the action with the std::function<void(Node*)> */
+    bool initWithFunction(const std::function<void(Node*)>& func);
+
+protected:
+    /** function that will be called with the "sender" as the 1st argument */
+    std::function<void(Node*)> _functionN;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(CallFuncN);
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif //__CCINSTANT_ACTION_H__

+ 2820 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionInterval.cpp

@@ -0,0 +1,2820 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCActionInterval.h"
+
+#include <stdarg.h>
+
+#include "2d/CCSprite.h"
+#include "2d/CCNode.h"
+#include "2d/CCSpriteFrame.h"
+#include "2d/CCActionInstant.h"
+#include "base/CCDirector.h"
+#include "base/CCEventCustom.h"
+#include "base/CCEventDispatcher.h"
+#include "platform/CCStdC.h"
+#include "base/CCScriptSupport.h"
+
+NS_CC_BEGIN
+
+// Extra action for making a Sequence or Spawn when only adding one action to it.
+class ExtraAction : public FiniteTimeAction
+{
+public:
+    static ExtraAction* create();
+    virtual ExtraAction* clone() const;
+    virtual ExtraAction* reverse() const;
+    virtual void update(float time);
+    virtual void step(float dt);
+};
+
+ExtraAction* ExtraAction::create()
+{
+    ExtraAction* ret = new (std::nothrow) ExtraAction();
+    if (ret)
+    {
+        ret->autorelease();
+    }
+    return ret;
+}
+
+ExtraAction* ExtraAction::clone() const
+{
+    // no copy constructor
+    return ExtraAction::create();
+}
+
+ExtraAction* ExtraAction::reverse() const
+{
+    return ExtraAction::create();
+}
+
+void ExtraAction::update(float /*time*/)
+{
+}
+
+void ExtraAction::step(float /*dt*/)
+{
+}
+
+//
+// IntervalAction
+//
+
+bool ActionInterval::initWithDuration(float d)
+{
+    _duration = d;
+
+    _elapsed = 0;
+    _firstTick = true;
+    _done = false;
+    
+    return true;
+}
+
+bool ActionInterval::sendUpdateEventToScript(float dt, Action *actionObject)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendActionEventToJS(actionObject, kActionUpdate, (void *)&dt))
+            return true;
+    }
+#endif
+    return false;
+}
+
+bool ActionInterval::isDone() const
+{
+    return _done;
+}
+
+void ActionInterval::step(float dt)
+{
+    if (_firstTick)
+    {
+        _firstTick = false;
+        _elapsed = 0;
+    }
+    else
+    {
+        _elapsed += dt;
+    }
+    
+    
+    float updateDt = std::max(0.0f,                                  // needed for rewind. elapsed could be negative
+                              std::min(1.0f, _elapsed / _duration)
+                              );
+
+    if (sendUpdateEventToScript(updateDt, this)) return;
+    
+    this->update(updateDt);
+
+    _done = _elapsed >= _duration;
+}
+
+void ActionInterval::setAmplitudeRate(float /*amp*/)
+{
+    // Abstract class needs implementation
+    CCASSERT(0, "Subclass should implement this method!");
+}
+
+float ActionInterval::getAmplitudeRate()
+{
+    // Abstract class needs implementation
+    CCASSERT(0, "Subclass should implement this method!");
+
+    return 0;
+}
+
+void ActionInterval::startWithTarget(Node *target)
+{
+    FiniteTimeAction::startWithTarget(target);
+    _elapsed = 0.0f;
+    _firstTick = true;
+    _done = false;
+}
+
+//
+// Sequence
+//
+
+Sequence* Sequence::createWithTwoActions(FiniteTimeAction *actionOne, FiniteTimeAction *actionTwo)
+{
+    Sequence *sequence = new (std::nothrow) Sequence();
+    if (sequence && sequence->initWithTwoActions(actionOne, actionTwo))
+    {
+        sequence->autorelease();
+        return sequence;
+    }
+    
+    delete sequence;
+    return nullptr;
+}
+
+Sequence* Sequence::create(FiniteTimeAction *action1, ...)
+{
+    va_list params;
+    va_start(params, action1);
+
+    Sequence *ret = Sequence::createWithVariableList(action1, params);
+
+    va_end(params);
+    
+    return ret;
+}
+
+Sequence* Sequence::createWithVariableList(FiniteTimeAction *action1, va_list args)
+{
+    FiniteTimeAction *now;
+    FiniteTimeAction *prev = action1;
+    bool bOneAction = true;
+
+    while (action1)
+    {
+        now = va_arg(args, FiniteTimeAction*);
+        if (now)
+        {
+            prev = createWithTwoActions(prev, now);
+            bOneAction = false;
+        }
+        else
+        {
+            // If only one action is added to Sequence, make up a Sequence by adding a simplest finite time action.
+            if (bOneAction)
+            {
+                prev = createWithTwoActions(prev, ExtraAction::create());
+            }
+            break;
+        }
+    }
+    
+    return ((Sequence*)prev);
+}
+
+Sequence* Sequence::create(const Vector<FiniteTimeAction*>& arrayOfActions)
+{
+    Sequence* seq = new (std::nothrow) Sequence;
+    
+    if (seq && seq->init(arrayOfActions))
+    {
+        seq->autorelease();
+        return seq;
+    }
+    
+    delete seq;
+    return nullptr;
+}
+
+bool Sequence::init(const Vector<FiniteTimeAction*>& arrayOfActions)
+{
+    auto count = arrayOfActions.size();
+    if (count == 0)
+        return false;
+
+    if (count == 1)
+        return initWithTwoActions(arrayOfActions.at(0), ExtraAction::create());
+
+    // else size > 1
+    auto prev = arrayOfActions.at(0);
+    for (int i = 1; i < count-1; ++i)
+    {
+        prev = createWithTwoActions(prev, arrayOfActions.at(i));
+    }
+
+    return initWithTwoActions(prev, arrayOfActions.at(count-1));
+}
+
+bool Sequence::initWithTwoActions(FiniteTimeAction *actionOne, FiniteTimeAction *actionTwo)
+{
+    CCASSERT(actionOne != nullptr, "actionOne can't be nullptr!");
+    CCASSERT(actionTwo != nullptr, "actionTwo can't be nullptr!");
+    if (actionOne == nullptr || actionTwo == nullptr)
+    {
+        log("Sequence::initWithTwoActions error: action is nullptr!!");
+        return false;
+    }
+
+    float d = actionOne->getDuration() + actionTwo->getDuration();
+    ActionInterval::initWithDuration(d);
+
+    _actions[0] = actionOne;
+    actionOne->retain();
+
+    _actions[1] = actionTwo;
+    actionTwo->retain();
+
+    return true;
+}
+
+bool Sequence::isDone() const
+{
+    // fix issue #17884
+    if (dynamic_cast<ActionInstant*>(_actions[1]))
+        return (_done && _actions[1]->isDone());
+    else
+        return _done;
+}
+
+Sequence* Sequence::clone() const
+{
+    // no copy constructor
+    if (_actions[0] && _actions[1])
+    {
+        return Sequence::create(_actions[0]->clone(), _actions[1]->clone(), nullptr);
+    } else {
+        return nullptr;
+    }
+}
+
+Sequence::Sequence()
+: _split(0)
+{
+    _actions[0] = nullptr;
+    _actions[1] = nullptr;
+}
+
+Sequence::~Sequence()
+{
+    CC_SAFE_RELEASE(_actions[0]);
+    CC_SAFE_RELEASE(_actions[1]);
+}
+
+void Sequence::startWithTarget(Node *target)
+{
+    if (target == nullptr)
+    {
+        log("Sequence::startWithTarget error: target is nullptr!");
+        return;
+    }
+    if (_actions[0] == nullptr || _actions[1] == nullptr)
+    {
+        log("Sequence::startWithTarget error: _actions[0] or _actions[1] is nullptr!");
+        return;
+    }
+    if (_duration > FLT_EPSILON)
+        // fix #14936 - FLT_EPSILON (instant action) / very fast duration (0.001) leads to wrong split, that leads to call instant action few times
+        _split = _actions[0]->getDuration() > FLT_EPSILON ? _actions[0]->getDuration() / _duration : 0;
+    
+    ActionInterval::startWithTarget(target);
+    _last = -1;
+}
+
+void Sequence::stop()
+{
+    // Issue #1305
+    if( _last != - 1 && _actions[_last])
+    {
+        _actions[_last]->stop();
+    }
+
+    ActionInterval::stop();
+}
+
+void Sequence::update(float t)
+{
+    int found = 0;
+    float new_t = 0.0f;
+
+    if( t < _split )
+    {
+        // action[0]
+        found = 0;
+        if( _split != 0 )
+            new_t = t / _split;
+        else
+            new_t = 1;
+
+    }
+    else
+    {
+        // action[1]
+        found = 1;
+        if ( _split == 1 )
+            new_t = 1;
+        else
+            new_t = (t-_split) / (1 - _split );
+    }
+
+    if ( found==1 )
+    {
+        if( _last == -1 )
+        {
+            // action[0] was skipped, execute it.
+            _actions[0]->startWithTarget(_target);
+            if (!(sendUpdateEventToScript(1.0f, _actions[0])))
+                _actions[0]->update(1.0f);
+            _actions[0]->stop();
+        }
+        else if( _last == 0 )
+        {
+            // switching to action 1. stop action 0.
+            if (!(sendUpdateEventToScript(1.0f, _actions[0])))
+                _actions[0]->update(1.0f);
+            _actions[0]->stop();
+        }
+    }
+    else if(found==0 && _last==1 )
+    {
+        // Reverse mode ?
+        // FIXME: Bug. this case doesn't contemplate when _last==-1, found=0 and in "reverse mode"
+        // since it will require a hack to know if an action is on reverse mode or not.
+        // "step" should be overridden, and the "reverseMode" value propagated to inner Sequences.
+        if (!(sendUpdateEventToScript(0, _actions[1])))
+            _actions[1]->update(0);
+        _actions[1]->stop();
+    }
+    // Last action found and it is done.
+    if( found == _last && _actions[found]->isDone() )
+    {
+        return;
+    }
+
+    // Last action found and it is done
+    if( found != _last )
+    {
+        _actions[found]->startWithTarget(_target);
+    }
+    if (!(sendUpdateEventToScript(new_t, _actions[found])))
+        _actions[found]->update(new_t);
+    _last = found;
+}
+
+Sequence* Sequence::reverse() const
+{
+    if (_actions[0] && _actions[1])
+        return Sequence::createWithTwoActions(_actions[1]->reverse(), _actions[0]->reverse());
+    else
+        return nullptr;
+}
+
+//
+// Repeat
+//
+
+Repeat* Repeat::create(FiniteTimeAction *action, unsigned int times)
+{
+    Repeat* repeat = new (std::nothrow) Repeat();
+    if (repeat && repeat->initWithAction(action, times))
+    {
+        repeat->autorelease();
+        return repeat;
+    }
+
+    delete repeat;
+    return nullptr;
+}
+
+bool Repeat::initWithAction(FiniteTimeAction *action, unsigned int times)
+{
+    if (action && ActionInterval::initWithDuration(action->getDuration() * times))
+    {
+        _times = times;
+        _innerAction = action;
+        action->retain();
+
+        _actionInstant = dynamic_cast<ActionInstant*>(action) ? true : false;
+        //an instant action needs to be executed one time less in the update method since it uses startWithTarget to execute the action
+        // minggo: instant action doesn't execute action in Repeat::startWithTarget(), so comment it.
+//        if (_actionInstant) 
+//        {
+//            _times -=1;
+//        }
+        _total = 0;
+
+        return true;
+    }
+
+    return false;
+}
+
+Repeat* Repeat::clone() const
+{
+    // no copy constructor
+    return Repeat::create(_innerAction->clone(), _times);
+}
+
+Repeat::~Repeat()
+{
+    CC_SAFE_RELEASE(_innerAction);
+}
+
+void Repeat::startWithTarget(Node *target)
+{
+    _total = 0;
+    _nextDt = _innerAction->getDuration()/_duration;
+    ActionInterval::startWithTarget(target);
+    _innerAction->startWithTarget(target);
+}
+
+void Repeat::stop()
+{
+    _innerAction->stop();
+    ActionInterval::stop();
+}
+
+// issue #80. Instead of hooking step:, hook update: since it can be called by any 
+// container action like Repeat, Sequence, Ease, etc..
+void Repeat::update(float dt)
+{
+    if (dt >= _nextDt)
+    {
+        while (dt >= _nextDt && _total < _times)
+        {
+            if (!(sendUpdateEventToScript(1.0f, _innerAction)))
+                _innerAction->update(1.0f);
+            _total++;
+
+            _innerAction->stop();
+            _innerAction->startWithTarget(_target);
+            _nextDt = _innerAction->getDuration()/_duration * (_total+1);
+        }
+
+        // fix for issue #1288, incorrect end value of repeat
+        if (std::abs(dt - 1.0f) < FLT_EPSILON && _total < _times)
+        {
+            if (!(sendUpdateEventToScript(1.0f, _innerAction)))
+                _innerAction->update(1.0f);
+            
+            _total++;
+        }
+
+        // don't set an instant action back or update it, it has no use because it has no duration
+        if (!_actionInstant)
+        {
+            if (_total == _times)
+            {
+                // minggo: inner action update is invoked above, don't have to invoke it here
+//                if (!(sendUpdateEventToScript(1, _innerAction)))
+//                    _innerAction->update(1);
+                _innerAction->stop();
+            }
+            else
+            {
+                // issue #390 prevent jerk, use right update
+                if (!(sendUpdateEventToScript(dt - (_nextDt - _innerAction->getDuration()/_duration), _innerAction)))
+                    _innerAction->update(dt - (_nextDt - _innerAction->getDuration()/_duration));
+            }
+        }
+    }
+    else
+    {
+        if (!(sendUpdateEventToScript(fmodf(dt * _times,1.0f), _innerAction)))
+            _innerAction->update(fmodf(dt * _times,1.0f));
+    }
+}
+
+bool Repeat::isDone() const
+{
+    return _total == _times;
+}
+
+Repeat* Repeat::reverse() const
+{
+    return Repeat::create(_innerAction->reverse(), _times);
+}
+
+//
+// RepeatForever
+//
+RepeatForever::~RepeatForever()
+{
+    CC_SAFE_RELEASE(_innerAction);
+}
+
+RepeatForever *RepeatForever::create(ActionInterval *action)
+{
+    RepeatForever *ret = new (std::nothrow) RepeatForever();
+    if (ret && ret->initWithAction(action))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    
+    delete ret;
+    return nullptr;
+}
+
+bool RepeatForever::initWithAction(ActionInterval *action)
+{
+    CCASSERT(action != nullptr, "action can't be nullptr!");
+    if (action == nullptr)
+    {
+        log("RepeatForever::initWithAction error:action is nullptr!");
+        return false;
+    }
+    
+    action->retain();
+    _innerAction = action;
+    
+    return true;
+}
+
+RepeatForever *RepeatForever::clone() const
+{
+    // no copy constructor
+    return RepeatForever::create(_innerAction->clone());
+}
+
+void RepeatForever::startWithTarget(Node* target)
+{
+    ActionInterval::startWithTarget(target);
+    _innerAction->startWithTarget(target);
+}
+
+void RepeatForever::step(float dt)
+{
+    _innerAction->step(dt);
+    // only action interval should prevent jerk, issue #17808
+    if (_innerAction->isDone() && _innerAction->getDuration() > 0)
+    {
+        float diff = _innerAction->getElapsed() - _innerAction->getDuration();
+        if (diff > _innerAction->getDuration())
+            diff = fmodf(diff, _innerAction->getDuration());
+        _innerAction->startWithTarget(_target);
+        // to prevent jerk. cocos2d-iphone issue #390, 1247
+        _innerAction->step(0.0f);
+        _innerAction->step(diff);
+    }
+}
+
+bool RepeatForever::isDone() const
+{
+    return false;
+}
+
+RepeatForever *RepeatForever::reverse() const
+{
+    return RepeatForever::create(_innerAction->reverse());
+}
+
+//
+// Spawn
+//
+
+Spawn* Spawn::create(FiniteTimeAction *action1, ...)
+{
+    va_list params;
+    va_start(params, action1);
+
+    Spawn *ret = Spawn::createWithVariableList(action1, params);
+
+    va_end(params);
+    
+    return ret;
+}
+
+Spawn* Spawn::createWithVariableList(FiniteTimeAction *action1, va_list args)
+{
+    FiniteTimeAction *now;
+    FiniteTimeAction *prev = action1;
+    bool oneAction = true;
+
+    while (action1)
+    {
+        now = va_arg(args, FiniteTimeAction*);
+        if (now)
+        {
+            prev = createWithTwoActions(prev, now);
+            oneAction = false;
+        }
+        else
+        {
+            // If only one action is added to Spawn, make up a Spawn by adding a simplest finite time action.
+            if (oneAction)
+            {
+                prev = createWithTwoActions(prev, ExtraAction::create());
+            }
+            break;
+        }
+    }
+
+    return ((Spawn*)prev);
+}
+
+Spawn* Spawn::create(const Vector<FiniteTimeAction*>& arrayOfActions)
+{
+    Spawn* ret = new (std::nothrow) Spawn;
+    
+    if (ret && ret->init(arrayOfActions))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    
+    delete ret;
+    return nullptr;
+}
+
+Spawn* Spawn::createWithTwoActions(FiniteTimeAction *action1, FiniteTimeAction *action2)
+{
+    Spawn *spawn = new (std::nothrow) Spawn();
+    if (spawn && spawn->initWithTwoActions(action1, action2))
+    {
+        spawn->autorelease();
+        return spawn;
+    }
+    
+    delete spawn;
+    return nullptr;
+}
+
+bool Spawn::init(const Vector<FiniteTimeAction*>& arrayOfActions)
+{
+    auto count = arrayOfActions.size();
+    
+    if (count == 0)
+        return false;
+    
+    if (count == 1)
+        return initWithTwoActions(arrayOfActions.at(0), ExtraAction::create());
+    
+    // else count > 1
+    auto prev = arrayOfActions.at(0);
+    for (int i = 1; i < count-1; ++i)
+    {
+        prev = createWithTwoActions(prev, arrayOfActions.at(i));
+    }
+    
+    return initWithTwoActions(prev, arrayOfActions.at(count-1));
+}
+
+bool Spawn::initWithTwoActions(FiniteTimeAction *action1, FiniteTimeAction *action2)
+{
+    CCASSERT(action1 != nullptr, "action1 can't be nullptr!");
+    CCASSERT(action2 != nullptr, "action2 can't be nullptr!");
+    if (action1 == nullptr || action2 == nullptr)
+    {
+        log("Spawn::initWithTwoActions error: action is nullptr!");
+        return false;
+    }
+
+    bool ret = false;
+
+    float d1 = action1->getDuration();
+    float d2 = action2->getDuration();
+
+    if (ActionInterval::initWithDuration(MAX(d1, d2)))
+    {
+        _one = action1;
+        _two = action2;
+
+        if (d1 > d2)
+        {
+            _two = Sequence::createWithTwoActions(action2, DelayTime::create(d1 - d2));
+        } 
+        else if (d1 < d2)
+        {
+            _one = Sequence::createWithTwoActions(action1, DelayTime::create(d2 - d1));
+        }
+
+        _one->retain();
+        _two->retain();
+
+        ret = true;
+    }
+
+    return ret;
+}
+
+Spawn* Spawn::clone() const
+{
+    // no copy constructor
+    if (_one && _two)
+        return Spawn::createWithTwoActions(_one->clone(), _two->clone());
+    else
+        return nullptr;
+}
+
+Spawn::Spawn()
+: _one(nullptr)
+, _two(nullptr)
+{
+    
+}
+
+Spawn::~Spawn()
+{
+    CC_SAFE_RELEASE(_one);
+    CC_SAFE_RELEASE(_two);
+}
+
+void Spawn::startWithTarget(Node *target)
+{
+    if (target == nullptr)
+    {
+        log("Spawn::startWithTarget error: target is nullptr!");
+        return;
+    }
+    if (_one == nullptr || _two == nullptr)
+    {
+        log("Spawn::startWithTarget error: _one or _two is nullptr!");
+        return;
+    }
+    
+    ActionInterval::startWithTarget(target);
+    _one->startWithTarget(target);
+    _two->startWithTarget(target);
+}
+
+void Spawn::stop()
+{
+    if (_one)
+        _one->stop();
+
+    if (_two)
+        _two->stop();
+
+    ActionInterval::stop();
+}
+
+void Spawn::update(float time)
+{
+    if (_one)
+    {
+        if (!(sendUpdateEventToScript(time, _one)))
+            _one->update(time);
+    }
+    if (_two)
+    {
+        if (!(sendUpdateEventToScript(time, _two)))
+            _two->update(time);
+    }
+}
+
+Spawn* Spawn::reverse() const
+{
+    if (_one && _two)
+        return Spawn::createWithTwoActions(_one->reverse(), _two->reverse());
+    
+    return nullptr;
+}
+
+//
+// RotateTo
+//
+
+RotateTo* RotateTo::create(float duration, float dstAngle)
+{
+    RotateTo* rotateTo = new (std::nothrow) RotateTo();
+    if (rotateTo && rotateTo->initWithDuration(duration, dstAngle, dstAngle))
+    {
+        rotateTo->autorelease();
+        return rotateTo;
+    }
+    
+    delete rotateTo;
+    return nullptr;
+}
+
+RotateTo* RotateTo::create(float duration, float dstAngleX, float dstAngleY)
+{
+    RotateTo* rotateTo = new (std::nothrow) RotateTo();
+    if (rotateTo && rotateTo->initWithDuration(duration, dstAngleX, dstAngleY))
+    {
+        rotateTo->autorelease();
+        return rotateTo;
+    }
+    
+    delete rotateTo;
+    return nullptr;
+}
+
+RotateTo* RotateTo::create(float duration, const Vec3& dstAngle3D)
+{
+    RotateTo* rotateTo = new (std::nothrow) RotateTo();
+    if(rotateTo && rotateTo->initWithDuration(duration, dstAngle3D))
+    {
+        rotateTo->autorelease();
+        return rotateTo;
+    }
+
+    delete rotateTo;
+    return nullptr;
+}
+
+RotateTo::RotateTo()
+: _is3D(false)
+{
+}
+
+bool RotateTo::initWithDuration(float duration, float dstAngleX, float dstAngleY)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _dstAngle.x = dstAngleX;
+        _dstAngle.y = dstAngleY;
+        
+        return true;
+    }
+    
+    return false;
+}
+
+bool RotateTo::initWithDuration(float duration, const Vec3& dstAngle3D)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _dstAngle = dstAngle3D;
+        _is3D = true;
+        
+        return true;
+    }
+    
+    return false;
+}
+
+RotateTo* RotateTo::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) RotateTo();
+    if(_is3D)
+       a->initWithDuration(_duration, _dstAngle);
+    else
+        a->initWithDuration(_duration, _dstAngle.x, _dstAngle.y);
+    a->autorelease();
+    return a;
+}
+
+void RotateTo::calculateAngles(float &startAngle, float &diffAngle, float dstAngle)
+{
+    if (startAngle > 0)
+    {
+        startAngle = fmodf(startAngle, 360.0f);
+    }
+    else
+    {
+        startAngle = fmodf(startAngle, -360.0f);
+    }
+
+    diffAngle = dstAngle - startAngle;
+    if (diffAngle > 180)
+    {
+        diffAngle -= 360;
+    }
+    if (diffAngle < -180)
+    {
+        diffAngle += 360;
+    }
+}
+
+void RotateTo::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    
+    if (_is3D)
+    {
+        _startAngle = _target->getRotation3D();
+    }
+    else
+    {
+        _startAngle.x = _target->getRotationSkewX();
+        _startAngle.y = _target->getRotationSkewY();
+    }
+
+    calculateAngles(_startAngle.x, _diffAngle.x, _dstAngle.x);
+    calculateAngles(_startAngle.y, _diffAngle.y, _dstAngle.y);
+    calculateAngles(_startAngle.z, _diffAngle.z, _dstAngle.z);
+}
+
+void RotateTo::update(float time)
+{
+    if (_target)
+    {
+        if(_is3D)
+        {
+            _target->setRotation3D(Vec3(
+                _startAngle.x + _diffAngle.x * time,
+                _startAngle.y + _diffAngle.y * time,
+                _startAngle.z + _diffAngle.z * time
+            ));
+        }
+        else
+        {
+#if CC_USE_PHYSICS
+            if (_startAngle.x == _startAngle.y && _diffAngle.x == _diffAngle.y)
+            {
+                _target->setRotation(_startAngle.x + _diffAngle.x * time);
+            }
+            else
+            {
+                _target->setRotationSkewX(_startAngle.x + _diffAngle.x * time);
+                _target->setRotationSkewY(_startAngle.y + _diffAngle.y * time);
+            }
+#else
+            _target->setRotationSkewX(_startAngle.x + _diffAngle.x * time);
+            _target->setRotationSkewY(_startAngle.y + _diffAngle.y * time);
+#endif // CC_USE_PHYSICS
+        }
+    }
+}
+
+RotateTo *RotateTo::reverse() const
+{
+    CCASSERT(false, "RotateTo doesn't support the 'reverse' method");
+    return nullptr;
+}
+
+//
+// RotateBy
+//
+
+RotateBy* RotateBy::create(float duration, float deltaAngle)
+{
+    RotateBy *rotateBy = new (std::nothrow) RotateBy();
+    if (rotateBy && rotateBy->initWithDuration(duration, deltaAngle))
+    {
+        rotateBy->autorelease();
+        return rotateBy;
+    }
+    
+    delete rotateBy;
+    return nullptr;
+}
+
+RotateBy* RotateBy::create(float duration, float deltaAngleX, float deltaAngleY)
+{
+    RotateBy *rotateBy = new (std::nothrow) RotateBy();
+    if (rotateBy && rotateBy->initWithDuration(duration, deltaAngleX, deltaAngleY))
+    {
+        rotateBy->autorelease();
+        return rotateBy;
+    }
+
+    delete rotateBy;
+    return nullptr;
+}
+
+RotateBy* RotateBy::create(float duration, const Vec3& deltaAngle3D)
+{
+    RotateBy *rotateBy = new (std::nothrow) RotateBy();
+    if(rotateBy && rotateBy->initWithDuration(duration, deltaAngle3D))
+    {
+        rotateBy->autorelease();
+        return rotateBy;
+    }
+
+    delete rotateBy;
+    return nullptr;
+}
+
+RotateBy::RotateBy()
+: _is3D(false)
+{
+}
+
+bool RotateBy::initWithDuration(float duration, float deltaAngle)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _deltaAngle.x = _deltaAngle.y = deltaAngle;
+        return true;
+    }
+
+    return false;
+}
+
+bool RotateBy::initWithDuration(float duration, float deltaAngleX, float deltaAngleY)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _deltaAngle.x = deltaAngleX;
+        _deltaAngle.y = deltaAngleY;
+        return true;
+    }
+    
+    return false;
+}
+
+bool RotateBy::initWithDuration(float duration, const Vec3& deltaAngle3D)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _deltaAngle = deltaAngle3D;
+        _is3D = true;
+        return true;
+    }
+
+    return false;
+}
+
+
+RotateBy* RotateBy::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) RotateBy();
+    if(_is3D)
+        a->initWithDuration(_duration, _deltaAngle);
+    else
+        a->initWithDuration(_duration, _deltaAngle.x, _deltaAngle.y);
+    a->autorelease();
+    return a;
+}
+
+void RotateBy::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    if(_is3D)
+    {
+        _startAngle = target->getRotation3D();
+    }
+    else
+    {
+        _startAngle.x = target->getRotationSkewX();
+        _startAngle.y = target->getRotationSkewY();
+    }
+}
+
+void RotateBy::update(float time)
+{
+    // FIXME: shall I add % 360
+    if (_target)
+    {
+        if(_is3D)
+        {
+            Vec3 v;
+            v.x = _startAngle.x + _deltaAngle.x * time;
+            v.y = _startAngle.y + _deltaAngle.y * time;
+            v.z = _startAngle.z + _deltaAngle.z * time;
+            _target->setRotation3D(v);
+        }
+        else
+        {
+#if CC_USE_PHYSICS
+            if (_startAngle.x == _startAngle.y && _deltaAngle.x == _deltaAngle.y)
+            {
+                _target->setRotation(_startAngle.x + _deltaAngle.x * time);
+            }
+            else
+            {
+                _target->setRotationSkewX(_startAngle.x + _deltaAngle.x * time);
+                _target->setRotationSkewY(_startAngle.y + _deltaAngle.y * time);
+            }
+#else
+            _target->setRotationSkewX(_startAngle.x + _deltaAngle.x * time);
+            _target->setRotationSkewY(_startAngle.y + _deltaAngle.y * time);
+#endif // CC_USE_PHYSICS
+        }
+    }
+}
+
+RotateBy* RotateBy::reverse() const
+{
+    if(_is3D)
+    {
+        Vec3 v;
+        v.x = - _deltaAngle.x;
+        v.y = - _deltaAngle.y;
+        v.z = - _deltaAngle.z;
+        return RotateBy::create(_duration, v);
+    }
+    else
+    {
+        return RotateBy::create(_duration, -_deltaAngle.x, -_deltaAngle.y);
+    }
+}
+
+//
+// MoveBy
+//
+
+MoveBy* MoveBy::create(float duration, const Vec2& deltaPosition)
+{
+    return MoveBy::create(duration, Vec3(deltaPosition.x, deltaPosition.y, 0));
+}
+
+MoveBy* MoveBy::create(float duration, const Vec3 &deltaPosition)
+{
+    MoveBy *ret = new (std::nothrow) MoveBy();
+    
+    if (ret && ret->initWithDuration(duration, deltaPosition))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    
+    delete ret;
+    return nullptr;
+}
+
+bool MoveBy::initWithDuration(float duration, const Vec2& deltaPosition)
+{
+    return MoveBy::initWithDuration(duration, Vec3(deltaPosition.x, deltaPosition.y, 0));
+}
+
+bool MoveBy::initWithDuration(float duration, const Vec3& deltaPosition)
+{
+    bool ret = false;
+    
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _positionDelta = deltaPosition;
+        _is3D = true;
+        ret = true;
+    }
+    
+    return ret;
+}
+
+MoveBy* MoveBy::clone() const
+{
+    // no copy constructor
+    return MoveBy::create(_duration, _positionDelta);
+}
+
+void MoveBy::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _previousPosition = _startPosition = target->getPosition3D();
+}
+
+MoveBy* MoveBy::reverse() const
+{
+    return MoveBy::create(_duration, -_positionDelta);
+}
+
+void MoveBy::update(float t)
+{
+    if (_target)
+    {
+#if CC_ENABLE_STACKABLE_ACTIONS
+        Vec3 currentPos = _target->getPosition3D();
+        Vec3 diff = currentPos - _previousPosition;
+        _startPosition = _startPosition + diff;
+        Vec3 newPos =  _startPosition + (_positionDelta * t);
+        _target->setPosition3D(newPos);
+        _previousPosition = newPos;
+#else
+        _target->setPosition3D(_startPosition + _positionDelta * t);
+#endif // CC_ENABLE_STACKABLE_ACTIONS
+    }
+}
+
+//
+// MoveTo
+//
+
+MoveTo* MoveTo::create(float duration, const Vec2& position)
+{
+    return MoveTo::create(duration, Vec3(position.x, position.y, 0));
+}
+
+MoveTo* MoveTo::create(float duration, const Vec3& position)
+{
+    MoveTo *ret = new (std::nothrow) MoveTo();
+    
+    if (ret && ret->initWithDuration(duration, position))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    
+    delete ret;
+    return nullptr;
+}
+
+bool MoveTo::initWithDuration(float duration, const Vec2& position)
+{
+    return initWithDuration(duration, Vec3(position.x, position.y, 0));
+}
+
+bool MoveTo::initWithDuration(float duration, const Vec3& position)
+{
+    bool ret = false;
+    
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _endPosition = position;
+        ret = true;
+    }
+    
+    return ret;
+}
+
+MoveTo* MoveTo::clone() const
+{
+    // no copy constructor
+    return MoveTo::create(_duration, _endPosition);
+}
+
+void MoveTo::startWithTarget(Node *target)
+{
+    MoveBy::startWithTarget(target);
+    _positionDelta = _endPosition - target->getPosition3D();
+}
+
+MoveTo* MoveTo::reverse() const
+{
+    CCASSERT(false, "reverse() not supported in MoveTo");
+    return nullptr;
+}
+
+//
+// SkewTo
+//
+SkewTo* SkewTo::create(float t, float sx, float sy)
+{
+    SkewTo *skewTo = new (std::nothrow) SkewTo();
+    if (skewTo && skewTo->initWithDuration(t, sx, sy))
+    {
+        skewTo->autorelease();
+        return skewTo;
+    }
+
+    delete skewTo;
+    return nullptr;
+}
+
+bool SkewTo::initWithDuration(float t, float sx, float sy)
+{
+    bool bRet = false;
+
+    if (ActionInterval::initWithDuration(t))
+    {
+        _endSkewX = sx;
+        _endSkewY = sy;
+
+        bRet = true;
+    }
+
+    return bRet;
+}
+
+SkewTo* SkewTo::clone() const
+{
+    // no copy constructor
+    return SkewTo::create(_duration, _endSkewX, _endSkewY);
+}
+
+SkewTo* SkewTo::reverse() const
+{
+    CCASSERT(false, "reverse() not supported in SkewTo");
+    return nullptr;
+}
+
+void SkewTo::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+
+    _startSkewX = target->getSkewX();
+
+    if (_startSkewX > 0)
+    {
+        _startSkewX = fmodf(_startSkewX, 180.f);
+    }
+    else
+    {
+        _startSkewX = fmodf(_startSkewX, -180.f);
+    }
+
+    _deltaX = _endSkewX - _startSkewX;
+
+    if (_deltaX > 180)
+    {
+        _deltaX -= 360;
+    }
+    if (_deltaX < -180)
+    {
+        _deltaX += 360;
+    }
+
+    _startSkewY = target->getSkewY();
+
+    if (_startSkewY > 0)
+    {
+        _startSkewY = fmodf(_startSkewY, 360.f);
+    }
+    else
+    {
+        _startSkewY = fmodf(_startSkewY, -360.f);
+    }
+
+    _deltaY = _endSkewY - _startSkewY;
+
+    if (_deltaY > 180)
+    {
+        _deltaY -= 360;
+    }
+    if (_deltaY < -180)
+    {
+        _deltaY += 360;
+    }
+}
+
+void SkewTo::update(float t)
+{
+    _target->setSkewX(_startSkewX + _deltaX * t);
+    _target->setSkewY(_startSkewY + _deltaY * t);
+}
+
+SkewTo::SkewTo()
+: _skewX(0.0)
+, _skewY(0.0)
+, _startSkewX(0.0)
+, _startSkewY(0.0)
+, _endSkewX(0.0)
+, _endSkewY(0.0)
+, _deltaX(0.0)
+, _deltaY(0.0)
+{
+}
+
+//
+// SkewBy
+//
+SkewBy* SkewBy::create(float t, float sx, float sy)
+{
+    SkewBy *skewBy = new (std::nothrow) SkewBy();
+    if (skewBy && skewBy->initWithDuration(t, sx, sy))
+    {
+        skewBy->autorelease();
+        return  skewBy;
+    }
+
+    delete skewBy;
+    return nullptr;
+}
+
+SkewBy * SkewBy::clone() const
+{
+    // no copy constructor
+    return SkewBy::create(_duration, _skewX, _skewY);
+}
+
+bool SkewBy::initWithDuration(float t, float deltaSkewX, float deltaSkewY)
+{
+    bool ret = false;
+
+    if (SkewTo::initWithDuration(t, deltaSkewX, deltaSkewY))
+    {
+        _skewX = deltaSkewX;
+        _skewY = deltaSkewY;
+
+        ret = true;
+    }
+
+    return ret;
+}
+
+void SkewBy::startWithTarget(Node *target)
+{
+    SkewTo::startWithTarget(target);
+    _deltaX = _skewX;
+    _deltaY = _skewY;
+    _endSkewX = _startSkewX + _deltaX;
+    _endSkewY = _startSkewY + _deltaY;
+}
+
+SkewBy* SkewBy::reverse() const
+{
+    return SkewBy::create(_duration, -_skewX, -_skewY);
+}
+
+ResizeTo* ResizeTo::create(float duration, const cocos2d::Size& final_size)
+{
+    ResizeTo *ret = new (std::nothrow) ResizeTo();
+    
+    if (ret)
+    {
+        if (ret->initWithDuration(duration, final_size))
+        {
+            ret->autorelease();
+        } 
+        else
+        {
+            delete ret;
+            ret = nullptr;
+        }
+    }
+    
+    return ret;
+}
+
+ResizeTo* ResizeTo::clone() const
+{
+    // no copy constructor
+    ResizeTo* a = new (std::nothrow) ResizeTo();
+    a->initWithDuration(_duration, _finalSize);
+    a->autorelease();
+
+    return a;
+}
+
+void ResizeTo::startWithTarget(cocos2d::Node* target)
+{
+    ActionInterval::startWithTarget(target);
+    _initialSize = target->getContentSize();
+    _sizeDelta = _finalSize - _initialSize;
+}
+
+void ResizeTo::update(float time)
+{
+    if (_target)
+    {
+        auto new_size = _initialSize + (_sizeDelta * time);
+        _target->setContentSize(new_size);
+    }
+}
+
+bool ResizeTo::initWithDuration(float duration, const cocos2d::Size& final_size)
+{
+    if (cocos2d::ActionInterval::initWithDuration(duration))
+    {
+        _finalSize = final_size;
+        return true;
+    }
+
+    return false;
+}
+
+//
+// ResizeBy
+//
+
+ResizeBy* ResizeBy::create(float duration, const cocos2d::Size& deltaSize)
+{
+    ResizeBy *ret = new (std::nothrow) ResizeBy();
+    
+    if (ret)
+    {
+        if (ret->initWithDuration(duration, deltaSize))
+        {
+            ret->autorelease();
+        } 
+        else
+        {
+              delete ret;
+              ret = nullptr;
+        }
+    }
+    
+    return ret;
+}
+
+ResizeBy* ResizeBy::clone() const
+{
+    // no copy constructor
+    auto a = new (std::nothrow) ResizeBy();
+    a->initWithDuration(_duration, _sizeDelta);
+    a->autorelease();
+    return a;
+}
+
+void ResizeBy::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _previousSize = _startSize = target->getContentSize();
+}
+
+ResizeBy* ResizeBy::reverse() const
+{
+    cocos2d::Size newSize(-_sizeDelta.width, -_sizeDelta.height);
+    return ResizeBy::create(_duration, newSize);
+}
+
+void ResizeBy::update(float t)
+{
+    if (_target)
+    {
+        _target->setContentSize(_startSize + (_sizeDelta * t));
+    }
+}
+
+bool ResizeBy::initWithDuration(float duration, const cocos2d::Size& deltaSize)
+{
+    bool ret = false;
+    
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _sizeDelta = deltaSize;
+        ret = true;
+    }
+    
+    return ret;
+}
+
+//
+// JumpBy
+//
+
+JumpBy* JumpBy::create(float duration, const Vec2& position, float height, int jumps)
+{
+    JumpBy *jumpBy = new (std::nothrow) JumpBy();
+    if (jumpBy && jumpBy->initWithDuration(duration, position, height, jumps))
+    {
+        jumpBy->autorelease();
+        return jumpBy;
+    }
+    
+    delete jumpBy;
+    return nullptr;
+}
+
+bool JumpBy::initWithDuration(float duration, const Vec2& position, float height, int jumps)
+{
+    CCASSERT(jumps>=0, "Number of jumps must be >= 0");
+    if (jumps < 0)
+    {
+        log("JumpBy::initWithDuration error: Number of jumps must be >= 0");
+        return false;
+    }
+    
+    if (ActionInterval::initWithDuration(duration) && jumps>=0)
+    {
+        _delta = position;
+        _height = height;
+        _jumps = jumps;
+
+        return true;
+    }
+
+    return false;
+}
+
+JumpBy* JumpBy::clone() const
+{
+    // no copy constructor
+    return JumpBy::create(_duration, _delta, _height, _jumps);
+}
+
+void JumpBy::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _previousPos = _startPosition = target->getPosition();
+}
+
+void JumpBy::update(float t)
+{
+    // parabolic jump (since v0.8.2)
+    if (_target)
+    {
+        float frac = fmodf( t * _jumps, 1.0f );
+        float y = _height * 4 * frac * (1 - frac);
+        y += _delta.y * t;
+
+        float x = _delta.x * t;
+#if CC_ENABLE_STACKABLE_ACTIONS
+        Vec2 currentPos = _target->getPosition();
+
+        Vec2 diff = currentPos - _previousPos;
+        _startPosition = diff + _startPosition;
+
+        Vec2 newPos = _startPosition + Vec2(x,y);
+        _target->setPosition(newPos);
+
+        _previousPos = newPos;
+#else
+        _target->setPosition(_startPosition + Vec2(x,y));
+#endif // !CC_ENABLE_STACKABLE_ACTIONS
+    }
+}
+
+JumpBy* JumpBy::reverse() const
+{
+    return JumpBy::create(_duration, Vec2(-_delta.x, -_delta.y),
+        _height, _jumps);
+}
+
+//
+// JumpTo
+//
+
+JumpTo* JumpTo::create(float duration, const Vec2& position, float height, int jumps)
+{
+    JumpTo *jumpTo = new (std::nothrow) JumpTo();
+    if (jumpTo && jumpTo->initWithDuration(duration, position, height, jumps))
+    {
+        jumpTo->autorelease();
+        return jumpTo;
+    }
+    
+    delete jumpTo;
+    return nullptr;
+}
+
+bool JumpTo::initWithDuration(float duration, const Vec2& position, float height, int jumps)
+{
+    CCASSERT(jumps>=0, "Number of jumps must be >= 0");
+    if (jumps < 0)
+    {
+        log("JumpTo::initWithDuration error:Number of jumps must be >= 0");
+        return false;
+    }
+
+    if (ActionInterval::initWithDuration(duration) && jumps>=0)
+    {
+        _endPosition = position;
+        _height = height;
+        _jumps = jumps;
+
+        return true;
+    }
+
+    return false;
+}
+
+JumpTo* JumpTo::clone() const
+{
+    // no copy constructor
+    return JumpTo::create(_duration, _endPosition, _height, _jumps);
+}
+
+JumpTo* JumpTo::reverse() const
+{
+    CCASSERT(false, "reverse() not supported in JumpTo");
+    return nullptr;
+}
+
+void JumpTo::startWithTarget(Node *target)
+{
+    JumpBy::startWithTarget(target);
+    _delta.set(_endPosition.x - _startPosition.x, _endPosition.y - _startPosition.y);
+}
+
+// Bezier cubic formula:
+//    ((1 - t) + t)3 = 1 
+// Expands to ...
+//   (1 - t)3 + 3t(1-t)2 + 3t2(1 - t) + t3 = 1 
+static inline float bezierat( float a, float b, float c, float d, float t )
+{
+    return (powf(1-t,3) * a + 
+            3*t*(powf(1-t,2))*b + 
+            3*powf(t,2)*(1-t)*c +
+            powf(t,3)*d );
+}
+
+//
+// BezierBy
+//
+
+BezierBy* BezierBy::create(float t, const ccBezierConfig& c)
+{
+    BezierBy *bezierBy = new (std::nothrow) BezierBy();
+    if (bezierBy && bezierBy->initWithDuration(t, c))
+    {
+        bezierBy->autorelease();
+        return bezierBy;
+    }
+    
+    delete bezierBy;
+    return nullptr;
+}
+
+bool BezierBy::initWithDuration(float t, const ccBezierConfig& c)
+{
+    if (ActionInterval::initWithDuration(t))
+    {
+        _config = c;
+        return true;
+    }
+
+    return false;
+}
+
+void BezierBy::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _previousPosition = _startPosition = target->getPosition();
+}
+
+BezierBy* BezierBy::clone() const
+{
+    // no copy constructor
+    return BezierBy::create(_duration, _config);
+}
+
+void BezierBy::update(float time)
+{
+    if (_target)
+    {
+        float xa = 0;
+        float xb = _config.controlPoint_1.x;
+        float xc = _config.controlPoint_2.x;
+        float xd = _config.endPosition.x;
+
+        float ya = 0;
+        float yb = _config.controlPoint_1.y;
+        float yc = _config.controlPoint_2.y;
+        float yd = _config.endPosition.y;
+
+        float x = bezierat(xa, xb, xc, xd, time);
+        float y = bezierat(ya, yb, yc, yd, time);
+
+#if CC_ENABLE_STACKABLE_ACTIONS
+        Vec2 currentPos = _target->getPosition();
+        Vec2 diff = currentPos - _previousPosition;
+        _startPosition = _startPosition + diff;
+
+        Vec2 newPos = _startPosition + Vec2(x,y);
+        _target->setPosition(newPos);
+
+        _previousPosition = newPos;
+#else
+        _target->setPosition( _startPosition + Vec2(x,y));
+#endif // !CC_ENABLE_STACKABLE_ACTIONS
+    }
+}
+
+BezierBy* BezierBy::reverse() const
+{
+    ccBezierConfig r;
+
+    r.endPosition = -_config.endPosition;
+    r.controlPoint_1 = _config.controlPoint_2 + (-_config.endPosition);
+    r.controlPoint_2 = _config.controlPoint_1 + (-_config.endPosition);
+
+    BezierBy *action = BezierBy::create(_duration, r);
+    return action;
+}
+
+//
+// BezierTo
+//
+
+BezierTo* BezierTo::create(float t, const ccBezierConfig& c)
+{
+    BezierTo *bezierTo = new (std::nothrow) BezierTo();
+    if (bezierTo && bezierTo->initWithDuration(t, c))
+    {
+        bezierTo->autorelease();
+        return bezierTo;
+    }
+    
+    delete bezierTo;
+    return nullptr;
+}
+
+bool BezierTo::initWithDuration(float t, const ccBezierConfig &c)
+{
+    if (ActionInterval::initWithDuration(t))
+    {
+        _toConfig = c;
+        return true;
+    }
+    
+    return false;
+}
+
+BezierTo* BezierTo::clone() const
+{
+    // no copy constructor
+    return BezierTo::create(_duration, _toConfig);
+}
+
+void BezierTo::startWithTarget(Node *target)
+{
+    BezierBy::startWithTarget(target);
+    _config.controlPoint_1 = _toConfig.controlPoint_1 - _startPosition;
+    _config.controlPoint_2 = _toConfig.controlPoint_2 - _startPosition;
+    _config.endPosition = _toConfig.endPosition - _startPosition;
+}
+
+BezierTo* BezierTo::reverse() const
+{
+    CCASSERT(false, "CCBezierTo doesn't support the 'reverse' method");
+    return nullptr;
+}
+
+//
+// ScaleTo
+//
+ScaleTo* ScaleTo::create(float duration, float s)
+{
+    ScaleTo *scaleTo = new (std::nothrow) ScaleTo();
+    if (scaleTo && scaleTo->initWithDuration(duration, s))
+    {
+        scaleTo->autorelease();
+        return  scaleTo;
+    }
+    
+    delete scaleTo;
+    return nullptr;
+}
+
+ScaleTo* ScaleTo::create(float duration, float sx, float sy)
+{
+    ScaleTo *scaleTo = new (std::nothrow) ScaleTo();
+    if (scaleTo && scaleTo->initWithDuration(duration, sx, sy))
+    {
+        scaleTo->autorelease();
+        return scaleTo;
+    }
+    
+    delete scaleTo;
+    return nullptr;
+}
+
+ScaleTo* ScaleTo::create(float duration, float sx, float sy, float sz)
+{
+    ScaleTo *scaleTo = new (std::nothrow) ScaleTo();
+    if (scaleTo && scaleTo->initWithDuration(duration, sx, sy, sz))
+    {
+        scaleTo->autorelease();
+        return scaleTo;
+    }
+    
+    delete scaleTo;
+    return nullptr;
+}
+
+bool ScaleTo::initWithDuration(float duration, float s)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _endScaleX = s;
+        _endScaleY = s;
+        _endScaleZ = s;
+
+        return true;
+    }
+
+    return false;
+}
+
+bool ScaleTo::initWithDuration(float duration, float sx, float sy)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _endScaleX = sx;
+        _endScaleY = sy;
+        _endScaleZ = 1.f;
+
+        return true;
+    }
+
+    return false;
+}
+
+bool ScaleTo::initWithDuration(float duration, float sx, float sy, float sz)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _endScaleX = sx;
+        _endScaleY = sy;
+        _endScaleZ = sz;
+
+        return true;
+    }
+
+    return false;
+}
+
+ScaleTo* ScaleTo::clone() const
+{
+    // no copy constructor
+    return ScaleTo::create(_duration, _endScaleX, _endScaleY, _endScaleZ);
+}
+
+ScaleTo* ScaleTo::reverse() const
+{
+    CCASSERT(false, "reverse() not supported in ScaleTo");
+    return nullptr;
+}
+
+void ScaleTo::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _startScaleX = target->getScaleX();
+    _startScaleY = target->getScaleY();
+    _startScaleZ = target->getScaleZ();
+    _deltaX = _endScaleX - _startScaleX;
+    _deltaY = _endScaleY - _startScaleY;
+    _deltaZ = _endScaleZ - _startScaleZ;
+}
+
+void ScaleTo::update(float time)
+{
+    if (_target)
+    {
+        _target->setScaleX(_startScaleX + _deltaX * time);
+        _target->setScaleY(_startScaleY + _deltaY * time);
+        _target->setScaleZ(_startScaleZ + _deltaZ * time);
+    }
+}
+
+//
+// ScaleBy
+//
+
+ScaleBy* ScaleBy::create(float duration, float s)
+{
+    ScaleBy *scaleBy = new (std::nothrow) ScaleBy();
+    if (scaleBy && scaleBy->initWithDuration(duration, s))
+    {
+        scaleBy->autorelease();
+        return scaleBy;
+    }
+    
+    delete scaleBy;
+    return nullptr;
+}
+
+ScaleBy* ScaleBy::create(float duration, float sx, float sy)
+{
+    ScaleBy *scaleBy = new (std::nothrow) ScaleBy();
+    if (scaleBy && scaleBy->initWithDuration(duration, sx, sy, 1.f))
+    {
+        scaleBy->autorelease();
+        return scaleBy;
+    }
+    
+    delete scaleBy;
+    return nullptr;
+}
+
+ScaleBy* ScaleBy::create(float duration, float sx, float sy, float sz)
+{
+    ScaleBy *scaleBy = new (std::nothrow) ScaleBy();
+    if (scaleBy && scaleBy->initWithDuration(duration, sx, sy, sz))
+    {
+        scaleBy->autorelease();
+        return scaleBy;
+    }
+    
+    delete scaleBy;
+    return nullptr;
+}
+
+ScaleBy* ScaleBy::clone() const
+{
+    // no copy constructor
+    return ScaleBy::create(_duration, _endScaleX, _endScaleY, _endScaleZ);
+}
+
+void ScaleBy::startWithTarget(Node *target)
+{
+    ScaleTo::startWithTarget(target);
+    _deltaX = _startScaleX * _endScaleX - _startScaleX;
+    _deltaY = _startScaleY * _endScaleY - _startScaleY;
+    _deltaZ = _startScaleZ * _endScaleZ - _startScaleZ;
+}
+
+ScaleBy* ScaleBy::reverse() const
+{
+    return ScaleBy::create(_duration, 1 / _endScaleX, 1 / _endScaleY, 1/ _endScaleZ);
+}
+
+//
+// Blink
+//
+
+Blink* Blink::create(float duration, int blinks)
+{
+    Blink *blink = new (std::nothrow) Blink();
+    if (blink && blink->initWithDuration(duration, blinks))
+    {
+        blink->autorelease();
+        return blink;
+    }
+
+    delete blink;
+    return nullptr;
+}
+
+bool Blink::initWithDuration(float duration, int blinks)
+{
+    CCASSERT(blinks>=0, "blinks should be >= 0");
+    if (blinks < 0)
+    {
+        log("Blink::initWithDuration error:blinks should be >= 0");
+        return false;
+    }
+    
+    if (ActionInterval::initWithDuration(duration) && blinks>=0)
+    {
+        _times = blinks;
+        return true;
+    }
+
+    return false;
+}
+
+void Blink::stop()
+{
+    if (nullptr != _target)
+        _target->setVisible(_originalState);
+    ActionInterval::stop();
+}
+
+void Blink::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _originalState = target->isVisible();
+}
+
+Blink* Blink::clone() const
+{
+    // no copy constructor
+    return Blink::create(_duration, _times);
+}
+
+void Blink::update(float time)
+{
+    if (_target && ! isDone())
+    {
+        float slice = 1.0f / _times;
+        float m = fmodf(time, slice);
+        _target->setVisible(m > slice / 2 ? true : false);
+    }
+}
+
+Blink* Blink::reverse() const
+{
+    return Blink::create(_duration, _times);
+}
+
+//
+// FadeIn
+//
+
+FadeIn* FadeIn::create(float d)
+{
+    FadeIn* action = new (std::nothrow) FadeIn();
+    if (action && action->initWithDuration(d,255))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+FadeIn* FadeIn::clone() const
+{
+    // no copy constructor
+    return FadeIn::create(_duration);
+}
+
+void FadeIn::setReverseAction(cocos2d::FadeTo *ac)
+{
+    _reverseAction = ac;
+}
+
+
+FadeTo* FadeIn::reverse() const
+{
+    auto action = FadeOut::create(_duration);
+    action->setReverseAction(const_cast<FadeIn*>(this));
+    return action;
+    
+}
+
+void FadeIn::startWithTarget(cocos2d::Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    
+    if (nullptr != _reverseAction)
+        this->_toOpacity = this->_reverseAction->_fromOpacity;
+    else
+        _toOpacity = 255;
+    
+    if (target)
+        _fromOpacity = target->getOpacity();
+}
+
+//
+// FadeOut
+//
+
+FadeOut* FadeOut::create(float d)
+{
+    FadeOut* action = new (std::nothrow) FadeOut();
+    if (action && action->initWithDuration(d,0))
+    {
+        action->autorelease();
+        return action;
+    }
+    
+    delete action;
+    return nullptr;
+}
+
+FadeOut* FadeOut::clone() const
+{
+    // no copy constructor
+    return FadeOut::create(_duration);
+}
+
+void FadeOut::startWithTarget(cocos2d::Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    
+    if (nullptr != _reverseAction)
+        _toOpacity = _reverseAction->_fromOpacity;
+    else
+        _toOpacity = 0;
+    
+    if (target)
+        _fromOpacity = target->getOpacity();
+}
+
+void FadeOut::setReverseAction(cocos2d::FadeTo *ac)
+{
+    _reverseAction = ac;
+}
+
+
+FadeTo* FadeOut::reverse() const
+{
+    auto action = FadeIn::create(_duration);
+    action->setReverseAction(const_cast<FadeOut*>(this));
+    return action;
+}
+
+//
+// FadeTo
+//
+
+FadeTo* FadeTo::create(float duration, uint8_t opacity)
+{
+    FadeTo *fadeTo = new (std::nothrow) FadeTo();
+    if (fadeTo && fadeTo->initWithDuration(duration, opacity))
+    {
+        fadeTo->autorelease();
+        return fadeTo;
+    }
+    
+    delete fadeTo;
+    return nullptr;
+}
+
+bool FadeTo::initWithDuration(float duration, uint8_t opacity)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _toOpacity = opacity;
+        return true;
+    }
+
+    return false;
+}
+
+FadeTo* FadeTo::clone() const
+{
+    // no copy constructor
+    return FadeTo::create(_duration, _toOpacity);
+}
+
+FadeTo* FadeTo::reverse() const
+{
+    CCASSERT(false, "reverse() not supported in FadeTo");
+    return nullptr;
+}
+
+void FadeTo::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+
+    if (target)
+    {
+        _fromOpacity = target->getOpacity();
+    }
+}
+
+void FadeTo::update(float time)
+{
+    if (_target)
+    {
+        _target->setOpacity((uint8_t)(_fromOpacity + (_toOpacity - _fromOpacity) * time));
+    }
+}
+
+//
+// TintTo
+//
+TintTo* TintTo::create(float duration, uint8_t red, uint8_t green, uint8_t blue)
+{
+    TintTo *tintTo = new (std::nothrow) TintTo();
+    if (tintTo && tintTo->initWithDuration(duration, red, green, blue))
+    {
+        tintTo->autorelease();
+        return tintTo;
+    }
+    
+    delete tintTo;
+    return nullptr;
+}
+
+TintTo* TintTo::create(float duration, const Color3B& color)
+{
+    return create(duration, color.r, color.g, color.b);
+}
+
+bool TintTo::initWithDuration(float duration, uint8_t red, uint8_t green, uint8_t blue)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _to = Color3B(red, green, blue);
+        return true;
+    }
+
+    return false;
+}
+
+TintTo* TintTo::clone() const
+{
+    // no copy constructor
+    return TintTo::create(_duration, _to.r, _to.g, _to.b);
+}
+
+TintTo* TintTo::reverse() const
+{
+    CCASSERT(false, "reverse() not supported in TintTo");
+    return nullptr;
+}
+
+void TintTo::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    if (_target)
+    {
+        _from = _target->getColor();
+    }
+}
+
+void TintTo::update(float time)
+{
+    if (_target)
+    {
+        _target->setColor(Color3B(uint8_t(_from.r + (_to.r - _from.r) * time),
+            (uint8_t)(_from.g + (_to.g - _from.g) * time),
+            (uint8_t)(_from.b + (_to.b - _from.b) * time)));
+    }
+}
+
+//
+// TintBy
+//
+
+TintBy* TintBy::create(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue)
+{
+    TintBy *tintBy = new (std::nothrow) TintBy();
+    if (tintBy && tintBy->initWithDuration(duration, deltaRed, deltaGreen, deltaBlue))
+    {
+        tintBy->autorelease();
+        return tintBy;
+    }
+    
+    delete tintBy;
+    return nullptr;
+}
+
+bool TintBy::initWithDuration(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _deltaR = deltaRed;
+        _deltaG = deltaGreen;
+        _deltaB = deltaBlue;
+
+        return true;
+    }
+
+    return false;
+}
+
+TintBy* TintBy::clone() const
+{
+    // no copy constructor
+    return TintBy::create(_duration, _deltaR, _deltaG, _deltaB);
+}
+
+void TintBy::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+
+    if (target)
+    {
+        Color3B color = target->getColor();
+        _fromR = color.r;
+        _fromG = color.g;
+        _fromB = color.b;
+    }    
+}
+
+void TintBy::update(float time)
+{
+    if (_target)
+    {
+        _target->setColor(Color3B((uint8_t)(_fromR + _deltaR * time),
+            (uint8_t)(_fromG + _deltaG * time),
+            (uint8_t)(_fromB + _deltaB * time)));
+    }    
+}
+
+TintBy* TintBy::reverse() const
+{
+    return TintBy::create(_duration, -_deltaR, -_deltaG, -_deltaB);
+}
+
+//
+// DelayTime
+//
+DelayTime* DelayTime::create(float d)
+{
+    DelayTime* action = new (std::nothrow) DelayTime();
+    if (action && action->initWithDuration(d))
+    {
+        action->autorelease();
+        return action;
+    }
+    
+    delete action;
+    return nullptr;
+}
+
+DelayTime* DelayTime::clone() const
+{
+    // no copy constructor
+    return DelayTime::create(_duration);
+}
+
+void DelayTime::update(float /*time*/)
+{
+    return;
+}
+
+DelayTime* DelayTime::reverse() const
+{
+    return DelayTime::create(_duration);
+}
+
+//
+// ReverseTime
+//
+
+ReverseTime* ReverseTime::create(FiniteTimeAction *action)
+{
+    // casting to prevent warnings
+    ReverseTime *reverseTime = new (std::nothrow) ReverseTime();
+    if (reverseTime && reverseTime->initWithAction( action->clone() ))
+    {
+        reverseTime->autorelease();
+        return reverseTime;
+    }
+    
+    delete reverseTime;
+    return nullptr;
+}
+
+bool ReverseTime::initWithAction(FiniteTimeAction *action)
+{
+    CCASSERT(action != nullptr, "action can't be nullptr!");
+    CCASSERT(action != _other, "action doesn't equal to _other!");
+    if (action == nullptr || action == _other)
+    {
+        log("ReverseTime::initWithAction error: action is null or action equal to _other");
+        return false;
+    }
+
+    if (ActionInterval::initWithDuration(action->getDuration()))
+    {
+        // Don't leak if action is reused
+        CC_SAFE_RELEASE(_other);
+
+        _other = action;
+        action->retain();
+
+        return true;
+    }
+
+    return false;
+}
+
+ReverseTime* ReverseTime::clone() const
+{
+    // no copy constructor
+    return ReverseTime::create(_other->clone());
+}
+
+ReverseTime::ReverseTime() : _other(nullptr)
+{
+
+}
+
+ReverseTime::~ReverseTime()
+{
+    CC_SAFE_RELEASE(_other);
+}
+
+void ReverseTime::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _other->startWithTarget(target);
+}
+
+void ReverseTime::stop()
+{
+    _other->stop();
+    ActionInterval::stop();
+}
+
+void ReverseTime::update(float time)
+{
+    if (_other)
+    {
+        if (!(sendUpdateEventToScript(1 - time, _other)))
+            _other->update(1 - time);
+    }
+}
+
+ReverseTime* ReverseTime::reverse() const
+{
+    // FIXME: This looks like a bug
+    return (ReverseTime*)_other->clone();
+}
+
+//
+// Animate
+//
+Animate* Animate::create(Animation *animation)
+{
+    Animate *animate = new (std::nothrow) Animate();
+    if (animate && animate->initWithAnimation(animation))
+    {
+        animate->autorelease();
+        return animate;
+    }
+    
+    delete animate;
+    return nullptr;
+}
+
+Animate::Animate()
+{
+}
+
+Animate::~Animate()
+{
+    CC_SAFE_RELEASE(_animation);
+    CC_SAFE_RELEASE(_origFrame);
+    CC_SAFE_DELETE(_splitTimes);
+    CC_SAFE_RELEASE(_frameDisplayedEvent);
+}
+
+bool Animate::initWithAnimation(Animation* animation)
+{
+    CCASSERT( animation!=nullptr, "Animate: argument Animation must be non-nullptr");
+    if (animation == nullptr)
+    {
+        log("Animate::initWithAnimation: argument Animation must be non-nullptr");
+        return false;
+    }
+
+    float singleDuration = animation->getDuration();
+
+    if ( ActionInterval::initWithDuration(singleDuration * animation->getLoops() ) )
+    {
+        _nextFrame = 0;
+        setAnimation(animation);
+        _origFrame = nullptr;
+        _executedLoops = 0;
+
+        _splitTimes->reserve(animation->getFrames().size());
+
+        float accumUnitsOfTime = 0;
+        float newUnitOfTimeValue = singleDuration / animation->getTotalDelayUnits();
+
+        auto& frames = animation->getFrames();
+
+        for (auto& frame : frames)
+        {
+            float value = (accumUnitsOfTime * newUnitOfTimeValue) / singleDuration;
+            accumUnitsOfTime += frame->getDelayUnits();
+            _splitTimes->push_back(value);
+        }    
+        return true;
+    }
+    return false;
+}
+
+void Animate::setAnimation(cocos2d::Animation *animation)
+{
+    if (_animation != animation)
+    {
+        CC_SAFE_RETAIN(animation);
+        CC_SAFE_RELEASE(_animation);
+        _animation = animation;
+    }
+}
+
+Animate* Animate::clone() const
+{
+    // no copy constructor
+    return Animate::create(_animation->clone());
+}
+
+void Animate::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    Sprite *sprite = static_cast<Sprite*>(target);
+
+    CC_SAFE_RELEASE(_origFrame);
+
+    if (_animation->getRestoreOriginalFrame())
+    {
+        _origFrame = sprite->getSpriteFrame();
+        _origFrame->retain();
+    }
+    _nextFrame = 0;
+    _executedLoops = 0;
+}
+
+void Animate::stop()
+{
+    if (_animation->getRestoreOriginalFrame() && _target)
+    {
+        auto blend = static_cast<Sprite*>(_target)->getBlendFunc();
+        static_cast<Sprite*>(_target)->setSpriteFrame(_origFrame);
+        static_cast<Sprite*>(_target)->setBlendFunc(blend);
+    }
+
+    ActionInterval::stop();
+}
+
+void Animate::update(float t)
+{
+    // if t==1, ignore. Animation should finish with t==1
+    if( t < 1.0f )
+    {
+        t *= _animation->getLoops();
+
+        // new loop?  If so, reset frame counter
+        unsigned int loopNumber = (unsigned int)t;
+        if( loopNumber > _executedLoops )
+        {
+            _nextFrame = 0;
+            _executedLoops++;
+        }
+
+        // new t for animations
+        t = fmodf(t, 1.0f);
+    }
+
+    auto& frames = _animation->getFrames();
+    auto numberOfFrames = frames.size();
+    SpriteFrame *frameToDisplay = nullptr;
+
+    for( int i=_nextFrame; i < numberOfFrames; i++ )
+    {
+        float splitTime = _splitTimes->at(i);
+
+        if( splitTime <= t )
+        {
+            auto blend = static_cast<Sprite*>(_target)->getBlendFunc();
+            _currFrameIndex = i;
+            AnimationFrame* frame = frames.at(_currFrameIndex);
+            frameToDisplay = frame->getSpriteFrame();
+            static_cast<Sprite*>(_target)->setSpriteFrame(frameToDisplay);
+            static_cast<Sprite*>(_target)->setBlendFunc(blend);
+
+            const ValueMap& dict = frame->getUserInfo();
+            if ( !dict.empty() )
+            {
+                if (_frameDisplayedEvent == nullptr)
+                    _frameDisplayedEvent = new (std::nothrow) EventCustom(AnimationFrameDisplayedNotification);
+                
+                _frameDisplayedEventInfo.target = _target;
+                _frameDisplayedEventInfo.userInfo = &dict;
+                _frameDisplayedEvent->setUserData(&_frameDisplayedEventInfo);
+                Director::getInstance()->getEventDispatcher()->dispatchEvent(_frameDisplayedEvent);
+            }
+            _nextFrame = i+1;
+        }
+        // Issue 1438. Could be more than one frame per tick, due to low frame rate or frame delta < 1/FPS
+        else {
+            break;
+        }
+    }
+}
+
+Animate* Animate::reverse() const
+{
+    auto& oldArray = _animation->getFrames();
+    Vector<AnimationFrame*> newArray(oldArray.size());
+   
+    if (!oldArray.empty())
+    {
+        for (auto iter = oldArray.crbegin(), iterCrend = oldArray.crend(); iter != iterCrend; ++iter)
+        {
+            AnimationFrame* animFrame = *iter;
+            if (!animFrame)
+            {
+                break;
+            }
+
+            newArray.pushBack(animFrame->clone());
+        }
+    }
+
+    Animation *newAnim = Animation::create(newArray, _animation->getDelayPerUnit(), _animation->getLoops());
+    newAnim->setRestoreOriginalFrame(_animation->getRestoreOriginalFrame());
+    return Animate::create(newAnim);
+}
+
+// TargetedAction
+
+TargetedAction::TargetedAction()
+: _action(nullptr)
+, _forcedTarget(nullptr)
+{
+
+}
+
+TargetedAction::~TargetedAction()
+{
+    CC_SAFE_RELEASE(_forcedTarget);
+    CC_SAFE_RELEASE(_action);
+}
+
+TargetedAction* TargetedAction::create(Node* target, FiniteTimeAction* action)
+{
+    TargetedAction* p = new (std::nothrow) TargetedAction();
+    if (p && p->initWithTarget(target, action))
+    {
+        p->autorelease();
+        return p;
+    }
+    
+    delete p;
+    return nullptr;
+}
+
+
+bool TargetedAction::initWithTarget(Node* target, FiniteTimeAction* action)
+{
+    if(ActionInterval::initWithDuration(action->getDuration()))
+    {
+        CC_SAFE_RETAIN(target);
+        _forcedTarget = target;
+        CC_SAFE_RETAIN(action);
+        _action = action;
+        return true;
+    }
+    return false;
+}
+
+TargetedAction* TargetedAction::clone() const
+{
+    // no copy constructor
+    // win32 : use the _other's copy object.
+    return TargetedAction::create(_forcedTarget, _action->clone());
+}
+
+TargetedAction* TargetedAction::reverse() const
+{
+    // just reverse the internal action
+    auto a = new (std::nothrow) TargetedAction();
+    a->initWithTarget(_forcedTarget, _action->reverse());
+    a->autorelease();
+    return a;
+}
+
+void TargetedAction::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _action->startWithTarget(_forcedTarget);
+}
+
+void TargetedAction::stop()
+{
+    _action->stop();
+}
+
+void TargetedAction::update(float time)
+{
+    if (!(sendUpdateEventToScript(time, _action)))
+        _action->update(time);
+}
+
+void TargetedAction::setForcedTarget(Node* forcedTarget)
+{
+    if( _forcedTarget != forcedTarget )
+    {
+        CC_SAFE_RETAIN(forcedTarget);
+        CC_SAFE_RELEASE(_forcedTarget);
+        _forcedTarget = forcedTarget;
+    }
+}
+
+// ActionFloat
+
+ActionFloat* ActionFloat::create(float duration, float from, float to, ActionFloatCallback callback)
+{
+    auto ref = new (std::nothrow) ActionFloat();
+    if (ref && ref->initWithDuration(duration, from, to, callback))
+    {
+        ref->autorelease();
+        return ref;
+    }
+    
+    delete ref;
+    return nullptr;
+}
+
+bool ActionFloat::initWithDuration(float duration, float from, float to, ActionFloatCallback callback)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _from = from;
+        _to = to;
+        _callback = callback;
+        return true;
+    }
+    return false;
+}
+
+ActionFloat* ActionFloat::clone() const
+{
+    return ActionFloat::create(_duration, _from, _to, _callback);
+}
+
+void ActionFloat::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _delta = _to - _from;
+}
+
+void ActionFloat::update(float delta)
+{
+    float value = _to - _delta * (1 - delta);
+
+    if (_callback)
+    {
+        // report back value to caller
+        _callback(value);
+    }
+}
+
+ActionFloat* ActionFloat::reverse() const
+{
+    return ActionFloat::create(_duration, _to, _from, _callback);
+}
+
+NS_CC_END

+ 1652 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionInterval.h

@@ -0,0 +1,1652 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef __ACTION_CCINTERVAL_ACTION_H__
+#define __ACTION_CCINTERVAL_ACTION_H__
+
+#include <vector>
+
+#include "2d/CCAction.h"
+#include "2d/CCAnimation.h"
+#include "base/CCProtocols.h"
+#include "base/CCVector.h"
+
+NS_CC_BEGIN
+
+class Node;
+class SpriteFrame;
+class EventCustom;
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/** @class ActionInterval
+@brief An interval action is an action that takes place within a certain period of time.
+It has an start time, and a finish time. The finish time is the parameter
+duration plus the start time.
+
+These ActionInterval actions have some interesting properties, like:
+- They can run normally (default)
+- They can run reversed with the reverse method
+- They can run with the time altered with the Accelerate, AccelDeccel and Speed actions.
+
+For example, you can simulate a Ping Pong effect running the action normally and
+then running it again in Reverse mode.
+
+Example:
+
+@code
+auto action = MoveBy::create(1.0f, Vec2::ONE);
+auto pingPongAction = Sequence::create(action, action->reverse(), nullptr);
+@endcode
+*/
+class CC_DLL ActionInterval : public FiniteTimeAction
+{
+public:
+    /** How many seconds had elapsed since the actions started to run.
+     *
+     * @return The seconds had elapsed since the actions started to run.
+     */
+    float getElapsed() { return _elapsed; }
+
+    /** Sets the amplitude rate, extension in GridAction
+     *
+     * @param amp   The amplitude rate.
+     */
+    void setAmplitudeRate(float amp);
+    
+    /** Gets the amplitude rate, extension in GridAction
+     *
+     * @return  The amplitude rate.
+     */
+    float getAmplitudeRate();
+
+    //
+    // Overrides
+    //
+    virtual bool isDone() const override;
+    /**
+     * @param dt in seconds
+     */
+    virtual void step(float dt) override;
+    virtual void startWithTarget(Node *target) override;
+    virtual ActionInterval* reverse() const override
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+
+    virtual ActionInterval *clone() const override
+    {
+        CC_ASSERT(0);
+        return nullptr;
+    }
+
+CC_CONSTRUCTOR_ACCESS:
+    /** initializes the action */
+    bool initWithDuration(float d);
+
+protected:
+    float _elapsed;
+    bool _firstTick;
+    bool _done;
+    
+protected:
+    bool sendUpdateEventToScript(float dt, Action *actionObject);
+};
+
+/** @class Sequence
+ * @brief Runs actions sequentially, one after another.
+ */
+class CC_DLL Sequence : public ActionInterval
+{
+public:
+    /** Helper constructor to create an array of sequenceable actions.
+     *
+     * @return An autoreleased Sequence object.
+     */
+    static Sequence* create(FiniteTimeAction *action1, ...) CC_REQUIRES_NULL_TERMINATION;
+
+    /** Helper constructor to create an array of sequenceable actions given an array.
+     * @code
+     * When this function bound to the js or lua,the input params changed
+     * in js  :var   create(var   object1,var   object2, ...)
+     * in lua :local create(local object1,local object2, ...)
+     * @endcode
+     *
+     * @param arrayOfActions An array of sequenceable actions.
+     * @return An autoreleased Sequence object.
+     */
+    static Sequence* create(const Vector<FiniteTimeAction*>& arrayOfActions);
+    /** Helper constructor to create an array of sequence-able actions.
+     *
+     * @param action1 The first sequenceable action.
+     * @param args The va_list variable.
+     * @return An autoreleased Sequence object.
+     * @js NA
+     */
+    static Sequence* createWithVariableList(FiniteTimeAction *action1, va_list args);
+    /** Creates the action.
+     * @param actionOne The first sequenceable action.
+     * @param actionTwo The second sequenceable action.
+     * @return An autoreleased Sequence object.
+     * @js NA
+     */
+    static Sequence* createWithTwoActions(FiniteTimeAction *actionOne, FiniteTimeAction *actionTwo);
+
+    //
+    // Overrides
+    //
+    virtual Sequence* clone() const override;
+    virtual Sequence* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void stop() override;
+    virtual bool isDone() const override;
+    /**
+     * @param t In seconds.
+     */
+    virtual void update(float t) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Sequence();
+    virtual ~Sequence();
+
+    /** initializes the action */
+    bool initWithTwoActions(FiniteTimeAction *pActionOne, FiniteTimeAction *pActionTwo);
+    bool init(const Vector<FiniteTimeAction*>& arrayOfActions);
+
+protected:
+    FiniteTimeAction *_actions[2];
+    float _split;
+    int _last;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Sequence);
+};
+
+/** @class Repeat
+ * @brief Repeats an action a number of times.
+ * To repeat an action forever use the RepeatForever action.
+ */
+class CC_DLL Repeat : public ActionInterval
+{
+public:
+    /** Creates a Repeat action. Times is an unsigned integer between 1 and pow(2,30).
+     *
+     * @param action The action needs to repeat.
+     * @param times The repeat times.
+     * @return An autoreleased Repeat object.
+     */
+    static Repeat* create(FiniteTimeAction *action, unsigned int times);
+
+    /** Sets the inner action.
+     *
+     * @param action The inner action.
+     */
+    void setInnerAction(FiniteTimeAction *action)
+    {
+        if (_innerAction != action)
+        {
+            CC_SAFE_RETAIN(action);
+            CC_SAFE_RELEASE(_innerAction);
+            _innerAction = action;
+        }
+    }
+
+    /** Gets the inner action.
+     *
+     * @return The inner action.
+     */
+    FiniteTimeAction* getInnerAction()
+    {
+        return _innerAction;
+    }
+
+    //
+    // Overrides
+    //
+    virtual Repeat* clone() const override;
+    virtual Repeat* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void stop() override;
+    /**
+     * @param dt In seconds.
+     */
+    virtual void update(float dt) override;
+    virtual bool isDone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Repeat() {}
+    virtual ~Repeat();
+
+    /** initializes a Repeat action. Times is an unsigned integer between 1 and pow(2,30) */
+    bool initWithAction(FiniteTimeAction *pAction, unsigned int times);
+
+protected:
+    unsigned int _times;
+    unsigned int _total;
+    float _nextDt;
+    bool _actionInstant;
+    /** Inner action */
+    FiniteTimeAction *_innerAction;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Repeat);
+};
+
+/** @class RepeatForever
+ * @brief Repeats an action for ever.
+ To repeat the an action for a limited number of times use the Repeat action.
+ * @warning This action can't be Sequenceable because it is not an IntervalAction.
+ */
+class CC_DLL RepeatForever : public ActionInterval
+{
+public:
+    /** Creates the action.
+     *
+     * @param action The action need to repeat forever.
+     * @return An autoreleased RepeatForever object.
+     */
+    static RepeatForever* create(ActionInterval *action);
+
+    /** Sets the inner action.
+     *
+     * @param action The inner action.
+     */
+    void setInnerAction(ActionInterval *action)
+    {
+        if (_innerAction != action)
+        {
+            CC_SAFE_RELEASE(_innerAction);
+            _innerAction = action;
+            CC_SAFE_RETAIN(_innerAction);
+        }
+    }
+
+    /** Gets the inner action.
+     *
+     * @return The inner action.
+     */
+    ActionInterval* getInnerAction()
+    {
+        return _innerAction;
+    }
+
+    //
+    // Overrides
+    //
+    virtual RepeatForever* clone() const override;
+    virtual RepeatForever* reverse() const override;
+    virtual void startWithTarget(Node* target) override;
+    /**
+     * @param dt In seconds.
+     */
+    virtual void step(float dt) override;
+    virtual bool isDone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    RepeatForever()
+    : _innerAction(nullptr)
+    {}
+    virtual ~RepeatForever();
+
+    /** initializes the action */
+    bool initWithAction(ActionInterval *action);
+
+protected:
+    /** Inner action */
+    ActionInterval *_innerAction;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(RepeatForever);
+};
+
+/** @class Spawn
+ * @brief Spawn a new action immediately
+ */
+class CC_DLL Spawn : public ActionInterval
+{
+public:
+    /** Helper constructor to create an array of spawned actions.
+     * @code
+     * When this function bound to the js or lua, the input params changed.
+     * in js  :var   create(var   object1,var   object2, ...)
+     * in lua :local create(local object1,local object2, ...)
+     * @endcode
+     *
+     * @return An autoreleased Spawn object.
+     */
+    static Spawn* create(FiniteTimeAction *action1, ...) CC_REQUIRES_NULL_TERMINATION;
+
+    /** Helper constructor to create an array of spawned actions. 
+     *
+     * @param action1   The first sequenceable action.
+     * @param args  The va_list variable.
+     * @return  An autoreleased Spawn object.
+     * @js NA
+     */
+    static Spawn* createWithVariableList(FiniteTimeAction *action1, va_list args);
+
+    /** Helper constructor to create an array of spawned actions given an array.
+     *
+     * @param arrayOfActions    An array of spawned actions.
+     * @return  An autoreleased Spawn object.
+     */
+    static Spawn* create(const Vector<FiniteTimeAction*>& arrayOfActions);
+
+    /** Creates the Spawn action.
+     *
+     * @param action1   The first spawned action.
+     * @param action2   The second spawned action.
+     * @return An autoreleased Spawn object.
+     * @js NA
+     */
+    static Spawn* createWithTwoActions(FiniteTimeAction *action1, FiniteTimeAction *action2);
+
+    //
+    // Overrides
+    //
+    virtual Spawn* clone() const override;
+    virtual Spawn* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void stop() override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Spawn();
+    virtual ~Spawn();
+
+    /** initializes the Spawn action with the 2 actions to spawn */
+    bool initWithTwoActions(FiniteTimeAction *action1, FiniteTimeAction *action2);
+    bool init(const Vector<FiniteTimeAction*>& arrayOfActions);
+
+protected:
+    FiniteTimeAction *_one;
+    FiniteTimeAction *_two;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Spawn);
+};
+
+/** @class RotateTo
+ * @brief Rotates a Node object to a certain angle by modifying it's rotation attribute.
+ The direction will be decided by the shortest angle.
+*/ 
+class CC_DLL RotateTo : public ActionInterval
+{
+public:
+    /** 
+     * Creates the action with separate rotation angles.
+     *
+     * @param duration Duration time, in seconds.
+     * @param dstAngleX In degreesCW.
+     * @param dstAngleY In degreesCW.
+     * @return An autoreleased RotateTo object.
+     */
+    static RotateTo* create(float duration, float dstAngleX, float dstAngleY);
+
+    /** 
+     * Creates the action.
+     *
+     * @param duration Duration time, in seconds.
+     * @param dstAngle In degreesCW.
+     * @return An autoreleased RotateTo object.
+     */
+    static RotateTo* create(float duration, float dstAngle);
+
+    /** 
+     * Creates the action with 3D rotation angles.
+     * @param duration Duration time, in seconds.
+     * @param dstAngle3D A Vec3 angle.
+     * @return An autoreleased RotateTo object.
+     */
+    static RotateTo* create(float duration, const Vec3& dstAngle3D);
+
+    //
+    // Overrides
+    //
+    virtual RotateTo* clone() const override;
+    virtual RotateTo* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    RotateTo();
+    virtual ~RotateTo() {}
+
+    /** 
+     * initializes the action
+     * @param duration in seconds
+     * @param dstAngleX in degreesCW
+     * @param dstAngleY in degreesCW
+     */
+    bool initWithDuration(float duration, float dstAngleX, float dstAngleY);
+    /**
+     * initializes the action
+     * @param duration in seconds
+     */
+    bool initWithDuration(float duration, const Vec3& dstAngle3D);
+
+    /** 
+     * calculates the start and diff angles
+     * @param dstAngle in degreesCW
+     */
+    void calculateAngles(float &startAngle, float &diffAngle, float dstAngle);
+    
+protected:
+    bool _is3D;
+    Vec3 _dstAngle;
+    Vec3 _startAngle;
+    Vec3 _diffAngle;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(RotateTo);
+};
+
+/** @class RotateBy
+ * @brief Rotates a Node object clockwise a number of degrees by modifying it's rotation attribute.
+*/
+class CC_DLL RotateBy : public ActionInterval
+{
+public:
+    /** 
+     * Creates the action.
+     *
+     * @param duration Duration time, in seconds.
+     * @param deltaAngle In degreesCW.
+     * @return An autoreleased RotateBy object.
+     */
+    static RotateBy* create(float duration, float deltaAngle);
+    /**
+     * Creates the action with separate rotation angles.
+     *
+     * @param duration Duration time, in seconds.
+     * @param deltaAngleZ_X In degreesCW.
+     * @param deltaAngleZ_Y In degreesCW.
+     * @return An autoreleased RotateBy object.
+     * @warning The physics body contained in Node doesn't support rotate with different x and y angle.
+     */
+    static RotateBy* create(float duration, float deltaAngleZ_X, float deltaAngleZ_Y);
+    /** Creates the action with 3D rotation angles.
+     *
+     * @param duration Duration time, in seconds.
+     * @param deltaAngle3D A Vec3 angle.
+     * @return An autoreleased RotateBy object.
+     */
+    static RotateBy* create(float duration, const Vec3& deltaAngle3D);
+
+    //
+    // Override
+    //
+    virtual RotateBy* clone() const override;
+    virtual RotateBy* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    RotateBy();
+    virtual ~RotateBy() {}
+
+    /** initializes the action */
+    bool initWithDuration(float duration, float deltaAngle);
+    /** 
+     * @warning The physics body contained in Node doesn't support rotate with different x and y angle.
+     * @param deltaAngleZ_X in degreesCW
+     * @param deltaAngleZ_Y in degreesCW
+     */
+    bool initWithDuration(float duration, float deltaAngleZ_X, float deltaAngleZ_Y);
+    bool initWithDuration(float duration, const Vec3& deltaAngle3D);
+    
+protected:
+    bool _is3D;
+    Vec3 _deltaAngle;
+    Vec3 _startAngle;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(RotateBy);
+};
+
+/** @class MoveBy
+ * @brief Moves a Node object x,y pixels by modifying it's position attribute.
+ x and y are relative to the position of the object.
+ Several MoveBy actions can be concurrently called, and the resulting
+ movement will be the sum of individual movements.
+ @since v2.1beta2-custom
+ */
+class CC_DLL MoveBy : public ActionInterval
+{
+public:
+    /** 
+     * Creates the action.
+     *
+     * @param duration Duration time, in seconds.
+     * @param deltaPosition The delta distance in 2d, it's a Vec2 type.
+     * @return An autoreleased MoveBy object.
+     */
+    static MoveBy* create(float duration, const Vec2& deltaPosition);
+    /**
+     * Creates the action.
+     *
+     * @param duration Duration time, in seconds.
+     * @param deltaPosition The delta distance in 3d, it's a Vec3 type.
+     * @return An autoreleased MoveBy object.
+     */
+    static MoveBy* create(float duration, const Vec3& deltaPosition);
+
+    //
+    // Overrides
+    //
+    virtual MoveBy* clone() const override;
+    virtual MoveBy* reverse() const  override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time in seconds
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    MoveBy():_is3D(false) {}
+    virtual ~MoveBy() {}
+
+    /** initializes the action */
+    bool initWithDuration(float duration, const Vec2& deltaPosition);
+    bool initWithDuration(float duration, const Vec3& deltaPosition);
+
+protected:
+    bool _is3D;
+    Vec3 _positionDelta;
+    Vec3 _startPosition;
+    Vec3 _previousPosition;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MoveBy);
+};
+
+/** @class MoveTo
+ * @brief Moves a Node object to the position x,y. x and y are absolute coordinates by modifying it's position attribute.
+ Several MoveTo actions can be concurrently called, and the resulting
+ movement will be the sum of individual movements.
+ @since v2.1beta2-custom
+ */
+class CC_DLL MoveTo : public MoveBy
+{
+public:
+    /** 
+     * Creates the action.
+     * @param duration Duration time, in seconds.
+     * @param position The destination position in 2d.
+     * @return An autoreleased MoveTo object.
+     */
+    static MoveTo* create(float duration, const Vec2& position);
+    /**
+     * Creates the action.
+     * @param duration Duration time, in seconds.
+     * @param position The destination position in 3d.
+     * @return An autoreleased MoveTo object.
+     */
+    static MoveTo* create(float duration, const Vec3& position);
+
+    //
+    // Overrides
+    //
+    virtual MoveTo* clone() const override;
+    virtual MoveTo* reverse() const  override;
+    virtual void startWithTarget(Node *target) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    MoveTo() {}
+    virtual ~MoveTo() {}
+
+    /** 
+     * initializes the action
+     * @param duration in seconds
+     */
+    bool initWithDuration(float duration, const Vec2& position);
+    /**
+     * initializes the action
+     * @param duration in seconds
+     */
+    bool initWithDuration(float duration, const Vec3& position);
+
+protected:
+    Vec3 _endPosition;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MoveTo);
+};
+
+/** @class SkewTo
+ * @brief Skews a Node object to given angles by modifying it's skewX and skewY attributes
+@since v1.0
+*/
+class CC_DLL SkewTo : public ActionInterval
+{
+public:
+    /** 
+     * Creates the action.
+     * @param t Duration time, in seconds.
+     * @param sx Skew x angle.
+     * @param sy Skew y angle.
+     * @return An autoreleased SkewTo object.
+     */
+    static SkewTo* create(float t, float sx, float sy);
+
+    //
+    // Overrides
+    //
+    virtual SkewTo* clone() const override;
+    virtual SkewTo* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    SkewTo();
+    virtual ~SkewTo() {}
+    /**
+     * @param t In seconds.
+     */
+    bool initWithDuration(float t, float sx, float sy);
+
+protected:
+    float _skewX;
+    float _skewY;
+    float _startSkewX;
+    float _startSkewY;
+    float _endSkewX;
+    float _endSkewY;
+    float _deltaX;
+    float _deltaY;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(SkewTo);
+};
+
+/** @class SkewBy
+* @brief Skews a Node object by skewX and skewY degrees.
+@since v1.0
+*/
+class CC_DLL SkewBy : public SkewTo
+{
+public:
+    /** 
+     * Creates the action.
+     * @param t Duration time, in seconds.
+     * @param deltaSkewX Skew x delta angle.
+     * @param deltaSkewY Skew y delta angle.
+     * @return An autoreleased SkewBy object.
+     */
+    static SkewBy* create(float t, float deltaSkewX, float deltaSkewY);
+
+    //
+    // Overrides
+    //
+    virtual void startWithTarget(Node *target) override;
+    virtual SkewBy* clone() const  override;
+    virtual SkewBy* reverse() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    SkewBy() {}
+    virtual ~SkewBy() {}
+    /**
+     * @param t In seconds.
+     */
+    bool initWithDuration(float t, float sx, float sy);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(SkewBy);
+};
+
+/** @class ResizeTo
+* @brief Resize a Node object to the final size by modifying it's Size attribute.
+*/
+class  CC_DLL ResizeTo : public ActionInterval 
+{
+public:
+    /**
+    * Creates the action.
+    * @brief Resize a Node object to the final size by modifying it's Size attribute. Works on all nodes where setContentSize is effective. But it's mostly useful for nodes where 9-slice is enabled
+    * @param duration Duration time, in seconds.
+    * @param final_size The target size to reach
+    * @return An autoreleased RotateTo object.
+    */
+    static ResizeTo* create(float duration, const cocos2d::Size& final_size);
+
+    //
+    // Overrides
+    //
+    virtual ResizeTo* clone() const override;
+    void startWithTarget(cocos2d::Node* target) override;
+    void update(float time) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    ResizeTo() {}
+    virtual ~ResizeTo() {}
+    
+    /**
+    * initializes the action
+    * @param duration in seconds
+    * @param final_size in Size type
+    */
+    bool initWithDuration(float duration, const cocos2d::Size& final_size);
+
+protected:
+    cocos2d::Size _initialSize;
+    cocos2d::Size _finalSize;
+    cocos2d::Size _sizeDelta;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ResizeTo);
+};
+
+
+/** @class ResizeBy
+* @brief Resize a Node object by a Size. Works on all nodes where setContentSize is effective. But it's mostly useful for nodes where 9-slice is enabled
+*/
+class CC_DLL ResizeBy : public ActionInterval 
+{
+public:
+    /**
+    * Creates the action.
+    *
+    * @param duration Duration time, in seconds.
+    * @param deltaSize The delta size.
+    * @return An autoreleased ResizeBy object.
+    */
+    static ResizeBy* create(float duration, const cocos2d::Size& deltaSize);
+    
+    //
+    // Overrides
+    //
+    virtual ResizeBy* clone() const override;
+    virtual ResizeBy* reverse() const  override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+    * @param time in seconds
+    */
+    virtual void update(float time) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    ResizeBy() {}
+    virtual ~ResizeBy() {}
+    
+    /** initializes the action */
+    bool initWithDuration(float duration, const cocos2d::Size& deltaSize);
+
+protected:
+    cocos2d::Size _sizeDelta;
+    cocos2d::Size _startSize;
+    cocos2d::Size _previousSize;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ResizeBy);
+};
+
+
+/** @class JumpBy
+ * @brief Moves a Node object simulating a parabolic jump movement by modifying it's position attribute.
+*/
+class CC_DLL JumpBy : public ActionInterval
+{
+public:
+    /** 
+     * Creates the action.
+     * @param duration Duration time, in seconds.
+     * @param position The jumping distance.
+     * @param height The jumping height.
+     * @param jumps The jumping times.
+     * @return An autoreleased JumpBy object.
+     */
+    static JumpBy* create(float duration, const Vec2& position, float height, int jumps);
+
+    //
+    // Overrides
+    //
+    virtual JumpBy* clone() const override;
+    virtual JumpBy* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    JumpBy() {}
+    virtual ~JumpBy() {}
+
+    /** 
+     * initializes the action
+     * @param duration in seconds
+     */
+    bool initWithDuration(float duration, const Vec2& position, float height, int jumps);
+
+protected:
+    Vec2           _startPosition;
+    Vec2           _delta;
+    float           _height;
+    int             _jumps;
+    Vec2           _previousPos;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(JumpBy);
+};
+
+/** @class JumpTo
+ * @brief Moves a Node object to a parabolic position simulating a jump movement by modifying it's position attribute.
+*/ 
+class CC_DLL JumpTo : public JumpBy
+{
+public:
+    /** 
+     * Creates the action.
+     * @param duration Duration time, in seconds.
+     * @param position The jumping destination position.
+     * @param height The jumping height.
+     * @param jumps The jumping times.
+     * @return An autoreleased JumpTo object.
+     */
+    static JumpTo* create(float duration, const Vec2& position, float height, int jumps);
+
+    //
+    // Override
+    //
+    virtual void startWithTarget(Node *target) override;
+    virtual JumpTo* clone() const override;
+    virtual JumpTo* reverse() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    JumpTo() {}
+    virtual ~JumpTo() {}
+
+    /** 
+     * initializes the action
+     * @param duration In seconds.
+     */
+    bool initWithDuration(float duration, const Vec2& position, float height, int jumps);
+
+protected:
+    Vec2 _endPosition;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(JumpTo);
+};
+
+/** @struct Bezier configuration structure
+ */
+typedef struct _ccBezierConfig {
+    //! end position of the bezier
+    Vec2 endPosition;
+    //! Bezier control point 1
+    Vec2 controlPoint_1;
+    //! Bezier control point 2
+    Vec2 controlPoint_2;
+} ccBezierConfig;
+
+/** @class BezierBy
+ * @brief An action that moves the target with a cubic Bezier curve by a certain distance.
+ */
+class CC_DLL BezierBy : public ActionInterval
+{
+public:
+    /** Creates the action with a duration and a bezier configuration.
+     * @param t Duration time, in seconds.
+     * @param c Bezier config.
+     * @return An autoreleased BezierBy object.
+     * @code
+     * When this function bound to js or lua,the input params are changed.
+     * in js: var create(var t,var table)
+     * in lua: local create(local t, local table)
+     * @endcode
+     */
+    static BezierBy* create(float t, const ccBezierConfig& c);
+
+    //
+    // Overrides
+    //
+    virtual BezierBy* clone() const override;
+    virtual BezierBy* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    BezierBy() {}
+    virtual ~BezierBy() {}
+
+    /** 
+     * initializes the action with a duration and a bezier configuration
+     * @param t in seconds
+     */
+    bool initWithDuration(float t, const ccBezierConfig& c);
+
+protected:
+    ccBezierConfig _config;
+    Vec2 _startPosition;
+    Vec2 _previousPosition;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(BezierBy);
+};
+
+/** @class BezierTo
+ * @brief An action that moves the target with a cubic Bezier curve to a destination point.
+ @since v0.8.2
+ */
+class CC_DLL BezierTo : public BezierBy
+{
+public:
+    /** Creates the action with a duration and a bezier configuration.
+     * @param t Duration time, in seconds.
+     * @param c Bezier config.
+     * @return An autoreleased BezierTo object.
+     * @code
+     * when this function bound to js or lua,the input params are changed
+     * in js: var create(var t,var table)
+     * in lua: local create(local t, local table)
+     * @endcode
+     */
+    static BezierTo* create(float t, const ccBezierConfig& c);
+
+    //
+    // Overrides
+    //
+    virtual void startWithTarget(Node *target) override;
+    virtual BezierTo* clone() const override;
+    virtual BezierTo* reverse() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    BezierTo() {}
+    virtual ~BezierTo() {}
+    /**
+     * @param t In seconds.
+     */
+    bool initWithDuration(float t, const ccBezierConfig &c);
+
+protected:
+    ccBezierConfig _toConfig;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(BezierTo);
+};
+
+/** @class ScaleTo
+ @brief Scales a Node object to a zoom factor by modifying it's scale attribute.
+ @warning This action doesn't support "reverse".
+ @warning The physics body contained in Node doesn't support this action.
+ */
+class CC_DLL ScaleTo : public ActionInterval
+{
+public:
+    /** 
+     * Creates the action with the same scale factor for X and Y.
+     * @param duration Duration time, in seconds.
+     * @param s Scale factor of x and y.
+     * @return An autoreleased ScaleTo object.
+     */
+    static ScaleTo* create(float duration, float s);
+
+    /** 
+     * Creates the action with and X factor and a Y factor.
+     * @param duration Duration time, in seconds.
+     * @param sx Scale factor of x.
+     * @param sy Scale factor of y.
+     * @return An autoreleased ScaleTo object.
+     */
+    static ScaleTo* create(float duration, float sx, float sy);
+
+    /** 
+     * Creates the action with X Y Z factor.
+     * @param duration Duration time, in seconds.
+     * @param sx Scale factor of x.
+     * @param sy Scale factor of y.
+     * @param sz Scale factor of z.
+     * @return An autoreleased ScaleTo object.
+     */
+    static ScaleTo* create(float duration, float sx, float sy, float sz);
+
+    //
+    // Overrides
+    //
+    virtual ScaleTo* clone() const override;
+    virtual ScaleTo* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    ScaleTo() {}
+    virtual ~ScaleTo() {}
+
+    /** 
+     * initializes the action with the same scale factor for X and Y
+     * @param duration in seconds
+     */
+    bool initWithDuration(float duration, float s);
+    /** 
+     * initializes the action with and X factor and a Y factor 
+     * @param duration in seconds
+     */
+    bool initWithDuration(float duration, float sx, float sy);
+    /** 
+     * initializes the action with X Y Z factor 
+     * @param duration in seconds
+     */
+    bool initWithDuration(float duration, float sx, float sy, float sz);
+
+protected:
+    float _scaleX;
+    float _scaleY;
+    float _scaleZ;
+    float _startScaleX;
+    float _startScaleY;
+    float _startScaleZ;
+    float _endScaleX;
+    float _endScaleY;
+    float _endScaleZ;
+    float _deltaX;
+    float _deltaY;
+    float _deltaZ;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ScaleTo);
+};
+
+/** @class ScaleBy
+ * @brief Scales a Node object a zoom factor by modifying it's scale attribute.
+ @warning The physics body contained in Node doesn't support this action.
+*/
+class CC_DLL ScaleBy : public ScaleTo
+{
+public:
+    /** 
+     * Creates the action with the same scale factor for X and Y.
+     * @param duration Duration time, in seconds.
+     * @param s Scale factor of x and y.
+     * @return An autoreleased ScaleBy object.
+     */
+    static ScaleBy* create(float duration, float s);
+
+    /** 
+     * Creates the action with and X factor and a Y factor.
+     * @param duration Duration time, in seconds.
+     * @param sx Scale factor of x.
+     * @param sy Scale factor of y.
+     * @return An autoreleased ScaleBy object.
+     */
+    static ScaleBy* create(float duration, float sx, float sy);
+
+    /** 
+     * Creates the action with X Y Z factor.
+     * @param duration Duration time, in seconds.
+     * @param sx Scale factor of x.
+     * @param sy Scale factor of y.
+     * @param sz Scale factor of z.
+     * @return An autoreleased ScaleBy object.
+     */
+    static ScaleBy* create(float duration, float sx, float sy, float sz);
+
+    //
+    // Overrides
+    //
+    virtual void startWithTarget(Node *target) override;
+    virtual ScaleBy* clone() const override;
+    virtual ScaleBy* reverse() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    ScaleBy() {}
+    virtual ~ScaleBy() {}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ScaleBy);
+};
+
+/** @class Blink
+ * @brief Blinks a Node object by modifying it's visible attribute.
+*/
+class CC_DLL Blink : public ActionInterval
+{
+public:
+    /** 
+     * Creates the action.
+     * @param duration Duration time, in seconds.
+     * @param blinks Blink times.
+     * @return An autoreleased Blink object.
+     */
+    static Blink* create(float duration, int blinks);
+
+    //
+    // Overrides
+    //
+    virtual Blink* clone() const override;
+    virtual Blink* reverse() const override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void stop() override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Blink() {}
+    virtual ~Blink() {}
+
+    /** 
+     * initializes the action 
+     * @param duration in seconds
+     */
+    bool initWithDuration(float duration, int blinks);
+    
+protected:
+    int _times;
+    bool _originalState;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Blink);
+};
+
+
+/** @class FadeTo
+ * @brief Fades an object that implements the RGBAProtocol protocol. It modifies the opacity from the current value to a custom one.
+ @warning This action doesn't support "reverse"
+ */
+class CC_DLL FadeTo : public ActionInterval
+{
+public:
+    /** 
+     * Creates an action with duration and opacity.
+     * @param duration Duration time, in seconds.
+     * @param opacity A certain opacity, the range is from 0 to 255.
+     * @return An autoreleased FadeTo object.
+     */
+    static FadeTo* create(float duration, uint8_t opacity);
+
+    //
+    // Overrides
+    //
+    virtual FadeTo* clone() const override;
+    virtual FadeTo* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    FadeTo() {}
+    virtual ~FadeTo() {}
+
+    /** 
+     * initializes the action with duration and opacity 
+     * @param duration in seconds
+     */
+    bool initWithDuration(float duration, uint8_t opacity);
+
+protected:
+    uint8_t _toOpacity;
+    uint8_t _fromOpacity;
+    friend class FadeOut;
+    friend class FadeIn;
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FadeTo);
+};
+
+/** @class FadeIn
+ * @brief Fades In an object that implements the RGBAProtocol protocol. It modifies the opacity from 0 to 255.
+ The "reverse" of this action is FadeOut
+ */
+class CC_DLL FadeIn : public FadeTo
+{
+public:
+    /** 
+     * Creates the action.
+     * @param d Duration time, in seconds.
+     * @return An autoreleased FadeIn object.
+     */
+    static FadeIn* create(float d);
+
+    //
+    // Overrides
+    //
+    virtual void startWithTarget(Node *target) override;
+    virtual FadeIn* clone() const override;
+    virtual FadeTo* reverse() const override;
+
+    /**
+     * @js NA
+     */
+    void setReverseAction(FadeTo* ac);
+
+CC_CONSTRUCTOR_ACCESS:
+    FadeIn():_reverseAction(nullptr) {}
+    virtual ~FadeIn() {}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FadeIn);
+    FadeTo* _reverseAction;
+};
+
+/** @class FadeOut
+ * @brief Fades Out an object that implements the RGBAProtocol protocol. It modifies the opacity from 255 to 0.
+ The "reverse" of this action is FadeIn
+*/
+class CC_DLL FadeOut : public FadeTo
+{
+public:
+    /** 
+     * Creates the action.
+     * @param d Duration time, in seconds.
+     */
+    static FadeOut* create(float d);
+
+    //
+    // Overrides
+    //
+    virtual void startWithTarget(Node *target) override;
+    virtual FadeOut* clone() const  override;
+    virtual FadeTo* reverse() const override;
+
+    /**
+     * @js NA
+     */
+    void setReverseAction(FadeTo* ac);
+
+CC_CONSTRUCTOR_ACCESS:
+    FadeOut():_reverseAction(nullptr) {}
+    virtual ~FadeOut() {}
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FadeOut);
+    FadeTo* _reverseAction;
+};
+
+/** @class TintTo
+ * @brief Tints a Node that implements the NodeRGB protocol from current tint to a custom one.
+ @warning This action doesn't support "reverse"
+ @since v0.7.2
+*/
+class CC_DLL TintTo : public ActionInterval
+{
+public:
+    /** 
+     * Creates an action with duration and color.
+     * @param duration Duration time, in seconds.
+     * @param red Red Color, from 0 to 255.
+     * @param green Green Color, from 0 to 255.
+     * @param blue Blue Color, from 0 to 255.
+     * @return An autoreleased TintTo object.
+     */
+    static TintTo* create(float duration, uint8_t red, uint8_t green, uint8_t blue);
+    /**
+     * Creates an action with duration and color.
+     * @param duration Duration time, in seconds.
+     * @param color It's a Color3B type.
+     * @return An autoreleased TintTo object.
+     */
+    static TintTo* create(float duration, const Color3B& color);
+
+    //
+    // Overrides
+    //
+    virtual TintTo* clone() const override;
+    virtual TintTo* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    TintTo() {}
+    virtual ~TintTo() {}
+
+    /** initializes the action with duration and color */
+    bool initWithDuration(float duration, uint8_t red, uint8_t green, uint8_t blue);
+
+protected:
+    Color3B _to;
+    Color3B _from;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(TintTo);
+};
+
+/** @class TintBy
+ @brief Tints a Node that implements the NodeRGB protocol from current tint to a custom one.
+ @since v0.7.2
+ */
+class CC_DLL TintBy : public ActionInterval
+{
+public:
+    /** 
+     * Creates an action with duration and color.
+     * @param duration Duration time, in seconds.
+     * @param deltaRed Delta red color.
+     * @param deltaGreen Delta green color.
+     * @param deltaBlue Delta blue color.
+     * @return An autoreleased TintBy object.
+     */
+    static TintBy* create(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue);
+
+    //
+    // Overrides
+    //
+    virtual TintBy* clone() const override;
+    virtual TintBy* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    TintBy() {}
+    virtual ~TintBy() {}
+
+    /** initializes the action with duration and color */
+    bool initWithDuration(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue);
+
+protected:
+    int16_t _deltaR;
+    int16_t _deltaG;
+    int16_t _deltaB;
+
+    int16_t _fromR;
+    int16_t _fromG;
+    int16_t _fromB;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(TintBy);
+};
+
+/** @class DelayTime
+ * @brief Delays the action a certain amount of seconds.
+*/
+class CC_DLL DelayTime : public ActionInterval
+{
+public:
+    /** 
+     * Creates the action.
+     * @param d Duration time, in seconds.
+     * @return An autoreleased DelayTime object.
+     */
+    static DelayTime* create(float d);
+
+    //
+    // Overrides
+    //
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    virtual DelayTime* reverse() const override;
+    virtual DelayTime* clone() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    DelayTime() {}
+    virtual ~DelayTime() {}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(DelayTime);
+};
+
+/** @class ReverseTime
+ * @brief Executes an action in reverse order, from time=duration to time=0
+ 
+ @warning Use this action carefully. This action is not
+ sequenceable. Use it as the default "reversed" method
+ of your own actions, but using it outside the "reversed"
+ scope is not recommended.
+*/
+class CC_DLL ReverseTime : public ActionInterval
+{
+public:
+    /** Creates the action.
+     *
+     * @param action a certain action.
+     * @return An autoreleased ReverseTime object.
+     */
+    static ReverseTime* create(FiniteTimeAction *action);
+
+    //
+    // Overrides
+    //
+    virtual ReverseTime* reverse() const override;
+    virtual ReverseTime* clone() const override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void stop() override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    ReverseTime();
+    virtual ~ReverseTime();
+
+    /** initializes the action */
+    bool initWithAction(FiniteTimeAction *action);
+
+protected:
+    FiniteTimeAction *_other;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ReverseTime);
+};
+
+class Texture2D;
+/** @class Animate
+ * @brief Animates a sprite given the name of an Animation.
+ */
+class CC_DLL Animate : public ActionInterval
+{
+public:
+    /** Creates the action with an Animation and will restore the original frame when the animation is over.
+     *
+     * @param animation A certain animation.
+     * @return An autoreleased Animate object.
+     */
+    static Animate* create(Animation *animation);
+
+    /** Sets the Animation object to be animated 
+     * 
+     * @param animation certain animation.
+     */
+    void setAnimation( Animation* animation );
+    /** returns the Animation object that is being animated 
+     *
+     * @return Gets the animation object that is being animated.
+     */
+    Animation* getAnimation() { return _animation; }
+    const Animation* getAnimation() const { return _animation; }
+
+    /**
+     * Gets the index of sprite frame currently displayed.
+     * @return int  the index of sprite frame currently displayed.
+     */
+    int getCurrentFrameIndex() { return _currFrameIndex; }
+    //
+    // Overrides
+    //
+    virtual Animate* clone() const override;
+    virtual Animate* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void stop() override;
+    /**
+     * @param t In seconds.
+     */
+    virtual void update(float t) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Animate();
+    virtual ~Animate();
+
+    /** initializes the action with an Animation and will restore the original frame when the animation is over */
+    bool initWithAnimation(Animation *animation);
+
+protected:
+    std::vector<float>* _splitTimes = new std::vector<float>;
+    int             _nextFrame = 0;
+    SpriteFrame*    _origFrame = nullptr;
+    int _currFrameIndex = 0;
+    unsigned int    _executedLoops = 0;
+    Animation*      _animation = nullptr;
+
+    EventCustom*    _frameDisplayedEvent = nullptr;
+    AnimationFrame::DisplayedEventInfo _frameDisplayedEventInfo;
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Animate);
+};
+
+/** @class TargetedAction
+ * @brief Overrides the target of an action so that it always runs on the target
+ * specified at action creation rather than the one specified by runAction.
+ */
+class CC_DLL TargetedAction : public ActionInterval
+{
+public:
+    /** Create an action with the specified action and forced target.
+     * 
+     * @param target The target needs to override.
+     * @param action The action needs to override.
+     * @return An autoreleased TargetedAction object.
+     */
+    static TargetedAction* create(Node* target, FiniteTimeAction* action);
+
+    /** Sets the target that the action will be forced to run with.
+     *
+     * @param forcedTarget The target that the action will be forced to run with.
+     */
+    void setForcedTarget(Node* forcedTarget);
+    /** returns the target that the action is forced to run with. 
+     *
+     * @return The target that the action is forced to run with.
+     */
+    Node* getForcedTarget() { return _forcedTarget; }
+    const Node* getForcedTarget() const { return _forcedTarget; }
+
+    //
+    // Overrides
+    //
+    virtual TargetedAction* clone() const override;
+    virtual TargetedAction* reverse() const  override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void stop() override;
+    /**
+     * @param time In seconds.
+     */
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    TargetedAction();
+    virtual ~TargetedAction();
+
+    /** Init an action with the specified action and forced target */
+    bool initWithTarget(Node* target, FiniteTimeAction* action);
+
+protected:
+    FiniteTimeAction* _action;
+    Node* _forcedTarget;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(TargetedAction);
+};
+
+/**
+ * @class ActionFloat
+ * @brief Action used to animate any value in range [from,to] over specified time interval
+ */
+class CC_DLL ActionFloat : public ActionInterval
+{
+public:
+    /**
+     *  Callback function used to report back result
+     */
+    typedef std::function<void(float value)> ActionFloatCallback;
+
+    /**
+     * Creates FloatAction with specified duration, from value, to value and callback to report back
+     * results
+     * @param duration of the action
+     * @param from value to start from
+     * @param to value to be at the end of the action
+     * @param callback to report back result
+     *
+     * @return An autoreleased ActionFloat object
+     */
+    static ActionFloat* create(float duration, float from, float to, ActionFloatCallback callback);
+
+    /**
+     * Overridden ActionInterval methods
+     */
+    void startWithTarget(Node* target) override;
+    void update(float delta) override;
+    ActionFloat* reverse() const override;
+    ActionFloat* clone() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    ActionFloat() {};
+    virtual ~ActionFloat() {};
+
+    bool initWithDuration(float duration, float from, float to, ActionFloatCallback callback);
+
+protected:
+    /* From value */
+    float _from;
+    /* To value */
+    float _to;
+    /* delta time */
+    float _delta;
+
+    /* Callback to report back results */
+    ActionFloatCallback _callback;
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ActionFloat);
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif //__ACTION_CCINTERVAL_ACTION_H__

+ 504 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionManager.cpp

@@ -0,0 +1,504 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2009      Valentin Milea
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCActionManager.h"
+#include "2d/CCNode.h"
+#include "2d/CCAction.h"
+#include "base/CCScheduler.h"
+#include "base/ccMacros.h"
+#include "base/ccCArray.h"
+#include "base/uthash.h"
+
+NS_CC_BEGIN
+//
+// singleton stuff
+//
+typedef struct _hashElement
+{
+    struct _ccArray     *actions;
+    Node                *target;
+    int                 actionIndex;
+    Action              *currentAction;
+    bool                currentActionSalvaged;
+    bool                paused;
+    UT_hash_handle      hh;
+} tHashElement;
+
+ActionManager::ActionManager()
+: _targets(nullptr),
+  _currentTarget(nullptr),
+  _currentTargetSalvaged(false)
+{
+
+}
+
+ActionManager::~ActionManager()
+{
+    CCLOGINFO("deallocing ActionManager: %p", this);
+
+    removeAllActions();
+}
+
+// private
+
+void ActionManager::deleteHashElement(tHashElement *element)
+{
+    ccArrayFree(element->actions);
+    HASH_DEL(_targets, element);
+    element->target->release();
+    free(element);
+}
+
+void ActionManager::actionAllocWithHashElement(tHashElement *element)
+{
+    // 4 actions per Node by default
+    if (element->actions == nullptr)
+    {
+        element->actions = ccArrayNew(4);
+    }else 
+    if (element->actions->num == element->actions->max)
+    {
+        ccArrayDoubleCapacity(element->actions);
+    }
+
+}
+
+void ActionManager::removeActionAtIndex(ssize_t index, tHashElement *element)
+{
+    Action *action = static_cast<Action*>(element->actions->arr[index]);
+
+    if (action == element->currentAction && (! element->currentActionSalvaged))
+    {
+        element->currentAction->retain();
+        element->currentActionSalvaged = true;
+    }
+
+    ccArrayRemoveObjectAtIndex(element->actions, index, true);
+
+    // update actionIndex in case we are in tick. looping over the actions
+    if (element->actionIndex >= index)
+    {
+        element->actionIndex--;
+    }
+
+    if (element->actions->num == 0)
+    {
+        if (_currentTarget == element)
+        {
+            _currentTargetSalvaged = true;
+        }
+        else
+        {
+            deleteHashElement(element);
+        }
+    }
+}
+
+// pause / resume
+
+void ActionManager::pauseTarget(Node *target)
+{
+    tHashElement *element = nullptr;
+    HASH_FIND_PTR(_targets, &target, element);
+    if (element)
+    {
+        element->paused = true;
+    }
+}
+
+void ActionManager::resumeTarget(Node *target)
+{
+    tHashElement *element = nullptr;
+    HASH_FIND_PTR(_targets, &target, element);
+    if (element)
+    {
+        element->paused = false;
+    }
+}
+
+Vector<Node*> ActionManager::pauseAllRunningActions()
+{
+    Vector<Node*> idsWithActions;
+    
+    for (tHashElement *element=_targets; element != nullptr; element = (tHashElement *)element->hh.next) 
+    {
+        if (! element->paused) 
+        {
+            element->paused = true;
+            idsWithActions.pushBack(element->target);
+        }
+    }    
+    
+    return idsWithActions;
+}
+
+void ActionManager::resumeTargets(const Vector<Node*>& targetsToResume)
+{
+    for(const auto &node : targetsToResume)
+    {
+        this->resumeTarget(node);
+    }
+}
+
+// run
+
+void ActionManager::addAction(Action *action, Node *target, bool paused)
+{
+    CCASSERT(action != nullptr, "action can't be nullptr!");
+    CCASSERT(target != nullptr, "target can't be nullptr!");
+    if(action == nullptr || target == nullptr)
+        return;
+
+    tHashElement *element = nullptr;
+    // we should convert it to Ref*, because we save it as Ref*
+    Ref *tmp = target;
+    HASH_FIND_PTR(_targets, &tmp, element);
+    if (! element)
+    {
+        element = (tHashElement*)calloc(sizeof(*element), 1);
+        element->paused = paused;
+        target->retain();
+        element->target = target;
+        HASH_ADD_PTR(_targets, target, element);
+    }
+
+     actionAllocWithHashElement(element);
+ 
+     CCASSERT(! ccArrayContainsObject(element->actions, action), "action already be added!");
+     ccArrayAppendObject(element->actions, action);
+ 
+     action->startWithTarget(target);
+}
+
+// remove
+
+void ActionManager::removeAllActions()
+{
+    for (tHashElement *element = _targets; element != nullptr; )
+    {
+        auto target = element->target;
+        element = (tHashElement*)element->hh.next;
+        removeAllActionsFromTarget(target);
+    }
+}
+
+void ActionManager::removeAllActionsFromTarget(Node *target)
+{
+    // explicit null handling
+    if (target == nullptr)
+    {
+        return;
+    }
+
+    tHashElement *element = nullptr;
+    HASH_FIND_PTR(_targets, &target, element);
+    if (element)
+    {
+        if (ccArrayContainsObject(element->actions, element->currentAction) && (! element->currentActionSalvaged))
+        {
+            element->currentAction->retain();
+            element->currentActionSalvaged = true;
+        }
+
+        ccArrayRemoveAllObjects(element->actions);
+        if (_currentTarget == element)
+        {
+            _currentTargetSalvaged = true;
+        }
+        else
+        {
+            deleteHashElement(element);
+        }
+    }
+}
+
+void ActionManager::removeAction(Action *action)
+{
+    // explicit null handling
+    if (action == nullptr)
+    {
+        return;
+    }
+
+    tHashElement *element = nullptr;
+    Ref *target = action->getOriginalTarget();
+    HASH_FIND_PTR(_targets, &target, element);
+    if (element)
+    {
+        auto i = ccArrayGetIndexOfObject(element->actions, action);
+        if (i != CC_INVALID_INDEX)
+        {
+            removeActionAtIndex(i, element);
+        }
+    }
+}
+
+void ActionManager::removeActionByTag(int tag, Node *target)
+{
+    CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
+    CCASSERT(target != nullptr, "target can't be nullptr!");
+    if (target == nullptr)
+    {
+        return;
+    }
+
+    tHashElement *element = nullptr;
+    HASH_FIND_PTR(_targets, &target, element);
+
+    if (element)
+    {
+        auto limit = element->actions->num;
+        for (int i = 0; i < limit; ++i)
+        {
+            Action *action = static_cast<Action*>(element->actions->arr[i]);
+
+            if (action->getTag() == (int)tag && action->getOriginalTarget() == target)
+            {
+                removeActionAtIndex(i, element);
+                break;
+            }
+        }
+    }
+}
+
+void ActionManager::removeAllActionsByTag(int tag, Node *target)
+{
+    CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
+    CCASSERT(target != nullptr, "target can't be nullptr!");
+    if (target == nullptr)
+    {
+        return;
+    }
+    
+    tHashElement *element = nullptr;
+    HASH_FIND_PTR(_targets, &target, element);
+    
+    if (element)
+    {
+        auto limit = element->actions->num;
+        for (int i = 0; i < limit;)
+        {
+            Action *action = static_cast<Action*>(element->actions->arr[i]);
+
+            if (action->getTag() == (int)tag && action->getOriginalTarget() == target)
+            {
+                removeActionAtIndex(i, element);
+                --limit;
+            }
+            else
+            {
+                ++i;
+            }
+        }
+    }
+}
+
+void ActionManager::removeActionsByFlags(unsigned int flags, Node *target)
+{
+    if (flags == 0)
+    {
+        return;
+    }
+    CCASSERT(target != nullptr, "target can't be nullptr!");
+    if (target == nullptr)
+    {
+        return;
+    }
+
+    tHashElement *element = nullptr;
+    HASH_FIND_PTR(_targets, &target, element);
+
+    if (element)
+    {
+        auto limit = element->actions->num;
+        for (int i = 0; i < limit;)
+        {
+            Action *action = static_cast<Action*>(element->actions->arr[i]);
+
+            if ((action->getFlags() & flags) != 0 && action->getOriginalTarget() == target)
+            {
+                removeActionAtIndex(i, element);
+                --limit;
+            }
+            else
+            {
+                ++i;
+            }
+        }
+    }
+}
+
+// get
+
+// FIXME: Passing "const O *" instead of "const O&" because HASH_FIND_IT requires the address of a pointer
+// and, it is not possible to get the address of a reference
+Action* ActionManager::getActionByTag(int tag, const Node *target) const
+{
+    CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
+
+    tHashElement *element = nullptr;
+    HASH_FIND_PTR(_targets, &target, element);
+
+    if (element)
+    {
+        if (element->actions != nullptr)
+        {
+            auto limit = element->actions->num;
+            for (int i = 0; i < limit; ++i)
+            {
+                Action *action = static_cast<Action*>(element->actions->arr[i]);
+
+                if (action->getTag() == (int)tag)
+                {
+                    return action;
+                }
+            }
+        }
+    }
+
+    return nullptr;
+}
+
+// FIXME: Passing "const O *" instead of "const O&" because HASH_FIND_IT requires the address of a pointer
+// and, it is not possible to get the address of a reference
+ssize_t ActionManager::getNumberOfRunningActionsInTarget(const Node *target) const
+{
+    tHashElement *element = nullptr;
+    HASH_FIND_PTR(_targets, &target, element);
+    if (element)
+    {
+        return element->actions ? element->actions->num : 0;
+    }
+
+    return 0;
+}
+
+// FIXME: Passing "const O *" instead of "const O&" because HASH_FIND_IT requires the address of a pointer
+// and, it is not possible to get the address of a reference
+size_t ActionManager::getNumberOfRunningActionsInTargetByTag(const Node *target,
+                                                             int tag)
+{
+    CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
+
+    tHashElement *element = nullptr;
+    HASH_FIND_PTR(_targets, &target, element);
+
+    if(!element || !element->actions)
+        return 0;
+
+    int count = 0;
+    auto limit = element->actions->num;
+    for(int i = 0; i < limit; ++i)
+    {
+        auto action = static_cast<Action*>(element->actions->arr[i]);
+        if(action->getTag() == tag)
+            ++count;
+    }
+
+    return count;
+}
+
+ssize_t ActionManager::getNumberOfRunningActions() const
+{
+    ssize_t count = 0;
+    struct _hashElement* element = nullptr;
+    struct _hashElement* tmp = nullptr;
+    HASH_ITER(hh, _targets, element, tmp)
+    {
+        count += (element->actions ? element->actions->num : 0);
+    }
+    return count;
+}
+
+// main loop
+void ActionManager::update(float dt)
+{
+    for (tHashElement *elt = _targets; elt != nullptr; )
+    {
+        _currentTarget = elt;
+        _currentTargetSalvaged = false;
+
+        if (! _currentTarget->paused)
+        {
+            // The 'actions' MutableArray may change while inside this loop.
+            for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
+                _currentTarget->actionIndex++)
+            {
+                _currentTarget->currentAction = static_cast<Action*>(_currentTarget->actions->arr[_currentTarget->actionIndex]);
+                if (_currentTarget->currentAction == nullptr)
+                {
+                    continue;
+                }
+
+                _currentTarget->currentActionSalvaged = false;
+
+                _currentTarget->currentAction->step(dt);
+
+                if (_currentTarget->currentActionSalvaged)
+                {
+                    // The currentAction told the node to remove it. To prevent the action from
+                    // accidentally deallocating itself before finishing its step, we retained
+                    // it. Now that step is done, it's safe to release it.
+                    _currentTarget->currentAction->release();
+                } else
+                if (_currentTarget->currentAction->isDone())
+                {
+                    _currentTarget->currentAction->stop();
+
+                    Action *action = _currentTarget->currentAction;
+                    // Make currentAction nil to prevent removeAction from salvaging it.
+                    _currentTarget->currentAction = nullptr;
+                    removeAction(action);
+                }
+
+                _currentTarget->currentAction = nullptr;
+            }
+        }
+
+        // elt, at this moment, is still valid
+        // so it is safe to ask this here (issue #490)
+        elt = (tHashElement*)(elt->hh.next);
+
+        // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
+        if (_currentTargetSalvaged && _currentTarget->actions->num == 0)
+        {
+            deleteHashElement(_currentTarget);
+        }
+        //if some node reference 'target', it's reference count >= 2 (issues #14050)
+        else if (_currentTarget->target->getReferenceCount() == 1)
+        {
+            deleteHashElement(_currentTarget);
+        }
+    }
+
+    // issue #635
+    _currentTarget = nullptr;
+}
+
+NS_CC_END

+ 216 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionManager.h

@@ -0,0 +1,216 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2009      Valentin Milea
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef __ACTION_CCACTION_MANAGER_H__
+#define __ACTION_CCACTION_MANAGER_H__
+
+#include "2d/CCAction.h"
+#include "base/CCVector.h"
+#include "base/CCRef.h"
+
+NS_CC_BEGIN
+
+class Action;
+
+struct _hashElement;
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/** @class ActionManager
+ @brief ActionManager is a singleton that manages all the actions.
+ Normally you won't need to use this singleton directly. 99% of the cases you will use the Node interface,
+ which uses this singleton.
+ But there are some cases where you might need to use this singleton.
+ Examples:
+    - When you want to run an action where the target is different from a Node. 
+    - When you want to pause / resume the actions.
+ 
+ @since v0.8
+ */
+class CC_DLL ActionManager : public Ref
+{
+public:
+    /**
+     * @js ctor
+     */
+    ActionManager();
+
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ActionManager();
+
+    // actions
+    
+    /** Adds an action with a target. 
+     If the target is already present, then the action will be added to the existing target.
+     If the target is not present, a new instance of this target will be created either paused or not, and the action will be added to the newly created target.
+     When the target is paused, the queued actions won't be 'ticked'.
+     *
+     * @param action    A certain action.
+     * @param target    The target which need to be added an action.
+     * @param paused    Is the target paused or not.
+     */
+    virtual void addAction(Action *action, Node *target, bool paused);
+
+    /** Removes all actions from all the targets.
+     */
+    virtual void removeAllActions();
+
+    /** Removes all actions from a certain target.
+     All the actions that belongs to the target will be removed.
+     *
+     * @param target    A certain target.
+     */
+    virtual void removeAllActionsFromTarget(Node *target);
+
+    /** Removes an action given an action reference.
+     *
+     * @param action    A certain target.
+     */
+    virtual void removeAction(Action *action);
+
+    /** Removes an action given its tag and the target.
+     *
+     * @param tag       The action's tag.
+     * @param target    A certain target.
+     */
+    virtual void removeActionByTag(int tag, Node *target);
+    
+    /** Removes all actions given its tag and the target.
+     *
+     * @param tag       The actions' tag.
+     * @param target    A certain target.
+     * @js NA
+     */
+    virtual void removeAllActionsByTag(int tag, Node *target);
+
+    /** Removes all actions matching at least one bit in flags and the target.
+     *
+     * @param flags     The flag field to match the actions' flags based on bitwise AND.
+     * @param target    A certain target.
+     * @js NA
+     */
+    virtual void removeActionsByFlags(unsigned int flags, Node *target);
+
+    /** Gets an action given its tag an a target.
+     *
+     * @param tag       The action's tag.
+     * @param target    A certain target.
+     * @return  The Action the with the given tag.
+     */
+    virtual Action* getActionByTag(int tag, const Node *target) const;
+
+    /** Returns the numbers of actions that are running in a certain target. 
+     * Composable actions are counted as 1 action. Example:
+     * - If you are running 1 Sequence of 7 actions, it will return 1.
+     * - If you are running 7 Sequences of 2 actions, it will return 7.
+     *
+     * @param target    A certain target.
+     * @return  The numbers of actions that are running in a certain target.
+     * @js NA
+     */
+    virtual ssize_t getNumberOfRunningActionsInTarget(const Node *target) const;
+    
+    /** Returns the numbers of actions that are running in all targets.
+     * @return  The numbers of actions that are running in all target.
+     * @js NA
+     */
+    virtual ssize_t getNumberOfRunningActions() const;
+
+
+    /** Returns the numbers of actions that are running in a
+     *  certain target with a specific tag.
+     * Like getNumberOfRunningActionsInTarget Composable actions
+     * are counted as 1 action. Example:
+     * - If you are running 1 Sequence of 7 actions, it will return 1.
+     * - If you are running 7 Sequences of 2 actions, it will return 7.
+     *
+     * @param target    A certain target.
+     * @param tag       Tag that will be searched.
+     * @return  The numbers of actions that are running in a certain target
+     *          with a specific tag.
+     * @see getNumberOfRunningActionsInTarget
+     * @js NA
+     */
+    virtual size_t getNumberOfRunningActionsInTargetByTag(const Node *target, int tag);
+
+
+    /** Pauses the target: all running actions and newly added actions will be paused.
+     *
+     * @param target    A certain target.
+     */
+    virtual void pauseTarget(Node *target);
+
+    /** Resumes the target. All queued actions will be resumed.
+     *
+     * @param target    A certain target.
+     */
+    virtual void resumeTarget(Node *target);
+    
+    /** Pauses all running actions, returning a list of targets whose actions were paused.
+     *
+     * @return  A list of targets whose actions were paused.
+     */
+    virtual Vector<Node*> pauseAllRunningActions();
+    
+    /** Resume a set of targets (convenience function to reverse a pauseAllRunningActions call).
+     *
+     * @param targetsToResume   A set of targets need to be resumed.
+     */
+    virtual void resumeTargets(const Vector<Node*>& targetsToResume);
+    
+    /** Main loop of ActionManager.
+     * @param dt    In seconds.
+     */
+    virtual void update(float dt);
+    
+protected:
+    // declared in ActionManager.m
+
+    void removeActionAtIndex(ssize_t index, struct _hashElement *element);
+    void deleteHashElement(struct _hashElement *element);
+    void actionAllocWithHashElement(struct _hashElement *element);
+
+protected:
+    struct _hashElement    *_targets;
+    struct _hashElement    *_currentTarget;
+    bool            _currentTargetSalvaged;
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif // __ACTION_CCACTION_MANAGER_H__

+ 134 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionPageTurn3D.cpp

@@ -0,0 +1,134 @@
+/****************************************************************************
+Copyright (c) 2009      Sindesso Pty Ltd http://www.sindesso.com/
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCActionPageTurn3D.h"
+#include "2d/CCGrid.h"
+#include "2d/CCNodeGrid.h"
+
+NS_CC_BEGIN
+
+PageTurn3D* PageTurn3D::create(float duration, const Size& gridSize)
+{
+    PageTurn3D *action = new (std::nothrow) PageTurn3D();
+
+    if (action && action->initWithDuration(duration, gridSize))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+PageTurn3D *PageTurn3D::clone() const
+{
+    // no copy constructor
+    return PageTurn3D::create(_duration, _gridSize);
+}
+
+GridBase* PageTurn3D::getGrid()
+{
+    auto result = Grid3D::create(_gridSize, _gridNodeTarget->getGridRect());
+    if (result)
+    {
+        result->setNeedDepthTestForBlit(true);
+    }
+    
+    return result;
+}
+
+/*
+ * Update each tick
+ * Time is the percentage of the way through the duration
+ */
+void PageTurn3D::update(float time)
+{
+    float tt = MAX(0, time - 0.25f);
+    float deltaAy = (tt * tt * 500);
+    float ay = -100 - deltaAy;
+    
+    float deltaTheta = sqrtf(time);
+    float theta = deltaTheta > 0.5f ? (float)M_PI_2*deltaTheta : (float)M_PI_2*(1-deltaTheta);
+    
+    float rotateByYAxis = (2-time)* (float)M_PI;
+    
+    float sinTheta = sinf(theta);
+    float cosTheta = cosf(theta);
+    
+    for (int i = 0; i <= _gridSize.width; ++i)
+    {
+        for (int j = 0; j <= _gridSize.height; ++j)
+        {
+            Vec2 pos((float)i, (float)j);
+            // Get original vertex
+            Vec3 p = getOriginalVertex(pos);
+            
+            p.x -= getGridRect().origin.x;
+            float R = sqrtf((p.x * p.x) + ((p.y - ay) * (p.y - ay)));
+            float r = R * sinTheta;
+            float alpha = asinf( p.x / R );
+            float beta = alpha / sinTheta;
+            float cosBeta = cosf( beta );
+            
+            // If beta > PI then we've wrapped around the cone
+            // Reduce the radius to stop these points interfering with others
+            if (beta <= M_PI)
+            {
+                p.x = ( r * sinf(beta));
+            }
+            else
+            {
+                // Force X = 0 to stop wrapped
+                // points
+                p.x = 0;
+            }
+
+            p.y = ( R + ay - ( r * (1 - cosBeta) * sinTheta));
+
+            // We scale z here to avoid the animation being
+            // too much bigger than the screen due to perspective transform
+
+            p.z = (r * ( 1 - cosBeta ) * cosTheta);// "100" didn't work for
+            p.x = p.z * sinf(rotateByYAxis) + p.x * cosf(rotateByYAxis);
+            p.z = p.z * cosf(rotateByYAxis) - p.x * sinf(rotateByYAxis);
+            p.z/=7;
+            //    Stop z coord from dropping beneath underlying page in a transition
+            // issue #751
+            if( p.z < 0.5f )
+            {
+                p.z = 0.5f;
+            }
+            
+            // Set new coords
+            p.x += getGridRect().origin.x;
+            setVertex(pos, p);
+            
+        }
+    }
+}
+
+NS_CC_END

+ 74 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionPageTurn3D.h

@@ -0,0 +1,74 @@
+/****************************************************************************
+Copyright (c) 2009      Sindesso Pty Ltd http://www.sindesso.com/
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __ACTION_CCPAGETURN3D_ACTION_H__
+#define __ACTION_CCPAGETURN3D_ACTION_H__
+
+#include "2d/CCActionGrid3D.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/**
+ @brief This action simulates a page turn from the bottom right hand corner of the screen.
+ 
+ @details It's not much use by itself but is used by the PageTurnTransition.
+         Based on an original paper by L Hong et al.
+         http://www.parc.com/publication/1638/turning-pages-of-3d-electronic-books.html
+  
+ @since v0.8.2
+ */
+class CC_DLL PageTurn3D : public Grid3DAction
+{
+public:
+    /**
+     * @js NA 
+     */
+    virtual GridBase* getGrid() override;
+
+    /**
+    @brief Create an action with duration, grid size.
+    @param duration Specify the duration of the PageTurn3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @return If the creation success, return a pointer of PageTurn3D action; otherwise, return nil.
+    */
+    static PageTurn3D* create(float duration, const Size& gridSize);
+
+    // Overrides
+    virtual PageTurn3D* clone() const override;
+    virtual void update(float time) override;
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif // __ACTION_CCPAGETURN3D_ACTION_H__

+ 133 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionProgressTimer.cpp

@@ -0,0 +1,133 @@
+/****************************************************************************
+Copyright (C) 2010      Lam Pham
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCActionProgressTimer.h"
+#include "2d/CCProgressTimer.h"
+
+NS_CC_BEGIN
+
+#define kProgressTimerCast ProgressTimer*
+
+// implementation of ProgressTo
+
+ProgressTo* ProgressTo::create(float duration, float percent)
+{
+    ProgressTo *progressTo = new (std::nothrow) ProgressTo();
+    if (progressTo && progressTo->initWithDuration(duration, percent))
+    {
+        progressTo->autorelease();
+        return progressTo;
+    }
+    
+    delete progressTo;
+    return nullptr;
+}
+
+bool ProgressTo::initWithDuration(float duration, float percent)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _to = percent;
+
+        return true;
+    }
+
+    return false;
+}
+
+ProgressTo* ProgressTo::clone() const
+{
+    // no copy constructor
+    return ProgressTo::create(_duration, _to);
+}
+
+ProgressTo* ProgressTo::reverse() const
+{
+    CCASSERT(false, "reverse() not supported in ProgressTo");
+    return nullptr;
+}
+
+void ProgressTo::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+    _from = ((kProgressTimerCast)(target))->getPercentage();
+}
+
+void ProgressTo::update(float time)
+{
+    ((kProgressTimerCast)(_target))->setPercentage(_from + (_to - _from) * time);
+}
+
+// implementation of ProgressFromTo
+
+ProgressFromTo* ProgressFromTo::create(float duration, float fromPercentage, float toPercentage)
+{
+    ProgressFromTo *progressFromTo = new (std::nothrow) ProgressFromTo();
+    if (progressFromTo && progressFromTo->initWithDuration(duration, fromPercentage, toPercentage)) {
+        progressFromTo->autorelease();
+        return progressFromTo;
+    }
+    
+    delete progressFromTo;
+    return nullptr;
+}
+
+bool ProgressFromTo::initWithDuration(float duration, float fromPercentage, float toPercentage)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _to = toPercentage;
+        _from = fromPercentage;
+
+        return true;
+    }
+
+    return false;
+}
+
+ProgressFromTo* ProgressFromTo::clone() const
+{
+    // no copy constructor
+    return ProgressFromTo::create(_duration, _from, _to);
+}
+
+
+ProgressFromTo* ProgressFromTo::reverse() const
+{
+    return ProgressFromTo::create(_duration, _to, _from);
+}
+
+void ProgressFromTo::startWithTarget(Node *target)
+{
+    ActionInterval::startWithTarget(target);
+}
+
+void ProgressFromTo::update(float time)
+{
+    ((kProgressTimerCast)(_target))->setPercentage(_from + (_to - _from) * time);
+}
+
+NS_CC_END

+ 134 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionProgressTimer.h

@@ -0,0 +1,134 @@
+/****************************************************************************
+Copyright (C) 2010      Lam Pham
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __ACTION_CCPROGRESS_TIMER_H__
+#define __ACTION_CCPROGRESS_TIMER_H__
+
+#include "2d/CCActionInterval.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/**
+@brief Progress to percentage.
+@details This action show the target node from current percentage to the specified percentage.
+        You should specify the destination percentage when creating the action.
+@since v0.99.1
+*/
+class CC_DLL ProgressTo : public ActionInterval
+{
+public:
+    /** 
+     * @brief Create and initializes with a duration and a destination percentage.
+     * @param duration Specify the duration of the ProgressTo action. It's a value in seconds.
+     * @param percent Specify the destination percentage.
+     * @return If the creation success, return a pointer of ProgressTo action; otherwise, return nil.
+     */
+    static ProgressTo* create(float duration, float percent);
+
+    //
+    // Overrides
+    //
+    virtual ProgressTo* clone() const override;
+    virtual ProgressTo* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    ProgressTo() {}
+    virtual ~ProgressTo() {}
+
+    /** 
+     * @brief Initializes with a duration and destination percentage. 
+     * @param duration Specify the duration of the ProgressTo action. It's a value in seconds.
+     * @param percent Specify the destination percentage.
+     * @return If the creation success, return true; otherwise, return false.
+     */
+    bool initWithDuration(float duration, float percent);
+
+protected:
+    float _to;
+    float _from;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ProgressTo);
+};
+
+/**
+@brief Progress from a percentage to another percentage.
+@since v0.99.1
+*/
+class CC_DLL ProgressFromTo : public ActionInterval
+{
+public:
+    /** 
+     * @brief Create and initializes the action with a duration, a "from" percentage and a "to" percentage.
+     * @param duration Specify the duration of the ProgressFromTo action. It's a value in seconds.
+     * @param fromPercentage Specify the source percentage.
+     * @param toPercentage Specify the destination percentage.
+     * @return If the creation success, return a pointer of ProgressFromTo action; otherwise, return nil.
+     */
+    static ProgressFromTo* create(float duration, float fromPercentage, float toPercentage);
+
+    //
+    // Overrides
+    //
+    virtual ProgressFromTo* clone() const override;
+    virtual ProgressFromTo* reverse() const override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    ProgressFromTo() {}
+    virtual ~ProgressFromTo() {}
+
+    /**
+     * @brief Initializes the action with a duration, a "from" percentage and a "to" percentage.
+     * @param duration Specify the duration of the ProgressFromTo action. It's a value in seconds.
+     * @param fromPercentage Specify the source percentage.
+     * @param toPercentage Specify the destination percentage.
+     * @return If the creation success, return true; otherwise, return false.
+     */
+    bool initWithDuration(float duration, float fromPercentage, float toPercentage);
+
+protected:
+    float _to;
+    float _from;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ProgressFromTo);
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif // __ACTION_CCPROGRESS_TIMER_H__

+ 879 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionTiledGrid.cpp

@@ -0,0 +1,879 @@
+/****************************************************************************
+Copyright (c) 2009      On-Core
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCActionTiledGrid.h"
+#include "2d/CCGrid.h"
+#include "2d/CCNodeGrid.h"
+#include "base/CCDirector.h"
+#include "base/ccMacros.h"
+
+NS_CC_BEGIN
+
+struct Tile
+{
+    Vec2    position;
+    Vec2    startPosition;
+    Size    delta;
+};
+
+// implementation of ShakyTiles3D
+
+ShakyTiles3D* ShakyTiles3D::create(float duration, const Size& gridSize, int range, bool shakeZ)
+{
+    ShakyTiles3D *action = new (std::nothrow) ShakyTiles3D();
+
+    if (action && action->initWithDuration(duration, gridSize, range, shakeZ))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool ShakyTiles3D::initWithDuration(float duration, const Size& gridSize, int range, bool shakeZ)
+{
+    if (TiledGrid3DAction::initWithDuration(duration, gridSize))
+    {
+        _randrange = range;
+        _shakeZ = shakeZ;
+
+        return true;
+    }
+
+    return false;
+}
+
+ShakyTiles3D* ShakyTiles3D::clone() const
+{
+    // no copy constructor
+    return ShakyTiles3D::create(_duration, _gridSize, _randrange, _shakeZ);
+}
+
+void ShakyTiles3D::update(float /*time*/)
+{
+    int i, j;
+
+    for (i = 0; i < _gridSize.width; ++i)
+    {
+        for (j = 0; j < _gridSize.height; ++j)
+        {
+            Vec2 pos((float)i, (float)j);
+            Quad3 coords = getOriginalTile(pos);
+
+            // X
+            coords.bl.x += ( rand() % (_randrange*2) ) - _randrange;
+            coords.br.x += ( rand() % (_randrange*2) ) - _randrange;
+            coords.tl.x += ( rand() % (_randrange*2) ) - _randrange;
+            coords.tr.x += ( rand() % (_randrange*2) ) - _randrange;
+
+            // Y
+            coords.bl.y += ( rand() % (_randrange*2) ) - _randrange;
+            coords.br.y += ( rand() % (_randrange*2) ) - _randrange;
+            coords.tl.y += ( rand() % (_randrange*2) ) - _randrange;
+            coords.tr.y += ( rand() % (_randrange*2) ) - _randrange;
+
+            if (_shakeZ)
+            {
+                coords.bl.z += ( rand() % (_randrange*2) ) - _randrange;
+                coords.br.z += ( rand() % (_randrange*2) ) - _randrange;
+                coords.tl.z += ( rand() % (_randrange*2) ) - _randrange;
+                coords.tr.z += ( rand() % (_randrange*2) ) - _randrange;
+            }
+                        
+            setTile(pos, coords);
+        }
+    }
+}
+
+// implementation of ShatteredTiles3D
+
+ShatteredTiles3D* ShatteredTiles3D::create(float duration, const Size& gridSize, int range, bool shatterZ)
+{
+    ShatteredTiles3D *action = new (std::nothrow) ShatteredTiles3D();
+
+    if (action && action->initWithDuration(duration, gridSize, range, shatterZ))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool ShatteredTiles3D::initWithDuration(float duration, const Size& gridSize, int range, bool shatterZ)
+{
+    if (TiledGrid3DAction::initWithDuration(duration, gridSize))
+    {
+        _once = false;
+        _randrange = range;
+        _shatterZ = shatterZ;
+
+        return true;
+    }
+
+    return false;
+}
+
+ShatteredTiles3D* ShatteredTiles3D::clone() const
+{
+    // no copy constructor
+    return ShatteredTiles3D::create(_duration, _gridSize, _randrange, _shatterZ);
+}
+
+void ShatteredTiles3D::update(float /*time*/)
+{
+    int i, j;
+
+    if (_once == false)
+    {
+        for (i = 0; i < _gridSize.width; ++i)
+        {
+            for (j = 0; j < _gridSize.height; ++j)
+            {
+                Vec2 pos((float)i, (float)j);
+                Quad3 coords = getOriginalTile(pos);
+                
+                // X
+                coords.bl.x += ( rand() % (_randrange*2) ) - _randrange;
+                coords.br.x += ( rand() % (_randrange*2) ) - _randrange;
+                coords.tl.x += ( rand() % (_randrange*2) ) - _randrange;
+                coords.tr.x += ( rand() % (_randrange*2) ) - _randrange;
+                
+                // Y
+                coords.bl.y += ( rand() % (_randrange*2) ) - _randrange;
+                coords.br.y += ( rand() % (_randrange*2) ) - _randrange;
+                coords.tl.y += ( rand() % (_randrange*2) ) - _randrange;
+                coords.tr.y += ( rand() % (_randrange*2) ) - _randrange;
+
+                if (_shatterZ) 
+                {
+                    coords.bl.z += ( rand() % (_randrange*2) ) - _randrange;
+                    coords.br.z += ( rand() % (_randrange*2) ) - _randrange;                
+                    coords.tl.z += ( rand() % (_randrange*2) ) - _randrange;
+                    coords.tr.z += ( rand() % (_randrange*2) ) - _randrange;
+                }
+                
+                setTile(pos, coords);
+            }
+        }
+        
+        _once = true;
+    }
+}
+
+// implementation of ShuffleTiles
+
+ShuffleTiles* ShuffleTiles::create(float duration, const Size& gridSize, unsigned int seed)
+{
+    ShuffleTiles *action = new (std::nothrow) ShuffleTiles();
+
+    if (action && action->initWithDuration(duration, gridSize, seed))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool ShuffleTiles::initWithDuration(float duration, const Size& gridSize, unsigned int seed)
+{
+    if (TiledGrid3DAction::initWithDuration(duration, gridSize))
+    {
+        _seed = seed;
+        _tilesOrder = nullptr;
+        _tiles = nullptr;
+
+        return true;
+    }
+
+    return false;
+}
+
+ShuffleTiles* ShuffleTiles::clone() const
+{
+    // no copy constructor
+    return ShuffleTiles::create(_duration, _gridSize, _seed);
+}
+
+ShuffleTiles::~ShuffleTiles()
+{
+    CC_SAFE_DELETE_ARRAY(_tilesOrder);
+    CC_SAFE_DELETE_ARRAY(_tiles);
+}
+
+void ShuffleTiles::shuffle(unsigned int *array, unsigned int len)
+{
+    for (int i = len - 1; i >= 0; i-- )
+    {
+        unsigned int j = rand() % (i+1);
+        unsigned int v = array[i];
+        array[i] = array[j];
+        array[j] = v;
+    }
+}
+
+Size ShuffleTiles::getDelta(const Size& pos) const
+{
+    unsigned int idx = static_cast<unsigned int>(pos.width * _gridSize.height + pos.height);
+    Vec2    pos2;
+    pos2.x = (float)(_tilesOrder[idx] / (int)_gridSize.height);
+    pos2.y = (float)(_tilesOrder[idx] % (int)_gridSize.height);
+
+    return Size((int)(pos2.x - pos.width), (int)(pos2.y - pos.height));
+}
+
+void ShuffleTiles::placeTile(const Vec2& pos, Tile *t)
+{
+    Quad3 coords = getOriginalTile(pos);
+
+    Vec2 step = _gridNodeTarget->getGrid()->getStep();
+    coords.bl.x += (int)(t->position.x * step.x);
+    coords.bl.y += (int)(t->position.y * step.y);
+
+    coords.br.x += (int)(t->position.x * step.x);
+    coords.br.y += (int)(t->position.y * step.y);
+
+    coords.tl.x += (int)(t->position.x * step.x);
+    coords.tl.y += (int)(t->position.y * step.y);
+
+    coords.tr.x += (int)(t->position.x * step.x);
+    coords.tr.y += (int)(t->position.y * step.y);
+
+    setTile(pos, coords);
+}
+
+void ShuffleTiles::startWithTarget(Node *target)
+{
+    TiledGrid3DAction::startWithTarget(target);
+
+    if (_seed != (unsigned int)-1)
+    {
+        std::srand(_seed);
+    }
+
+    _tilesCount = (unsigned int)(_gridSize.width * _gridSize.height);
+    _tilesOrder = new unsigned int[_tilesCount];
+
+    /**
+     * Use k to loop. Because _tilesCount is unsigned int,
+     * and i is used later for int.
+     */
+    for (unsigned int k = 0; k < _tilesCount; ++k)
+    {
+        _tilesOrder[k] = k;
+    }
+
+    shuffle(_tilesOrder, _tilesCount);
+
+    _tiles = (struct Tile *)new Tile[_tilesCount];
+    Tile *tileArray = (Tile*) _tiles;
+
+    for (int i = 0; i < _gridSize.width; ++i)
+    {
+        for ( int j = 0; j < _gridSize.height; ++j)
+        {
+            tileArray->position.set((float)i, (float)j);
+            tileArray->startPosition.set((float)i, (float)j);
+            tileArray->delta = getDelta(Size((float)i, (float)j));
+            ++tileArray;
+        }
+    }
+}
+
+void ShuffleTiles::update(float time)
+{
+    Tile *tileArray = (Tile*)_tiles;
+
+    for (int i = 0; i < _gridSize.width; ++i)
+    {
+        for (int j = 0; j < _gridSize.height; ++j)
+        {
+            tileArray->position = Vec2((float)tileArray->delta.width, (float)tileArray->delta.height) * time;
+            placeTile(Vec2((float)i, (float)j), tileArray);
+            ++tileArray;
+        }
+    }
+}
+
+// implementation of FadeOutTRTiles
+
+FadeOutTRTiles* FadeOutTRTiles::create(float duration, const Size& gridSize)
+{
+    FadeOutTRTiles *action = new (std::nothrow) FadeOutTRTiles();
+
+    if (action && action->initWithDuration(duration, gridSize))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+FadeOutTRTiles* FadeOutTRTiles::clone() const
+{
+    // no copy constructor
+    return FadeOutTRTiles::create(_duration, _gridSize);
+}
+
+float FadeOutTRTiles::testFunc(const Size& pos, float time)
+{
+    Vec2 n = Vec2((float)_gridSize.width, (float)_gridSize.height) * time;
+    if ((n.x + n.y) == 0.0f)
+    {
+        return 1.0f;
+    }
+
+    return powf((pos.width + pos.height) / (n.x + n.y), 6);
+}
+
+void FadeOutTRTiles::turnOnTile(const Vec2& pos)
+{
+    setTile(pos, getOriginalTile(pos));
+}
+
+void FadeOutTRTiles::turnOffTile(const Vec2& pos)
+{
+    Quad3 coords;
+    memset(&coords, 0, sizeof(Quad3));
+    setTile(pos, coords);
+}
+
+void FadeOutTRTiles::transformTile(const Vec2& pos, float distance)
+{
+    Quad3 coords = getOriginalTile(pos);
+    Vec2 step = _gridNodeTarget->getGrid()->getStep();
+
+    coords.bl.x += (step.x / 2) * (1.0f - distance);
+    coords.bl.y += (step.y / 2) * (1.0f - distance);
+
+    coords.br.x -= (step.x / 2) * (1.0f - distance);
+    coords.br.y += (step.y / 2) * (1.0f - distance);
+
+    coords.tl.x += (step.x / 2) * (1.0f - distance);
+    coords.tl.y -= (step.y / 2) * (1.0f - distance);
+
+    coords.tr.x -= (step.x / 2) * (1.0f - distance);
+    coords.tr.y -= (step.y / 2) * (1.0f - distance);
+
+    setTile(pos, coords);
+}
+
+void FadeOutTRTiles::update(float time)
+{
+    for (int i = 0; i < _gridSize.width; ++i)
+    {
+        for (int j = 0; j < _gridSize.height; ++j)
+        {
+            Vec2 pos((float)i, (float)j);
+            float distance = testFunc(Size((float)i, (float)j), time);
+            if ( distance == 0 )
+            {
+                turnOffTile(pos);
+            } else 
+            if (distance < 1)
+            {
+                transformTile(pos, distance);
+            }
+            else
+            {
+                turnOnTile(pos);
+            }
+        }
+    }
+}
+
+// implementation of FadeOutBLTiles
+
+FadeOutBLTiles* FadeOutBLTiles::create(float duration, const Size& gridSize)
+{
+    FadeOutBLTiles *action = new (std::nothrow) FadeOutBLTiles();
+
+    if (action && action->initWithDuration(duration, gridSize))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+FadeOutBLTiles* FadeOutBLTiles::clone() const
+{
+    // no copy constructor
+    return FadeOutBLTiles::create(_duration, _gridSize);
+}
+
+float FadeOutBLTiles::testFunc(const Size& pos, float time)
+{
+    Vec2 n = Vec2((float)_gridSize.width, (float)_gridSize.height) * (1.0f - time);
+    if ((pos.width + pos.height) == 0)
+    {
+        return 1.0f;
+    }
+
+    return powf((n.x + n.y) / (pos.width + pos.height), 6);
+}
+
+// implementation of FadeOutUpTiles
+
+FadeOutUpTiles* FadeOutUpTiles::create(float duration, const Size& gridSize)
+{
+    FadeOutUpTiles *action = new (std::nothrow) FadeOutUpTiles();
+
+    if (action && action->initWithDuration(duration, gridSize))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+FadeOutUpTiles* FadeOutUpTiles::clone() const
+{
+    // no copy constructor
+    return FadeOutUpTiles::create(_duration, _gridSize);
+}
+
+float FadeOutUpTiles::testFunc(const Size& pos, float time)
+{
+    Vec2 n = Vec2((float)_gridSize.width, (float)_gridSize.height) * time;
+    if (n.y == 0.0f)
+    {
+        return 1.0f;
+    }
+
+    return powf(pos.height / n.y, 6);
+}
+
+void FadeOutUpTiles::transformTile(const Vec2& pos, float distance)
+{
+    Quad3 coords = getOriginalTile(pos);
+    Vec2 step = _gridNodeTarget->getGrid()->getStep();
+
+    coords.bl.y += (step.y / 2) * (1.0f - distance);
+    coords.br.y += (step.y / 2) * (1.0f - distance);
+    coords.tl.y -= (step.y / 2) * (1.0f - distance);
+    coords.tr.y -= (step.y / 2) * (1.0f - distance);
+
+    setTile(pos, coords);
+}
+
+// implementation of FadeOutDownTiles
+
+FadeOutDownTiles* FadeOutDownTiles::create(float duration, const Size& gridSize)
+{
+    FadeOutDownTiles *action = new (std::nothrow) FadeOutDownTiles();
+
+    if (action && action->initWithDuration(duration, gridSize))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+FadeOutDownTiles* FadeOutDownTiles::clone() const
+{
+    // no copy constructor
+    return FadeOutDownTiles::create(_duration, _gridSize);
+}
+
+float FadeOutDownTiles::testFunc(const Size& pos, float time)
+{
+    Vec2 n = Vec2((float)_gridSize.width, (float)_gridSize.height) * (1.0f - time);
+    return powf(n.y / (pos.height > 0.0f ? pos.height : 0.1f), 6);
+}
+
+// implementation of TurnOffTiles
+
+TurnOffTiles* TurnOffTiles::create(float duration, const Size& gridSize)
+{
+    TurnOffTiles* action = new (std::nothrow) TurnOffTiles();
+    if (action && action->initWithDuration(duration, gridSize, 0))
+    {
+        action->autorelease();
+        return action;
+    }
+    
+    delete action;
+    return nullptr;
+}
+
+TurnOffTiles* TurnOffTiles::create(float duration, const Size& gridSize, unsigned int seed)
+{
+    TurnOffTiles *action = new (std::nothrow) TurnOffTiles();
+
+    if (action && action->initWithDuration(duration, gridSize, seed))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool TurnOffTiles::initWithDuration(float duration, const Size& gridSize, unsigned int seed)
+{
+    if (TiledGrid3DAction::initWithDuration(duration, gridSize))
+    {
+        _seed = seed;
+        _tilesOrder = nullptr;
+
+        return true;
+    }
+
+    return false;
+}
+
+TurnOffTiles* TurnOffTiles::clone() const
+{
+    // no copy constructor
+    return TurnOffTiles::create(_duration, _gridSize, _seed);
+}
+
+TurnOffTiles::~TurnOffTiles()
+{
+    CC_SAFE_DELETE_ARRAY(_tilesOrder);
+}
+
+void TurnOffTiles::shuffle(unsigned int *array, unsigned int len)
+{
+    for (int i = len - 1; i >= 0; i--)
+    {
+        unsigned int j = rand() % (i+1);
+        unsigned int v = array[i];
+        array[i] = array[j];
+        array[j] = v;
+    }
+}
+
+void TurnOffTiles::turnOnTile(const Vec2& pos)
+{
+    setTile(pos, getOriginalTile(pos));
+}
+
+void TurnOffTiles::turnOffTile(const Vec2& pos)
+{
+    Quad3 coords;
+
+    memset(&coords, 0, sizeof(Quad3));
+    setTile(pos, coords);
+}
+
+void TurnOffTiles::startWithTarget(Node *target)
+{
+    TiledGrid3DAction::startWithTarget(target);
+
+    if (_seed != (unsigned int)-1)
+    {
+        std::srand(_seed);
+    }
+
+    _tilesCount = (unsigned int)(_gridSize.width * _gridSize.height);
+    _tilesOrder = new unsigned int[_tilesCount];
+
+    for (unsigned int i = 0; i < _tilesCount; ++i)
+    {
+        _tilesOrder[i] = i;
+    }
+
+    shuffle(_tilesOrder, _tilesCount);
+}
+
+void TurnOffTiles::update(float time)
+{
+    unsigned int l = (unsigned int)(time * (float)_tilesCount);
+
+    unsigned int t = 0;
+    for (unsigned int i = 0; i < _tilesCount; i++ )
+    {
+        t = _tilesOrder[i];
+        //needs integer value
+        unsigned int x = (unsigned int)(t / _gridSize.height);
+        Vec2 tilePos((float)x, (float)(t % (unsigned int)_gridSize.height));
+
+        if ( i < l )
+        {
+            turnOffTile(tilePos);
+        }
+        else
+        {
+            turnOnTile(tilePos);
+        }
+    }
+}
+
+// implementation of WavesTiles3D
+
+WavesTiles3D* WavesTiles3D::create(float duration, const Size& gridSize, unsigned int waves, float amplitude)
+{
+    WavesTiles3D *action = new (std::nothrow) WavesTiles3D();
+
+    if (action && action->initWithDuration(duration, gridSize, waves, amplitude))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool WavesTiles3D::initWithDuration(float duration, const Size& gridSize, unsigned int waves, float amplitude)
+{
+    if (TiledGrid3DAction::initWithDuration(duration, gridSize))
+    {
+        _waves = waves;
+        _amplitude = amplitude;
+        _amplitudeRate = 1.0f;
+
+        return true;
+    }
+
+    return false;
+}
+
+WavesTiles3D* WavesTiles3D::clone() const
+{
+    // no copy constructor
+    return WavesTiles3D::create(_duration, _gridSize, _waves, _amplitude);
+}
+
+void WavesTiles3D::update(float time)
+{
+    for (int i = 0; i < _gridSize.width; i++ )
+    {
+        for (int j = 0; j < _gridSize.height; j++ )
+        {
+            Vec2 pos((float)i, (float)j);
+            Quad3 coords = getOriginalTile(pos);
+
+            coords.bl.z = (sinf(time * (float)M_PI  *_waves * 2 + 
+                (coords.bl.y+coords.bl.x) * .01f) * _amplitude * _amplitudeRate );
+            coords.br.z    = coords.bl.z;
+            coords.tl.z = coords.bl.z;
+            coords.tr.z = coords.bl.z;
+
+            setTile(pos, coords);
+        }
+    }
+}
+
+// implementation of JumpTiles3D
+
+JumpTiles3D* JumpTiles3D::create(float duration, const Size& gridSize, unsigned int numberOfJumps, float amplitude)
+{
+    JumpTiles3D *action = new (std::nothrow) JumpTiles3D();
+
+    if (action && action->initWithDuration(duration, gridSize, numberOfJumps, amplitude))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool JumpTiles3D::initWithDuration(float duration, const Size& gridSize, unsigned int numberOfJumps, float amplitude)
+{
+    if (TiledGrid3DAction::initWithDuration(duration, gridSize))
+    {
+        _jumps = numberOfJumps;
+        _amplitude = amplitude;
+        _amplitudeRate = 1.0f;
+
+        return true;
+    }
+
+    return false;
+}
+
+JumpTiles3D* JumpTiles3D::clone() const
+{
+    // no copy constructor
+    return JumpTiles3D::create(_duration, _gridSize, _jumps, _amplitude);
+}
+
+void JumpTiles3D::update(float time)
+{
+    float sinz =  (sinf((float)M_PI * time * _jumps * 2) * _amplitude * _amplitudeRate );
+    float sinz2 = (sinf((float)M_PI * (time * _jumps * 2 + 1)) * _amplitude * _amplitudeRate );
+
+    for (int i = 0; i < _gridSize.width; i++ )
+    {
+        for (int j = 0; j < _gridSize.height; j++ )
+        {
+            Vec2 pos((float)i, (float)j);
+            Quad3 coords = getOriginalTile(pos);
+
+            if ( ((i+j) % 2) == 0 )
+            {
+                coords.bl.z += sinz;
+                coords.br.z += sinz;
+                coords.tl.z += sinz;
+                coords.tr.z += sinz;
+            }
+            else
+            {
+                coords.bl.z += sinz2;
+                coords.br.z += sinz2;
+                coords.tl.z += sinz2;
+                coords.tr.z += sinz2;
+            }
+
+            setTile(pos, coords);
+        }
+    }
+}
+
+// implementation of SplitRows
+
+SplitRows* SplitRows::create(float duration, unsigned int nRows)
+{
+    SplitRows *action = new (std::nothrow) SplitRows();
+
+    if (action && action->initWithDuration(duration, nRows))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool SplitRows::initWithDuration(float duration, unsigned int rows)
+{
+    _rows = rows;
+
+    return TiledGrid3DAction::initWithDuration(duration, Size(1.0f, (float)rows));
+}
+
+SplitRows* SplitRows::clone() const
+{
+    // no copy constructor
+    return SplitRows::create(_duration, _rows);
+}
+
+void SplitRows::startWithTarget(Node *target)
+{
+    TiledGrid3DAction::startWithTarget(target);
+    _winSize = Director::getInstance()->getWinSizeInPixels();
+}
+
+void SplitRows::update(float time)
+{
+    for (int j = 0; j < _gridSize.height; ++j)
+    {
+        Vec2 pos(0, (float)j);
+        Quad3 coords = getOriginalTile(pos);
+        float    direction = 1;
+
+        if ( (j % 2 ) == 0 )
+        {
+            direction = -1;
+        }
+
+        coords.bl.x += direction * _winSize.width * time;
+        coords.br.x += direction * _winSize.width * time;
+        coords.tl.x += direction * _winSize.width * time;
+        coords.tr.x += direction * _winSize.width * time;
+
+        setTile(pos, coords);
+    }
+}
+
+// implementation of SplitCols
+
+SplitCols* SplitCols::create(float duration, unsigned int cols)
+{
+    SplitCols *action = new (std::nothrow) SplitCols();
+
+    if (action && action->initWithDuration(duration, cols))
+    {
+        action->autorelease();
+        return action;
+    }
+
+    delete action;
+    return nullptr;
+}
+
+bool SplitCols::initWithDuration(float duration, unsigned int cols)
+{
+    _cols = cols;
+    return TiledGrid3DAction::initWithDuration(duration, Size((float)cols, 1.0f));
+}
+
+SplitCols* SplitCols::clone() const
+{
+    // no copy constructor
+    return SplitCols::create(_duration, _cols);
+}
+
+void SplitCols::startWithTarget(Node *target)
+{
+    TiledGrid3DAction::startWithTarget(target);
+    _winSize = Director::getInstance()->getWinSizeInPixels();
+}
+
+void SplitCols::update(float time)
+{
+    for (unsigned int i = 0; i < _gridSize.width; ++i)
+    {
+        Vec2 pos((float)i, 0);
+        Quad3 coords = getOriginalTile(pos);
+        float    direction = 1;
+
+        if ( (i % 2 ) == 0 )
+        {
+            direction = -1;
+        }
+
+        coords.bl.y += direction * _winSize.height * time;
+        coords.br.y += direction * _winSize.height * time;
+        coords.tl.y += direction * _winSize.height * time;
+        coords.tr.y += direction * _winSize.height * time;
+
+        setTile(pos, coords);
+    }
+}
+
+NS_CC_END

+ 610 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionTiledGrid.h

@@ -0,0 +1,610 @@
+/****************************************************************************
+Copyright (c) 2009      On-Core
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __ACTION_CCTILEDGRID_ACTION_H__
+#define __ACTION_CCTILEDGRID_ACTION_H__
+
+#include "2d/CCActionGrid.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/**
+@brief ShakyTiles3D action.
+@details This action is make the target node shake with many tiles.
+        You can create the action by these parameters:
+        duration, grid size, range, whether shake on the z axis.
+ */
+class CC_DLL ShakyTiles3D : public TiledGrid3DAction
+{
+public:
+    /** 
+    @brief Create the action with a range, shake Z vertices, a grid and duration.
+    @param duration Specify the duration of the ShakyTiles3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param range Specify the range of the shaky effect.
+    @param shakeZ Specify whether shake on the z axis.
+    @return If the creation success, return a pointer of ShakyTiles3D action; otherwise, return nil.
+    */
+    static ShakyTiles3D* create(float duration, const Size& gridSize, int range, bool shakeZ);
+
+    // Override
+    virtual ShakyTiles3D* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    ShakyTiles3D() {}
+    virtual ~ShakyTiles3D() {}
+
+    /** 
+    @brief Initializes the action with a range, shake Z vertices, grid size and duration.
+    @param duration Specify the duration of the ShakyTiles3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param range Specify the range of the shaky effect.
+    @param shakeZ Specify whether shake on the z axis.
+    @return If the Initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, int range, bool shakeZ);
+
+protected:
+    int _randrange;
+    bool _shakeZ;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ShakyTiles3D);
+};
+
+/**
+@brief ShatteredTiles3D action.
+@details This action make the target node shattered with many tiles.
+        You can create the action by these parameters:
+        duration, grid size, range, whether shatter on the z axis.
+*/
+class CC_DLL ShatteredTiles3D : public TiledGrid3DAction
+{
+public:
+    /** 
+     * @brief Create the action with a range, whether of not to shatter Z vertices, grid size and duration.
+     * @param duration Specify the duration of the ShatteredTiles3D action. It's a value in seconds.
+     * @param gridSize Specify the size of the grid.
+     * @param range Specify the range of the shatter effect.
+     * @param shatterZ Specify whether shatter on the z axis.
+     * @return If the creation success, return a pointer of ShatteredTiles3D action; otherwise, return nil.
+     */
+    static ShatteredTiles3D* create(float duration, const Size& gridSize, int range, bool shatterZ);
+
+    // Override
+    virtual ShatteredTiles3D* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    ShatteredTiles3D() {}
+    virtual ~ShatteredTiles3D() {}
+
+    /** 
+    @brief Initializes the action with a range, shatter Z vertices, grid size and duration.
+    @param duration Specify the duration of the ShatteredTiles3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param range Specify the range of the shatter effect.
+    @param shatterZ Specify whether shake on the z axis.
+    @return If the Initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, int range, bool shatterZ);
+
+protected:
+    int _randrange;
+    bool _once;
+    bool _shatterZ;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ShatteredTiles3D);
+};
+
+struct Tile;
+/**
+@brief ShuffleTiles action.
+@details This action make the target node shuffle with many tiles in random order.
+        You can create the action by these parameters:
+        duration, grid size, the random seed.
+*/
+class CC_DLL ShuffleTiles : public TiledGrid3DAction
+{
+public:
+    /** 
+    * @brief Create the action with grid size, random seed and duration.
+    * @param duration Specify the duration of the ShuffleTiles action. It's a value in seconds.
+    * @param gridSize Specify the size of the grid.
+    * @param seed Specify the random seed.
+    * @return If the creation success, return a pointer of ShuffleTiles action; otherwise, return nil.
+    */
+    static ShuffleTiles* create(float duration, const Size& gridSize, unsigned int seed);
+
+    void shuffle(unsigned int *array, unsigned int len);
+    Size getDelta(const Size& pos) const;
+    void placeTile(const Vec2& pos, Tile *t);
+
+    // Overrides
+    virtual void startWithTarget(Node *target) override;
+    virtual void update(float time) override;
+    virtual ShuffleTiles* clone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    ShuffleTiles() {}
+    virtual ~ShuffleTiles();
+
+    /** 
+    * @brief Initializes the action with grid size, random seed and duration.
+    * @param duration Specify the duration of the ShuffleTiles action. It's a value in seconds.
+    * @param gridSize Specify the size of the grid.
+    * @param seed Specify the random seed.
+    * @return If the Initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, unsigned int seed);
+
+protected:
+    unsigned int _seed;
+    unsigned int _tilesCount;
+    unsigned int* _tilesOrder;
+    Tile* _tiles;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ShuffleTiles);
+};
+
+/**
+@brief FadeOutTRTiles action.
+@details Fades out the target node with many tiles from Bottom-Left to Top-Right.
+ */
+class CC_DLL FadeOutTRTiles : public TiledGrid3DAction
+{
+public:
+    /** 
+    * @brief Create the action with the grid size and the duration.
+    * @param duration Specify the duration of the FadeOutTRTiles action. It's a value in seconds.
+    * @param gridSize Specify the size of the grid.
+    * @return If the creation success, return a pointer of FadeOutTRTiles action; otherwise, return nil.
+    */
+    static FadeOutTRTiles* create(float duration, const Size& gridSize);
+
+    /**
+    @brief Calculate the percentage a tile should be shown.
+    @param pos The position index of the tile.
+    @param time The current percentage of the action.
+    @return Return the percentage the tile should be shown.
+    */
+    virtual float testFunc(const Size& pos, float time);
+
+    /**
+    @brief Show the tile at specified position.
+    @param pos The position index of the tile should be shown.
+    */
+    void turnOnTile(const Vec2& pos);
+
+    /**
+    @brief Hide the tile at specified position.
+    @param pos The position index of the tile should be hide.
+    */
+    void turnOffTile(const Vec2& pos);
+
+    /**
+    @brief Show part of the tile.
+    @param pos The position index of the tile should be shown.
+    @param distance The percentage that the tile should be shown.
+    */
+    virtual void transformTile(const Vec2& pos, float distance);
+
+    // Overrides
+    virtual void update(float time) override;
+    virtual FadeOutTRTiles* clone() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    FadeOutTRTiles() {}
+    virtual ~FadeOutTRTiles() {}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FadeOutTRTiles);
+};
+
+/**
+@brief FadeOutBLTiles action.
+@details Fades out the target node with many tiles from Top-Right to Bottom-Left.
+ */
+class CC_DLL FadeOutBLTiles : public FadeOutTRTiles
+{
+public:
+    /** 
+    * @brief Create the action with the grid size and the duration.
+    * @param duration Specify the duration of the FadeOutBLTiles action. It's a value in seconds.
+    * @param gridSize Specify the size of the grid.
+    * @return If the creation success, return a pointer of FadeOutBLTiles action; otherwise, return nil.
+    */
+    static FadeOutBLTiles* create(float duration, const Size& gridSize);
+
+    // Overrides
+    virtual float testFunc(const Size& pos, float time) override;
+    virtual FadeOutBLTiles* clone() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    FadeOutBLTiles() {}
+    virtual ~FadeOutBLTiles() {}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FadeOutBLTiles);
+};
+
+/**
+@brief FadeOutUpTiles action.
+@details Fades out the target node with many tiles from bottom to top.
+ */
+class CC_DLL FadeOutUpTiles : public FadeOutTRTiles
+{
+public:
+    /** 
+    * @brief Create the action with the grid size and the duration.
+    * @param duration Specify the duration of the FadeOutUpTiles action. It's a value in seconds.
+    * @param gridSize Specify the size of the grid.
+    * @return If the creation success, return a pointer of FadeOutUpTiles action; otherwise, return nil.
+    */
+    static FadeOutUpTiles* create(float duration, const Size& gridSize);
+
+    virtual void transformTile(const Vec2& pos, float distance) override;
+
+    // Overrides
+    virtual FadeOutUpTiles* clone() const override;
+    virtual float testFunc(const Size& pos, float time) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    FadeOutUpTiles() {}
+    virtual ~FadeOutUpTiles() {}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FadeOutUpTiles);
+};
+
+/**
+@brief FadeOutDownTiles action.
+@details Fades out the target node with many tiles from top to bottom.
+ */
+class CC_DLL FadeOutDownTiles : public FadeOutUpTiles
+{
+public:
+    /** 
+    * @brief Create the action with the grid size and the duration.
+    * @param duration Specify the duration of the FadeOutDownTiles action. It's a value in seconds.
+    * @param gridSize Specify the size of the grid.
+    * @return If the creation success, return a pointer of FadeOutDownTiles action; otherwise, return nil.
+    */
+    static FadeOutDownTiles* create(float duration, const Size& gridSize);
+
+    // Overrides
+    virtual FadeOutDownTiles* clone() const override;
+    virtual float testFunc(const Size& pos, float time) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    FadeOutDownTiles() {}
+    virtual ~FadeOutDownTiles() {}
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FadeOutDownTiles);
+};
+
+/**
+@brief TurnOffTiles action.
+@details Turn off the target node with many tiles in random order.
+ */
+class CC_DLL TurnOffTiles : public TiledGrid3DAction
+{
+public:
+    /** 
+    * @brief Create the action with the grid size and the duration.
+    * @param duration Specify the duration of the TurnOffTiles action. It's a value in seconds.
+    * @param gridSize Specify the size of the grid.
+    * @return If the creation success, return a pointer of TurnOffTiles action; otherwise, return nil.
+    */
+    static TurnOffTiles* create(float duration, const Size& gridSize);
+    /** 
+    * @brief Create the action with the grid size and the duration.
+    * @param duration Specify the duration of the TurnOffTiles action. It's a value in seconds.
+    * @param gridSize Specify the size of the grid.
+    * @param seed Specify the random seed.
+    * @return If the creation success, return a pointer of TurnOffTiles action; otherwise, return nil.
+    */
+    static TurnOffTiles* create(float duration, const Size& gridSize, unsigned int seed);
+
+    /**
+    @brief Shuffle the array specified.
+    @param array The array will be shuffled.
+    @param len The size of the array.
+    */
+    void shuffle(unsigned int *array, unsigned int len);
+
+    /**
+    @brief Show the tile at specified position.
+    @param pos The position index of the tile should be shown.
+    */
+    void turnOnTile(const Vec2& pos);
+
+    /**
+    @brief Hide the tile at specified position.
+    @param pos The position index of the tile should be hide.
+    */
+    void turnOffTile(const Vec2& pos);
+
+    // Overrides
+	virtual TurnOffTiles* clone() const override;
+    virtual void startWithTarget(Node *target) override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    TurnOffTiles() {}
+    virtual ~TurnOffTiles();
+
+    /** 
+    * @brief Initializes the action with grid size, random seed and duration.
+    * @param duration Specify the duration of the TurnOffTiles action. It's a value in seconds.
+    * @param gridSize Specify the size of the grid.
+    * @param seed Specify the random seed.
+    * @return If the Initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, unsigned int seed);
+
+protected:
+    unsigned int    _seed;
+    unsigned int    _tilesCount;
+    unsigned int*   _tilesOrder;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(TurnOffTiles);
+};
+
+/**
+@brief WavesTiles3D action.
+@details This action wave the target node with many tiles.
+*/
+class CC_DLL WavesTiles3D : public TiledGrid3DAction
+{
+public:
+    /** 
+     * @brief Create the action with a number of waves, the waves amplitude, the grid size and the duration.
+     * @param duration Specify the duration of the WavesTiles3D action. It's a value in seconds.
+     * @param gridSize Specify the size of the grid.
+     * @param waves Specify the waves count of the WavesTiles3D action.
+     * @param amplitude Specify the amplitude of the WavesTiles3D action.
+     * @return If the creation success, return a pointer of WavesTiles3D action; otherwise, return nil.
+     */
+    static WavesTiles3D* create(float duration, const Size& gridSize, unsigned int waves, float amplitude);
+
+    /**
+    @brief Get the amplitude of the effect.
+    @return Return the amplitude of the effect.
+    */
+    float getAmplitude() const { return _amplitude; }
+    /**
+    @brief Set the amplitude to the effect.
+    @param amplitude The value of amplitude will be set.
+    */
+    void setAmplitude(float amplitude) { _amplitude = amplitude; }
+
+    /**
+    @brief Get the amplitude rate of the effect.
+    @return Return the amplitude rate of the effect.
+    */
+    float getAmplitudeRate() const { return _amplitudeRate; }
+    /**
+    @brief Set the amplitude rate of the effect.
+    @param amplitudeRate The value of amplitude rate will be set.
+    */
+    void setAmplitudeRate(float amplitudeRate) { _amplitudeRate = amplitudeRate; }
+
+    // Override
+    virtual WavesTiles3D* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    WavesTiles3D() {}
+    virtual ~WavesTiles3D() {}
+
+    /** 
+    @brief Initializes an action with duration, grid size, waves count and amplitude.
+    @param duration Specify the duration of the WavesTiles3D action. It's a value in seconds.
+    @param gridSize Specify the size of the grid.
+    @param waves Specify the waves count of the WavesTiles3D action.
+    @param amplitude Specify the amplitude of the WavesTiles3D action.
+    @return If the initialization success, return true; otherwise, return false.
+    */
+    bool initWithDuration(float duration, const Size& gridSize, unsigned int waves, float amplitude);
+
+protected:
+    unsigned int _waves;
+    float _amplitude;
+    float _amplitudeRate;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(WavesTiles3D);
+};
+
+/**
+@brief JumpTiles3D action.
+@details Move the tiles of a target node across the Z axis.
+*/
+class CC_DLL JumpTiles3D : public TiledGrid3DAction
+{
+public:
+    /** 
+     * @brief Create the action with the number of jumps, the sin amplitude, the grid size and the duration.
+     * @param duration Specify the duration of the JumpTiles3D action. It's a value in seconds.
+     * @param gridSize Specify the size of the grid.
+     * @param numberOfJumps Specify the jump tiles count.
+     * @param amplitude Specify the amplitude of the JumpTiles3D action.
+     * @return If the creation success, return a pointer of JumpTiles3D action; otherwise, return nil.
+     */
+    static JumpTiles3D* create(float duration, const Size& gridSize, unsigned int numberOfJumps, float amplitude);
+
+    /**
+    @brief Get the amplitude of the effect.
+    @return Return the amplitude of the effect.
+    */
+    float getAmplitude() const { return _amplitude; }
+    /**
+    @brief Set the amplitude to the effect.
+    @param amplitude The value of amplitude will be set.
+    */
+    void setAmplitude(float amplitude) { _amplitude = amplitude; }
+
+    /**
+    @brief Get the amplitude rate of the effect.
+    @return Return the amplitude rate of the effect.
+    */
+    float getAmplitudeRate() const { return _amplitudeRate; }
+    /**
+    @brief Set the amplitude rate of the effect.
+    @param amplitudeRate The value of amplitude rate will be set.
+    */
+    void setAmplitudeRate(float amplitudeRate) { _amplitudeRate = amplitudeRate; }
+
+    // Override
+    virtual JumpTiles3D* clone() const override;
+    virtual void update(float time) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    JumpTiles3D() {}
+    virtual ~JumpTiles3D() {}
+
+    /** 
+     * @brief Initializes the action with the number of jumps, the sin amplitude, the grid size and the duration.
+     * @param duration Specify the duration of the JumpTiles3D action. It's a value in seconds.
+     * @param gridSize Specify the size of the grid.
+     * @param numberOfJumps Specify the jump tiles count.
+     * @param amplitude Specify the amplitude of the JumpTiles3D action.
+     * @return If the initialization success, return true; otherwise, return false.
+     */
+    bool initWithDuration(float duration, const Size& gridSize, unsigned int numberOfJumps, float amplitude);
+
+protected:
+    unsigned int _jumps;
+    float _amplitude;
+    float _amplitudeRate;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(JumpTiles3D);
+};
+
+/**
+@brief SplitRows action.
+@details Split the target node in many rows.
+        Then move out some rows from left, move out the other rows from right.
+*/
+class CC_DLL SplitRows : public TiledGrid3DAction
+{
+public :
+    /** 
+     * @brief Create the action with the number of rows and the duration.
+     * @param duration Specify the duration of the SplitRows action. It's a value in seconds.
+     * @param rows Specify the rows count should be split.
+     * @return If the creation success, return a pointer of SplitRows action; otherwise, return nil.
+     */
+    static SplitRows* create(float duration, unsigned int rows);
+
+    // Overrides
+    virtual SplitRows* clone() const override;
+    virtual void update(float time) override;
+    virtual void startWithTarget(Node *target) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    SplitRows() {}
+    virtual ~SplitRows() {}
+
+    /** 
+     * @brief Initializes the action with the number rows and the duration.
+     * @param duration Specify the duration of the SplitRows action. It's a value in seconds.
+     * @param rows Specify the rows count should be split.
+     * @return If the creation success, return true; otherwise, return false.
+     */
+    bool initWithDuration(float duration, unsigned int rows);
+
+protected:
+    unsigned int _rows;
+    Size _winSize;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(SplitRows);
+};
+
+/**
+@brief SplitCols action.
+@details Split the target node in many columns.
+        Then move out some columns from top, move out the other columns from bottom.
+*/
+class CC_DLL SplitCols : public TiledGrid3DAction
+{
+public:
+    
+    /** 
+     * @brief Create the action with the number of columns and the duration.
+     * @param duration Specify the duration of the SplitCols action. It's a value in seconds.
+     * @param cols Specify the columns count should be split.
+     * @return If the creation success, return a pointer of SplitCols action; otherwise, return nil.
+     */
+    static SplitCols* create(float duration, unsigned int cols);
+
+    // Overrides
+    virtual SplitCols* clone() const override;
+    /**
+     * @param time in seconds
+     */
+    virtual void update(float time) override;
+    virtual void startWithTarget(Node *target) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    SplitCols() {}
+    virtual ~SplitCols() {}
+
+    /** 
+     * @brief Initializes the action with the number columns and the duration.
+     * @param duration Specify the duration of the SplitCols action. It's a value in seconds.
+     * @param cols Specify the columns count should be split.
+     * @return If the creation success, return true; otherwise, return false.
+     */
+    bool initWithDuration(float duration, unsigned int cols);
+
+protected:
+    unsigned int _cols;
+    Size _winSize;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(SplitCols);
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif // __ACTION_CCTILEDGRID_ACTION_H__

+ 81 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionTween.cpp

@@ -0,0 +1,81 @@
+/****************************************************************************
+Copyright (c) 2009      lhunath (Maarten Billemont)
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCActionTween.h"
+
+NS_CC_BEGIN
+
+ActionTween* ActionTween::create(float duration, const std::string& key, float from, float to)
+{
+    ActionTween* ret = new (std::nothrow) ActionTween();
+    if (ret && ret->initWithDuration(duration, key, from, to))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    
+    delete ret;
+    return nullptr;
+}
+
+bool ActionTween::initWithDuration(float duration, const std::string& key, float from, float to)
+{
+    if (ActionInterval::initWithDuration(duration))
+    {
+        _key    = key;
+        _to       = to;
+        _from     = from;
+        return true;
+    }
+
+    return false;
+}
+
+ActionTween *ActionTween::clone() const
+{
+    return ActionTween::create(_duration, _key, _from, _to);
+}
+
+void ActionTween::startWithTarget(Node *target)
+{
+    CCASSERT(dynamic_cast<ActionTweenDelegate*>(target), "target must implement ActionTweenDelegate");
+    ActionInterval::startWithTarget(target);
+    _delta = _to - _from;
+}
+
+void ActionTween::update(float dt)
+{
+    dynamic_cast<ActionTweenDelegate*>(_target)->updateTweenAction(_to  - _delta * (1 - dt), _key);
+}
+
+ActionTween* ActionTween::reverse() const
+{
+    return ActionTween::create(_duration, _key, _to, _from);
+}
+
+
+NS_CC_END

+ 128 - 0
3rdparty/cocos2dx/include/cocos/2d/CCActionTween.h

@@ -0,0 +1,128 @@
+/****************************************************************************
+Copyright (c) 2009      lhunath (Maarten Billemont)
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __CCACTIONTWEEN_H__
+#define __CCACTIONTWEEN_H__
+
+#include "2d/CCActionInterval.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup actions
+ * @{
+ */
+
+/**
+@brief The delegate class for ActionTween.
+@details If you want to use ActionTween on a node.
+        You should implement the node follow these steps:
+        1. The node should be inherit from ActionTweenDelegate.
+        2. Override the virtual method updateTweenAction in the node.
+
+        Then once you running ActionTween on the node, the method updateTweenAction will be invoked.
+*/
+class CC_DLL ActionTweenDelegate
+{
+public:
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ActionTweenDelegate() {}
+
+    /**
+    @brief The callback function when ActionTween is running.
+    @param value The new value of the specified key.
+    @param key The key of property which should be updated.
+    */
+    virtual void updateTweenAction(float value, const std::string& key) = 0;
+};
+
+/** ActionTween
+
+ ActionTween is an action that lets you update any property of an object.
+ For example, if you want to modify the "width" property of a target from 200 to 300 in 2 seconds, then:
+
+ @code
+     auto modifyWidth = ActionTween::create(2, "width", 200, 300);
+     target->runAction(modifyWidth);
+ @endcode
+
+ Another example: ScaleTo action could be rewritten using PropertyAction:
+
+ @code
+     // scaleA and scaleB are equivalents
+     auto scaleA = ScaleTo::create(2, 3);                 // (duration, to)
+     auto scaleB = ActionTween::create(2, "scale", 1, 3); // (duration, key, from, to)
+ @endcode
+
+ @since v0.99.2
+ */
+class CC_DLL ActionTween : public ActionInterval
+{
+public:
+    /** 
+     * @brief Create and initializes the action with the property name (key), and the from and to parameters.
+     * @param duration The duration of the ActionTween. It's a value in seconds.
+     * @param key The key of property which should be updated.
+     * @param from The value of the specified property when the action begin.
+     * @param to The value of the specified property when the action end.
+     * @return If the creation success, return a pointer of ActionTween; otherwise, return nil.
+     */
+    static ActionTween* create(float duration, const std::string& key, float from, float to);
+
+    // Overrides
+    void startWithTarget(Node *target) override;
+    void update(float dt) override;
+    ActionTween* reverse() const override;
+    ActionTween *clone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /** 
+     * @brief Initializes the action with the property name (key), and the from and to parameters.
+     * @param duration The duration of the ActionTween. It's a value in seconds.
+     * @param key The key of property which should be updated.
+     * @param from The value of the specified property when the action begin.
+     * @param to The value of the specified property when the action end.
+     * @return If the initialization success, return true; otherwise, return false.
+     */
+    bool initWithDuration(float duration, const std::string& key, float from, float to);
+
+protected:
+    std::string       _key;
+    float            _from, _to;
+    float            _delta;
+};
+
+// end of actions group
+/// @}
+
+NS_CC_END
+
+#endif /* __CCACTIONTWEEN_H__ */
+
+

+ 203 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAnimation.cpp

@@ -0,0 +1,203 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCAnimation.h"
+#include "renderer/CCTextureCache.h"
+#include "renderer/CCTexture2D.h"
+#include "base/CCDirector.h"
+
+NS_CC_BEGIN
+
+AnimationFrame* AnimationFrame::create(SpriteFrame* spriteFrame, float delayUnits, const ValueMap& userInfo)
+{
+    auto ret = new (std::nothrow) AnimationFrame();
+    if (ret && ret->initWithSpriteFrame(spriteFrame, delayUnits, userInfo))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+AnimationFrame::AnimationFrame()
+: _spriteFrame(nullptr)
+, _delayUnits(0.0f)
+{
+
+}
+
+bool AnimationFrame::initWithSpriteFrame(SpriteFrame* spriteFrame, float delayUnits, const ValueMap& userInfo)
+{
+    setSpriteFrame(spriteFrame);
+    setDelayUnits(delayUnits);
+    setUserInfo(userInfo);
+
+    return true;
+}
+
+AnimationFrame::~AnimationFrame()
+{    
+    CCLOGINFO( "deallocing AnimationFrame: %p", this);
+
+    CC_SAFE_RELEASE(_spriteFrame);
+}
+
+AnimationFrame* AnimationFrame::clone() const
+{
+    // no copy constructor
+    auto frame = new (std::nothrow) AnimationFrame();
+    frame->initWithSpriteFrame(_spriteFrame->clone(),
+                               _delayUnits,
+                               _userInfo);
+
+    frame->autorelease();
+    return frame;
+}
+
+// implementation of Animation
+
+Animation* Animation::create()
+{
+    Animation *animation = new (std::nothrow) Animation();
+    animation->init();
+    animation->autorelease();
+
+    return animation;
+} 
+
+Animation* Animation::createWithSpriteFrames(const Vector<SpriteFrame*>& frames, float delay/* = 0.0f*/, unsigned int loops/* = 1*/)
+{
+    Animation *animation = new (std::nothrow) Animation();
+    animation->initWithSpriteFrames(frames, delay, loops);
+    animation->autorelease();
+
+    return animation;
+}
+
+Animation* Animation::create(const Vector<AnimationFrame*>& arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops /* = 1 */)
+{
+    Animation *animation = new (std::nothrow) Animation();
+    animation->initWithAnimationFrames(arrayOfAnimationFrameNames, delayPerUnit, loops);
+    animation->autorelease();
+    return animation;
+}
+
+bool Animation::init()
+{
+    _loops = 1;
+    _delayPerUnit = 0.0f;
+    
+    return true;
+}
+
+bool Animation::initWithSpriteFrames(const Vector<SpriteFrame*>& frames, float delay/* = 0.0f*/, unsigned int loops/* = 1*/)
+{
+    _delayPerUnit = delay;
+    _loops = loops;
+
+    for (auto& spriteFrame : frames)
+    {
+        auto animFrame = AnimationFrame::create(spriteFrame, 1, ValueMap());
+        _frames.pushBack(animFrame);
+        _totalDelayUnits++;
+    }
+
+    return true;
+}
+
+bool Animation::initWithAnimationFrames(const Vector<AnimationFrame*>& arrayOfAnimationFrames, float delayPerUnit, unsigned int loops)
+{
+    _delayPerUnit = delayPerUnit;
+    _loops = loops;
+
+    setFrames(arrayOfAnimationFrames);
+
+    for (auto& animFrame : _frames)
+    {
+        _totalDelayUnits += animFrame->getDelayUnits();
+    }
+    return true;
+}
+
+Animation::Animation()
+: _totalDelayUnits(0.0f)
+, _delayPerUnit(0.0f)
+, _duration(0.0f)
+, _restoreOriginalFrame(false)
+, _loops(0)
+{
+
+}
+
+Animation::~Animation()
+{
+    CCLOGINFO("deallocing Animation: %p", this);
+}
+
+void Animation::addSpriteFrame(SpriteFrame* spriteFrame)
+{
+    AnimationFrame *animFrame = AnimationFrame::create(spriteFrame, 1.0f, ValueMap());
+    _frames.pushBack(animFrame);
+
+    // update duration
+    _totalDelayUnits++;
+}
+
+void Animation::addSpriteFrameWithFile(const std::string& filename)
+{
+    Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
+    Rect rect = Rect::ZERO;
+    rect.size = texture->getContentSize();
+    SpriteFrame *frame = SpriteFrame::createWithTexture(texture, rect);
+    addSpriteFrame(frame);
+}
+
+void Animation::addSpriteFrameWithTexture(Texture2D *pobTexture, const Rect& rect)
+{
+    SpriteFrame *frame = SpriteFrame::createWithTexture(pobTexture, rect);
+    addSpriteFrame(frame);
+}
+
+float Animation::getDuration() const
+{
+    return _totalDelayUnits * _delayPerUnit;
+}
+
+Animation* Animation::clone() const
+{
+    // no copy constructor    
+    auto a = new (std::nothrow) Animation();
+    a->initWithAnimationFrames(_frames, _delayPerUnit, _loops);
+    a->setRestoreOriginalFrame(_restoreOriginalFrame);
+    a->autorelease();
+    return a;
+}
+
+NS_CC_END

+ 325 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAnimation.h

@@ -0,0 +1,325 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __CC_ANIMATION_H__
+#define __CC_ANIMATION_H__
+
+#include "platform/CCPlatformConfig.h"
+#include "base/CCRef.h"
+#include "base/CCValue.h"
+#include "base/CCVector.h"
+#include "2d/CCSpriteFrame.h"
+
+#include <string>
+
+NS_CC_BEGIN
+
+class Texture2D;
+class SpriteFrame;
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** @class AnimationFrame
+ *
+ * A frame of the animation. It contains information like:
+ * - sprite frame name.
+ * - # of delay units.
+ * - offset
+ 
+ @since v2.0
+ */
+class CC_DLL AnimationFrame : public Ref, public Clonable
+{
+public:
+    /** @struct DisplayedEventInfo
+     * When the animation display,Dispatches the event of UserData.
+     */
+    struct DisplayedEventInfo
+    {
+        Node* target;
+        const ValueMap* userInfo;
+    };
+    
+    /**
+     * Creates the animation frame with a spriteframe, number of delay units and a notification user info.
+     *
+     * @param spriteFrame The animation frame with a spriteframe.
+     * @param delayUnits Number of delay units.
+     * @param userInfo A notification user info.
+     * @since 3.0
+     */
+    static AnimationFrame* create(SpriteFrame* spriteFrame, float delayUnits, const ValueMap& userInfo);
+    /** Return a SpriteFrameName to be used.
+     *
+     * @return a SpriteFrameName to be used.
+     */
+    SpriteFrame* getSpriteFrame() const { return _spriteFrame; };
+    /** Set the SpriteFrame.
+     *
+     * @param frame A SpriteFrame will be used.
+     */
+    void setSpriteFrame(SpriteFrame* frame)
+    {
+        CC_SAFE_RETAIN(frame);
+        CC_SAFE_RELEASE(_spriteFrame);
+        _spriteFrame = frame;
+    }
+
+    /** Gets the units of time the frame takes.
+     *
+     * @return The units of time the frame takes.
+     */
+    float getDelayUnits() const { return _delayUnits; };
+    
+    /** Sets the units of time the frame takes.
+     *
+     * @param delayUnits The units of time the frame takes.
+     */
+    void setDelayUnits(float delayUnits) { _delayUnits = delayUnits; };
+    
+    /** @brief Gets user information
+     * A AnimationFrameDisplayedNotification notification will be broadcast when the frame is displayed with this dictionary as UserInfo. 
+     * If UserInfo is nil, then no notification will be broadcast.
+     *
+     * @return A dictionary as UserInfo
+     */
+    const ValueMap& getUserInfo() const { return _userInfo; };
+    ValueMap& getUserInfo() { return _userInfo; };
+    
+    /** Sets user information.
+     * @param userInfo A dictionary as UserInfo.
+     */
+    void setUserInfo(const ValueMap& userInfo)
+    {
+        _userInfo = userInfo;
+    }
+    
+    // Overrides
+    virtual AnimationFrame *clone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    AnimationFrame();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~AnimationFrame();
+    
+    /** initializes the animation frame with a spriteframe, number of delay units and a notification user info */
+    bool initWithSpriteFrame(SpriteFrame* spriteFrame, float delayUnits, const ValueMap& userInfo);
+
+protected:
+    
+    /** SpriteFrameName to be used */
+    SpriteFrame* _spriteFrame;
+
+    /**  how many units of time the frame takes */
+    float _delayUnits;
+
+    /**  A AnimationFrameDisplayedNotification notification will be broadcast when the frame is displayed with this dictionary as UserInfo. If UserInfo is nil, then no notification will be broadcast. */
+    ValueMap _userInfo;
+    
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(AnimationFrame);
+};
+
+
+
+
+/** @class Animation
+ * A Animation object is used to perform animations on the Sprite objects.
+ * The Animation object contains AnimationFrame objects, and a possible delay between the frames.
+ * You can animate a Animation object by using the Animate action. Example:
+ * @code
+ * sprite->runAction(Animate::create(animation));
+ * @endcode
+*/
+class CC_DLL Animation : public Ref, public Clonable
+{
+public:
+    /** Creates an animation.
+     * @since v0.99.5
+     */
+    static Animation* create();
+
+    /* Creates an animation with an array of SpriteFrame and a delay between frames in seconds.
+     * The frames will be added with one "delay unit".
+     * @since v0.99.5
+     * @param arrayOfSpriteFrameNames An array of SpriteFrame.
+     * @param delay A delay between frames in seconds.
+     * @param loops The times the animation is going to loop.
+     */
+    static Animation* createWithSpriteFrames(const Vector<SpriteFrame*>& arrayOfSpriteFrameNames, float delay = 0.0f, unsigned int loops = 1);
+
+    /* Creates an animation with an array of AnimationFrame, the delay per units in seconds and how many times it should be executed.
+     * @since v2.0
+     * @param arrayOfAnimationFrameNames An animation with an array of AnimationFrame.
+     * @param delayPerUnit The delay per units in seconds and how many times it should be executed.
+     * @param loops The times the animation is going to loop.
+     */
+    static Animation* create(const Vector<AnimationFrame*>& arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops = 1);
+
+    /** Adds a SpriteFrame to a Animation.
+     *
+     * @param frame The frame will be added with one "delay unit".
+     */
+    void addSpriteFrame(SpriteFrame *frame);
+
+    /** Adds a frame with an image filename. Internally it will create a SpriteFrame and it will add it.
+     * The frame will be added with one "delay unit".
+     * Added to facilitate the migration from v0.8 to v0.9.
+     * @param filename The path of SpriteFrame.
+     */
+    void addSpriteFrameWithFile(const std::string& filename);
+
+    /** Adds a frame with a texture and a rect. Internally it will create a SpriteFrame and it will add it.
+     * The frame will be added with one "delay unit".
+     * Added to facilitate the migration from v0.8 to v0.9.
+     * @param pobTexture A frame with a texture.
+     * @param rect The Texture of rect.
+     */
+    void addSpriteFrameWithTexture(Texture2D* pobTexture, const Rect& rect);
+
+    /** Gets the total Delay units of the Animation. 
+     *
+     * @return The total Delay units of the Animation.
+     */
+    float getTotalDelayUnits() const { return _totalDelayUnits; };
+    
+    /** Sets the delay in seconds of the "delay unit".
+     *
+     * @param delayPerUnit The delay in seconds of the "delay unit".
+     */
+    void setDelayPerUnit(float delayPerUnit) { _delayPerUnit = delayPerUnit; };
+    
+    /** Gets the delay in seconds of the "delay unit".
+     * 
+     * @return The delay in seconds of the "delay unit".
+     */
+    float getDelayPerUnit() const { return _delayPerUnit; };
+
+    
+    /** Gets the duration in seconds of the whole animation. It is the result of totalDelayUnits * delayPerUnit.
+     *
+     * @return Result of totalDelayUnits * delayPerUnit.
+     */
+    float getDuration() const;
+    
+    /** Gets the array of AnimationFrames.
+     * 
+     * @return The array of AnimationFrames.
+     */
+    const Vector<AnimationFrame*>& getFrames() const { return _frames; };
+    
+    /** Sets the array of AnimationFrames. 
+     *
+     * @param frames The array of AnimationFrames.
+     */
+    void setFrames(const Vector<AnimationFrame*>& frames)
+    {
+        _frames = frames;
+    }
+    
+    /** Checks whether to restore the original frame when animation finishes. 
+     *
+     * @return Restore the original frame when animation finishes.
+     */
+    bool getRestoreOriginalFrame() const { return _restoreOriginalFrame; };
+    
+    /** Sets whether to restore the original frame when animation finishes. 
+     *
+     * @param restoreOriginalFrame Whether to restore the original frame when animation finishes.
+     */
+    void setRestoreOriginalFrame(bool restoreOriginalFrame) { _restoreOriginalFrame = restoreOriginalFrame; };
+    
+    /** Gets the times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ... 
+     *
+     * @return The times the animation is going to loop.
+     */
+    unsigned int getLoops() const { return _loops; };
+    
+    /** Sets the times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ... 
+     *
+     * @param loops The times the animation is going to loop.
+     */
+    void setLoops(unsigned int loops) { _loops = loops; };
+    
+    // overrides
+    virtual Animation *clone() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    Animation();
+    virtual ~Animation();
+    
+    /** Initializes a Animation. */
+    bool init();
+    
+    /** Initializes a Animation with frames and a delay between frames.
+     * @since v0.99.5
+     */
+    bool initWithSpriteFrames(const Vector<SpriteFrame*>& arrayOfSpriteFrameNames, float delay = 0.0f, unsigned int loops = 1);
+    
+    /** Initializes a Animation with AnimationFrame.
+     * @since v2.0
+     */
+    bool initWithAnimationFrames(const Vector<AnimationFrame*>& arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops);
+
+protected:
+    /** total Delay units of the Animation. */
+    float _totalDelayUnits;
+
+    /** Delay in seconds of the "delay unit". */
+    float _delayPerUnit;
+
+    /** duration in seconds of the whole animation. It is the result of totalDelayUnits * delayPerUnit. */
+    float _duration;
+
+    /** array of AnimationFrames. */
+    Vector<AnimationFrame*> _frames;
+
+    /** whether or not it shall restore the original frame when the animation finishes. */
+    bool _restoreOriginalFrame;
+
+    /** how many times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ... */
+    unsigned int _loops;
+    
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Animation);
+};
+
+// end of sprite_nodes group
+/// @}
+
+NS_CC_END
+
+#endif // __CC_ANIMATION_H__

+ 245 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAnimationCache.cpp

@@ -0,0 +1,245 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCAnimationCache.h"
+#include "2d/CCSpriteFrameCache.h"
+#include "platform/CCFileUtils.h"
+
+using namespace std;
+
+NS_CC_BEGIN
+
+AnimationCache* AnimationCache::s_sharedAnimationCache = nullptr;
+
+AnimationCache* AnimationCache::getInstance()
+{
+    if (! s_sharedAnimationCache)
+    {
+        s_sharedAnimationCache = new (std::nothrow) AnimationCache();
+        s_sharedAnimationCache->init();
+    }
+
+    return s_sharedAnimationCache;
+}
+
+void AnimationCache::destroyInstance()
+{
+    CC_SAFE_RELEASE_NULL(s_sharedAnimationCache);
+}
+
+bool AnimationCache::init()
+{
+    return true;
+}
+
+AnimationCache::AnimationCache()
+{
+}
+
+AnimationCache::~AnimationCache()
+{
+    CCLOGINFO("deallocing AnimationCache: %p", this);
+}
+
+void AnimationCache::addAnimation(Animation *animation, const std::string& name)
+{
+    _animations.insert(name, animation);
+}
+
+void AnimationCache::removeAnimation(const std::string& name)
+{
+    if (name.empty())
+        return;
+
+    _animations.erase(name);
+}
+
+Animation* AnimationCache::getAnimation(const std::string& name)
+{
+    return _animations.at(name);
+}
+
+void AnimationCache::parseVersion1(const ValueMap& animations)
+{
+    SpriteFrameCache *frameCache = SpriteFrameCache::getInstance();
+
+    for (const auto& anim : animations)
+    {
+        const ValueMap& animationDict = anim.second.asValueMap();
+        const ValueVector& frameNames = animationDict.at("frames").asValueVector();
+        float delay = animationDict.at("delay").asFloat();
+        Animation* animation = nullptr;
+
+        if ( frameNames.empty() )
+        {
+            CCLOG("cocos2d: AnimationCache: Animation '%s' found in dictionary without any frames - cannot add to animation cache.", anim.first.c_str());
+            continue;
+        }
+
+        ssize_t frameNameSize = frameNames.size();
+        Vector<AnimationFrame*> frames(frameNameSize);
+
+        for (auto& frameName : frameNames)
+        {
+            SpriteFrame* spriteFrame = frameCache->getSpriteFrameByName(frameName.asString());
+
+            if ( ! spriteFrame ) {
+                CCLOG("cocos2d: AnimationCache: Animation '%s' refers to frame '%s' which is not currently in the SpriteFrameCache. This frame will not be added to the animation.", anim.first.c_str(), frameName.asString().c_str());
+
+                continue;
+            }
+
+            AnimationFrame* animFrame = AnimationFrame::create(spriteFrame, 1, ValueMap());
+            frames.pushBack(animFrame);
+        }
+
+        if ( frames.empty() )
+        {
+            CCLOG("cocos2d: AnimationCache: None of the frames for animation '%s' were found in the SpriteFrameCache. Animation is not being added to the Animation Cache.", anim.first.c_str());
+            continue;
+        }
+        else if ( frames.size() != frameNameSize )
+        {
+            CCLOG("cocos2d: AnimationCache: An animation in your dictionary refers to a frame which is not in the SpriteFrameCache. Some or all of the frames for the animation '%s' may be missing.", anim.first.c_str());
+        }
+
+        animation = Animation::create(frames, delay, 1);
+
+        AnimationCache::getInstance()->addAnimation(animation, anim.first);
+    }
+}
+
+void AnimationCache::parseVersion2(const ValueMap& animations)
+{
+    SpriteFrameCache *frameCache = SpriteFrameCache::getInstance();
+
+    for (const auto& anim : animations)
+    {
+        std::string name = anim.first;
+        ValueMap& animationDict = const_cast<ValueMap&>(anim.second.asValueMap());
+
+        const Value& loops = animationDict["loops"];
+        bool restoreOriginalFrame = animationDict["restoreOriginalFrame"].asBool();
+
+        ValueVector& frameArray = animationDict["frames"].asValueVector();
+
+        if ( frameArray.empty() )
+        {
+            CCLOG("cocos2d: AnimationCache: Animation '%s' found in dictionary without any frames - cannot add to animation cache.", name.c_str());
+            continue;
+        }
+
+        // Array of AnimationFrames
+        Vector<AnimationFrame*> array(static_cast<int>(frameArray.size()));
+
+        for (auto& obj : frameArray)
+        {
+            ValueMap& entry = obj.asValueMap();
+            std::string spriteFrameName = entry["spriteframe"].asString();
+            SpriteFrame *spriteFrame = frameCache->getSpriteFrameByName(spriteFrameName);
+
+            if( ! spriteFrame ) {
+                CCLOG("cocos2d: AnimationCache: Animation '%s' refers to frame '%s' which is not currently in the SpriteFrameCache. This frame will not be added to the animation.", name.c_str(), spriteFrameName.c_str());
+
+                continue;
+            }
+
+            float delayUnits = entry["delayUnits"].asFloat();
+            Value& userInfo = entry["notification"];
+
+            AnimationFrame *animFrame = AnimationFrame::create(spriteFrame, delayUnits, userInfo.getType() == Value::Type::MAP ? userInfo.asValueMap() : ValueMapNull);
+
+            array.pushBack(animFrame);
+        }
+
+        float delayPerUnit = animationDict["delayPerUnit"].asFloat();
+        Animation *animation = Animation::create(array, delayPerUnit, loops.getType() != Value::Type::NONE ? loops.asInt() : 1);
+
+        animation->setRestoreOriginalFrame(restoreOriginalFrame);
+
+        AnimationCache::getInstance()->addAnimation(animation, name);
+    }
+}
+
+void AnimationCache::addAnimationsWithDictionary(const ValueMap& dictionary,const std::string& plist)
+{
+    auto anisItr = dictionary.find("animations");
+    if (anisItr == dictionary.end() )
+    {
+        CCLOG("cocos2d: AnimationCache: No animations were found in provided dictionary.");
+        return;
+    }
+    
+    const Value& animations = anisItr->second;
+    unsigned int version = 1;
+
+    auto propsItr = dictionary.find("properties");
+    if(propsItr != dictionary.end() )
+    {
+        const ValueMap& properties = propsItr->second.asValueMap();
+        version = properties.at("format").asInt();
+        const ValueVector& spritesheets = properties.at("spritesheets").asValueVector();
+
+        for(const auto &value : spritesheets) {
+            std::string path = FileUtils::getInstance()->fullPathFromRelativeFile(value.asString(),plist);
+            SpriteFrameCache::getInstance()->addSpriteFramesWithFile(path);
+        }
+    }
+
+    switch (version) {
+        case 1:
+            parseVersion1(animations.asValueMap());
+            break;
+        case 2:
+            parseVersion2(animations.asValueMap());
+            break;
+        default:
+            CCASSERT(false, "Invalid animation format");
+    }
+}
+
+/** Read an NSDictionary from a plist file and parse it automatically for animations */
+void AnimationCache::addAnimationsWithFile(const std::string& plist)
+{
+    CCASSERT(!plist.empty(), "Invalid texture file name");
+    if (plist.empty()) {
+        log("%s error:file name is empty!", __FUNCTION__);
+        return;
+    }
+    
+    ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(plist);
+
+    CCASSERT( !dict.empty(), "CCAnimationCache: File could not be found");
+    if (dict.empty()) {
+        log("AnimationCache::addAnimationsWithFile error:%s not exist!", plist.c_str());
+    }
+
+    addAnimationsWithDictionary(dict,plist);
+}
+
+
+NS_CC_END

+ 133 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAnimationCache.h

@@ -0,0 +1,133 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __CC_ANIMATION_CACHE_H__
+#define __CC_ANIMATION_CACHE_H__
+
+#include "base/CCRef.h"
+#include "base/CCMap.h"
+#include "base/CCValue.h"
+#include "2d/CCAnimation.h"
+
+#include <string>
+
+NS_CC_BEGIN
+
+
+class Animation;
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** Singleton that manages the Animations.
+It saves in a cache the animations. You should use this class if you want to save your animations in a cache.
+
+Before v0.99.5, the recommend way was to save them on the Sprite. Since v0.99.5, you should use this class instead.
+
+@since v0.99.5
+@js cc.animationCache
+*/
+class CC_DLL AnimationCache : public Ref
+{
+public:
+    /**
+     * @js ctor
+     */
+    AnimationCache();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    ~AnimationCache();
+    /** Returns the shared instance of the Animation cache 
+	 @js NA
+	*/
+    static AnimationCache* getInstance();
+
+    /** Purges the cache. It releases all the Animation objects and the shared instance.
+		@js NA
+     */
+    static void destroyInstance();
+
+    bool init();
+
+    /** Adds a Animation with a name.
+     *
+     * @param animation An animation.
+     * @param name The name of animation.
+     */
+    void addAnimation(Animation *animation, const std::string& name);
+
+    /** Deletes a Animation from the cache.
+     *
+     * @param name The name of animation.
+     */
+    void removeAnimation(const std::string& name);
+
+    /** Returns a Animation that was previously added.
+     * If the name is not found it will return nil.
+     * You should retain the returned copy if you are going to use it.
+     *
+     * @return A Animation that was previously added. If the name is not found it will return nil.
+     */
+    Animation* getAnimation(const std::string& name);
+
+    /** Adds an animation from an NSDictionary.
+     * Make sure that the frames were previously loaded in the SpriteFrameCache.
+     * @param dictionary An NSDictionary.
+     * @param plist The path of the relative file,it use to find the plist path for load SpriteFrames.
+     * @since v1.1
+	 @js NA
+     */
+    void addAnimationsWithDictionary(const ValueMap& dictionary,const std::string& plist);
+
+    /** Adds an animation from a plist file.
+     * Make sure that the frames were previously loaded in the SpriteFrameCache.
+     * @since v1.1
+     * @js addAnimations
+     * @lua addAnimations
+     * @param plist An animation from a plist file.
+     */
+    void addAnimationsWithFile(const std::string& plist);
+
+private:
+    void parseVersion1(const ValueMap& animations);
+    void parseVersion2(const ValueMap& animations);
+
+private:
+    Map<std::string, Animation*> _animations;
+    static AnimationCache* s_sharedAnimationCache;
+};
+
+// end of sprite_nodes group
+/// @}
+
+NS_CC_END
+
+#endif // __CC_ANIMATION_CACHE_H__

+ 284 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAtlasNode.cpp

@@ -0,0 +1,284 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCAtlasNode.h"
+#include <stddef.h> // offsetof
+#include "base/ccTypes.h"
+#include "renderer/CCTextureAtlas.h"
+#include "base/CCDirector.h"
+#include "renderer/CCTextureCache.h"
+#include "base/ccUtils.h"
+#include "renderer/ccShaders.h"
+#include "renderer/CCRenderer.h"
+#include "renderer/backend/ProgramState.h"
+
+NS_CC_BEGIN
+
+// implementation AtlasNode
+
+// AtlasNode - Creation & Init
+
+AtlasNode::AtlasNode()
+{
+    auto& pipelineDescriptor = _quadCommand.getPipelineDescriptor();
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR);
+    _programState = new (std::nothrow) backend::ProgramState(program);
+    pipelineDescriptor.programState = _programState;
+    _mvpMatrixLocation = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix");
+    _textureLocation = pipelineDescriptor.programState->getUniformLocation("u_texture");
+  
+    auto vertexLayout = _programState->getVertexLayout();
+  //a_position
+    vertexLayout->setAttribute(backend::ATTRIBUTE_NAME_POSITION, 
+                               _programState->getAttributeLocation(backend::Attribute::POSITION), 
+                               backend::VertexFormat::FLOAT3, 
+                               0, 
+                               false);
+   
+    //a_texCoord
+    vertexLayout->setAttribute(backend::ATTRIBUTE_NAME_TEXCOORD, 
+                               _programState->getAttributeLocation(backend::Attribute::TEXCOORD), 
+                               backend::VertexFormat::FLOAT2, offsetof(V3F_C4B_T2F, texCoords), 
+                               false);
+   
+    //a_color
+    vertexLayout->setAttribute(backend::ATTRIBUTE_NAME_COLOR, 
+                               _programState->getAttributeLocation(backend::Attribute::COLOR),
+                               backend::VertexFormat::UBYTE4, offsetof(V3F_C4B_T2F, colors),
+                               true);
+    
+    vertexLayout->setLayout(sizeof(V3F_C4B_T2F));
+}
+
+AtlasNode::~AtlasNode()
+{
+    CC_SAFE_RELEASE(_textureAtlas);
+}
+
+AtlasNode * AtlasNode::create(const std::string& tile, int tileWidth, int tileHeight, int itemsToRender)
+{
+    AtlasNode * ret = new (std::nothrow) AtlasNode();
+    if (ret->initWithTileFile(tile, tileWidth, tileHeight, itemsToRender))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool AtlasNode::initWithTileFile(const std::string& tile, int tileWidth, int tileHeight, int itemsToRender)
+{
+    CCASSERT(tile.size() > 0, "file size should not be empty");
+    Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(tile);
+    return initWithTexture(texture, tileWidth, tileHeight, itemsToRender);
+}
+
+bool AtlasNode::initWithTexture(Texture2D* texture, int tileWidth, int tileHeight, int itemsToRender)
+{
+    _itemWidth  = tileWidth;
+    _itemHeight = tileHeight;
+
+    _colorUnmodified = Color3B::WHITE;
+    _isOpacityModifyRGB = true;
+
+    _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+
+    _textureAtlas = new (std::nothrow) TextureAtlas();
+
+    if (! _textureAtlas)
+    {
+        CCLOG("cocos2d: Could not initialize AtlasNode. Invalid Texture.");
+        return false;
+    }
+    
+    _textureAtlas->initWithTexture(texture, itemsToRender);
+
+    this->updateBlendFunc();
+    this->updateOpacityModifyRGB();
+
+    this->calculateMaxItems();
+
+    _quadsToDraw = itemsToRender;
+
+    return true;
+}
+
+
+// AtlasNode - Atlas generation
+
+void AtlasNode::calculateMaxItems()
+{
+    Size s = _textureAtlas->getTexture()->getContentSize();
+    
+    if (_ignoreContentScaleFactor)
+    {
+        s = _textureAtlas->getTexture()->getContentSizeInPixels();
+    }
+    
+    _itemsPerColumn = (int)(s.height / _itemHeight);
+    _itemsPerRow = (int)(s.width / _itemWidth);
+}
+
+void AtlasNode::updateAtlasValues()
+{
+    CCASSERT(false, "CCAtlasNode:Abstract updateAtlasValue not overridden");
+}
+
+// AtlasNode - draw
+void AtlasNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
+{    
+    if( _textureAtlas->getTotalQuads() == 0 )
+        return;
+    
+    auto programState = _quadCommand.getPipelineDescriptor().programState;
+    programState->setTexture(_textureLocation, 0, _textureAtlas->getTexture()->getBackendTexture());
+    
+    const auto& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    programState->setUniform(_mvpMatrixLocation, projectionMat.m, sizeof(projectionMat.m));
+    
+    _quadCommand.init(_globalZOrder, _textureAtlas->getTexture(), _blendFunc, _textureAtlas->getQuads(), _quadsToDraw, transform, flags);
+    renderer->addCommand(&_quadCommand);
+}
+
+// AtlasNode - RGBA protocol
+
+const Color3B& AtlasNode::getColor() const
+{
+    if(_isOpacityModifyRGB)
+    {
+        return _colorUnmodified;
+    }
+    return Node::getColor();
+}
+
+void AtlasNode::setColor(const Color3B& color3)
+{
+    Color3B tmp = color3;
+    _colorUnmodified = color3;
+
+    if( _isOpacityModifyRGB )
+    {
+        tmp.r = tmp.r * _displayedOpacity/255;
+        tmp.g = tmp.g * _displayedOpacity/255;
+        tmp.b = tmp.b * _displayedOpacity/255;
+    }
+    Node::setColor(tmp);
+}
+
+void AtlasNode::setOpacity(uint8_t opacity)
+{
+    Node::setOpacity(opacity);
+
+    // special opacity for premultiplied textures
+    if( _isOpacityModifyRGB )
+        this->setColor(_colorUnmodified);
+}
+
+void AtlasNode::setOpacityModifyRGB(bool value)
+{
+    Color3B oldColor = this->getColor();
+    _isOpacityModifyRGB = value;
+    this->setColor(oldColor);
+}
+
+bool AtlasNode::isOpacityModifyRGB() const
+{
+    return _isOpacityModifyRGB;
+}
+
+void AtlasNode::updateOpacityModifyRGB()
+{
+    _isOpacityModifyRGB = _textureAtlas->getTexture()->hasPremultipliedAlpha();
+}
+
+void AtlasNode::setIgnoreContentScaleFactor(bool ignoreContentScaleFactor)
+{
+    _ignoreContentScaleFactor = ignoreContentScaleFactor;
+}
+
+// AtlasNode - CocosNodeTexture protocol
+
+const BlendFunc& AtlasNode::getBlendFunc() const
+{
+    return _blendFunc;
+}
+
+void AtlasNode::setBlendFunc(const BlendFunc &blendFunc)
+{
+    _blendFunc = blendFunc;
+}
+
+void AtlasNode::updateBlendFunc()
+{
+    if( ! _textureAtlas->getTexture()->hasPremultipliedAlpha() )
+    {
+        _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
+        setOpacityModifyRGB(false);
+    }
+    else
+    {
+        _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+        setOpacityModifyRGB(true);
+    }
+}
+
+void AtlasNode::setTexture(Texture2D *texture)
+{
+    _textureAtlas->setTexture(texture);
+    this->updateBlendFunc();
+    this->updateOpacityModifyRGB();
+}
+
+Texture2D * AtlasNode::getTexture() const
+{
+    return _textureAtlas->getTexture();
+}
+
+void AtlasNode::setTextureAtlas(TextureAtlas* textureAtlas)
+{
+    CC_SAFE_RETAIN(textureAtlas);
+    CC_SAFE_RELEASE(_textureAtlas);
+    _textureAtlas = textureAtlas;
+}
+
+TextureAtlas * AtlasNode::getTextureAtlas() const
+{
+    return _textureAtlas;
+}
+
+size_t AtlasNode::getQuadsToDraw() const
+{
+    return _quadsToDraw;
+}
+
+void AtlasNode::setQuadsToDraw(ssize_t quadsToDraw)
+{
+    _quadsToDraw = quadsToDraw;
+}
+
+NS_CC_END

+ 155 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAtlasNode.h

@@ -0,0 +1,155 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#pragma once
+
+#include "2d/CCNode.h"
+#include "base/CCProtocols.h"
+#include "base/ccTypes.h"
+#include "renderer/CCQuadCommand.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+class TextureAtlas;
+
+/** @brief AtlasNode is a subclass of Node that implements the RGBAProtocol and TextureProtocol protocol.
+ * It knows how to render a TextureAtlas object.
+ * If you are going to render a TextureAtlas consider subclassing AtlasNode (or a subclass of AtlasNode).
+ * All features from Node are valid, plus the following features:
+ * - opacity and RGB colors.
+ */
+class CC_DLL AtlasNode : public Node, public TextureProtocol
+{    
+public:
+	/** creates a AtlasNode  with an Atlas file the width and height of each item and the quantity of items to render.
+     *
+     * @param filename The path of Atlas file.
+     * @param tileWidth The width of the item.
+     * @param tileHeight The height of the item.
+     * @param itemsToRender The quantity of items to render.
+     */
+	static AtlasNode * create(const std::string& filename, int tileWidth, int tileHeight, int itemsToRender);
+
+    /** updates the Atlas (indexed vertex array).
+    * Shall be overridden in subclasses.
+    */
+    virtual void updateAtlasValues();
+    
+    // Overrides
+    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
+    virtual Texture2D* getTexture() const override;
+    virtual void setTexture(Texture2D *texture) override;
+    virtual bool isOpacityModifyRGB() const override;
+    virtual void setOpacityModifyRGB(bool isOpacityModifyRGB) override;
+    virtual const Color3B& getColor() const override;
+    virtual void setColor(const Color3B& color) override;
+    virtual void setOpacity(uint8_t opacity) override;
+    
+    /**
+     * @code
+     * When this function bound into js or lua,the parameter will be changed
+     * In js: var setBlendFunc(var src, var dst)
+     * @endcode
+     * @lua NA
+     */
+    virtual void setBlendFunc(const BlendFunc& blendFunc) override;
+    /**
+     * @lua NA
+     */
+    virtual const BlendFunc& getBlendFunc() const override;
+    
+    /** Set an buffer manager of the texture vertex. */
+    void setTextureAtlas(TextureAtlas* textureAtlas);
+    
+    /** Return the buffer manager of the texture vertex. 
+     *
+     * @return Return A TextureAtlas.
+     */
+    TextureAtlas* getTextureAtlas() const;
+    
+    void setQuadsToDraw(ssize_t quadsToDraw);
+    size_t getQuadsToDraw() const;
+
+CC_CONSTRUCTOR_ACCESS:
+    AtlasNode();
+    virtual ~AtlasNode();
+
+    /** Initializes an AtlasNode  with an Atlas file the width and height of each item and the quantity of items to render*/
+    bool initWithTileFile(const std::string& tile, int tileWidth, int tileHeight, int itemsToRender);
+    
+    /** Initializes an AtlasNode  with a texture the width and height of each item measured in points and the quantity of items to render*/
+    bool initWithTexture(Texture2D* texture, int tileWidth, int tileHeight, int itemsToRender);
+
+protected:
+    friend class Director;
+    
+    void calculateMaxItems();
+    void updateBlendFunc();
+    void updateOpacityModifyRGB();
+    void setIgnoreContentScaleFactor(bool bIgnoreContentScaleFactor);
+
+    /** Chars per row. */
+    int    _itemsPerRow = 0;
+    /** Chars per column. */
+    int    _itemsPerColumn = 0;
+
+    /** Width of each char. */
+    int    _itemWidth = 0;
+    /** Height of each char. */
+    int    _itemHeight = 0;
+    
+    Color3B    _colorUnmodified;
+    
+    TextureAtlas* _textureAtlas = nullptr;
+    /** Protocol variables. */
+    bool _isOpacityModifyRGB = false;
+    BlendFunc _blendFunc;
+
+    /** Quads to draw. */
+    size_t _quadsToDraw = 0;
+
+    /** This variable is only used for LabelAtlas FPS display. So plz don't modify its value. */
+    bool _ignoreContentScaleFactor = false;
+    
+    QuadCommand _quadCommand;
+    backend::UniformLocation _textureLocation;
+    backend::UniformLocation _mvpMatrixLocation;
+    
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(AtlasNode);
+
+};
+
+// end of base_node group
+/// @}
+
+NS_CC_END

+ 722 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAutoPolygon.cpp

@@ -0,0 +1,722 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCAutoPolygon.h"
+#include "poly2tri/poly2tri.h"
+#include "base/CCDirector.h"
+#include "renderer/CCTextureCache.h"
+#include "clipper/clipper.hpp"
+#include <algorithm>
+#include <math.h>
+
+USING_NS_CC;
+
+static unsigned short quadIndices9[]={
+    0+4*0,1+4*0,2+4*0, 3+4*0,2+4*0,1+4*0,
+    0+4*1,1+4*1,2+4*1, 3+4*1,2+4*1,1+4*1,
+    0+4*2,1+4*2,2+4*2, 3+4*2,2+4*2,1+4*2,
+    0+4*3,1+4*3,2+4*3, 3+4*3,2+4*3,1+4*3,
+    0+4*4,1+4*4,2+4*4, 3+4*4,2+4*4,1+4*4,
+    0+4*5,1+4*5,2+4*5, 3+4*5,2+4*5,1+4*5,
+    0+4*6,1+4*6,2+4*6, 3+4*6,2+4*6,1+4*6,
+    0+4*7,1+4*7,2+4*7, 3+4*7,2+4*7,1+4*7,
+    0+4*8,1+4*8,2+4*8, 3+4*8,2+4*8,1+4*8,
+};
+
+const static float PRECISION = 10.0f;
+
+PolygonInfo::PolygonInfo()
+: _isVertsOwner(true)
+, _rect(Rect::ZERO)
+, _filename("")
+{
+    triangles.verts = nullptr;
+    triangles.indices = nullptr;
+    triangles.vertCount = 0;
+    triangles.indexCount = 0;
+};
+
+PolygonInfo::PolygonInfo(const PolygonInfo& other)
+: triangles()
+, _isVertsOwner(true)
+, _rect()
+{
+    _filename = other._filename;
+    _isVertsOwner = true;
+    _rect = other._rect;
+    triangles.verts = new (std::nothrow) V3F_C4B_T2F[other.triangles.vertCount];
+    triangles.indices = new (std::nothrow) unsigned short[other.triangles.indexCount];
+    CCASSERT(triangles.verts && triangles.indices, "not enough memory");
+    triangles.vertCount = other.triangles.vertCount;
+    triangles.indexCount = other.triangles.indexCount;
+    memcpy(triangles.verts, other.triangles.verts, other.triangles.vertCount * sizeof(other.triangles.verts[0]));
+    memcpy(triangles.indices, other.triangles.indices, other.triangles.indexCount * sizeof(other.triangles.indices[0]));
+};
+
+PolygonInfo& PolygonInfo::operator= (const PolygonInfo& other)
+{
+    if(this != &other)
+    {
+        releaseVertsAndIndices();
+        _filename = other._filename;
+        _isVertsOwner = true;
+        _rect = other._rect;
+        triangles.verts = new (std::nothrow) V3F_C4B_T2F[other.triangles.vertCount];
+        triangles.indices = new (std::nothrow) unsigned short[other.triangles.indexCount];
+        CCASSERT(triangles.verts && triangles.indices, "not enough memory");
+        triangles.vertCount = other.triangles.vertCount;
+        triangles.indexCount = other.triangles.indexCount;
+        memcpy(triangles.verts, other.triangles.verts, other.triangles.vertCount * sizeof(other.triangles.verts[0]));
+        memcpy(triangles.indices, other.triangles.indices, other.triangles.indexCount * sizeof(other.triangles.indices[0]));
+    }
+    return *this;
+}
+
+PolygonInfo::~PolygonInfo()
+{
+    releaseVertsAndIndices();
+}
+
+void PolygonInfo::setQuad(V3F_C4B_T2F_Quad *quad)
+{
+    releaseVertsAndIndices();
+    _isVertsOwner = false;
+    triangles.indices = quadIndices9;
+    triangles.vertCount = 4;
+    triangles.indexCount = 6;
+    triangles.verts = (V3F_C4B_T2F*)quad;
+}
+
+void PolygonInfo::setQuads(V3F_C4B_T2F_Quad *quad, int numberOfQuads)
+{
+    CCASSERT(numberOfQuads >= 1 && numberOfQuads <= 9, "Invalid number of Quads");
+
+    releaseVertsAndIndices();
+    _isVertsOwner = false;
+    triangles.indices = quadIndices9;
+    triangles.vertCount = 4 * numberOfQuads;
+    triangles.indexCount = 6 * numberOfQuads;
+    triangles.verts = (V3F_C4B_T2F*)quad;
+}
+
+void PolygonInfo::setTriangles(const TrianglesCommand::Triangles& other)
+{
+    this->releaseVertsAndIndices();
+    _isVertsOwner = false;
+    
+    this->triangles.vertCount = other.vertCount;
+    this->triangles.indexCount = other.indexCount;
+    this->triangles.verts = other.verts;
+    this->triangles.indices = other.indices;
+}
+
+void PolygonInfo::releaseVertsAndIndices()
+{
+    if(_isVertsOwner)
+    {
+        if(nullptr != triangles.verts)
+        {
+            CC_SAFE_DELETE_ARRAY(triangles.verts);
+        }
+        
+        if(nullptr != triangles.indices)
+        {
+            CC_SAFE_DELETE_ARRAY(triangles.indices);
+        }
+    }
+}
+
+unsigned int PolygonInfo::getVertCount() const
+{
+    return (unsigned int)triangles.vertCount;
+}
+
+unsigned int PolygonInfo::getTrianglesCount() const
+{
+    return (unsigned int)triangles.indexCount/3;
+}
+
+float PolygonInfo::getArea() const
+{
+    float area = 0;
+    V3F_C4B_T2F *verts = triangles.verts;
+    unsigned short *indices = triangles.indices;
+    for(unsigned int i = 0; i < triangles.indexCount; i+=3)
+    {
+        auto A = verts[indices[i]].vertices;
+        auto B = verts[indices[i+1]].vertices;
+        auto C = verts[indices[i+2]].vertices;
+        area += (A.x*(B.y-C.y) + B.x*(C.y-A.y) + C.x*(A.y - B.y))/2;
+    }
+    return area;
+}
+
+AutoPolygon::AutoPolygon(const std::string &filename)
+:_image(nullptr)
+,_data(nullptr)
+,_filename("")
+,_width(0)
+,_height(0)
+,_scaleFactor(0)
+{
+    _filename = filename;
+    _image = new (std::nothrow) Image();
+    _image->initWithImageFile(filename);
+    CCASSERT(_image->getPixelFormat()==backend::PixelFormat::RGBA8888, "unsupported format, currently only supports rgba8888");
+    _data = _image->getData();
+    _width = _image->getWidth();
+    _height = _image->getHeight();
+    _scaleFactor = Director::getInstance()->getContentScaleFactor();
+}
+
+AutoPolygon::~AutoPolygon()
+{
+    CC_SAFE_DELETE(_image);
+}
+
+std::vector<Vec2> AutoPolygon::trace(const Rect& rect, float threshold)
+{
+    Vec2 first = findFirstNoneTransparentPixel(rect, threshold);
+    return marchSquare(rect, first, threshold);
+}
+
+Vec2 AutoPolygon::findFirstNoneTransparentPixel(const Rect& rect, float threshold)
+{
+	bool found = false;
+    Vec2 i;
+    for(i.y = rect.origin.y; i.y < rect.origin.y+rect.size.height; i.y++)
+    {
+        if(found)break;
+        for(i.x = rect.origin.x; i.x < rect.origin.x+rect.size.width; i.x++)
+        {
+            auto alpha = getAlphaByPos(i);
+            if(alpha>threshold)
+            {
+                found = true;
+                break;
+            }
+        }
+    }
+    CCASSERT(found, "image is all transparent!");
+    return i;
+}
+
+unsigned char AutoPolygon::getAlphaByIndex(unsigned int i)
+{
+    return *(_data+i*4+3);
+}
+unsigned char AutoPolygon::getAlphaByPos(const Vec2& pos)
+{
+    return *(_data+((int)pos.y*_width+(int)pos.x)*4+3);
+}
+
+unsigned int AutoPolygon::getSquareValue(unsigned int x, unsigned int y, const Rect& rect, float threshold)
+{
+    /*
+     checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent
+     +---+---+
+     | 1 | 2 |
+     +---+---+
+     | 4 | 8 | <- current pixel (curx,cury)
+     +---+---+
+     */
+    unsigned int sv = 0;
+    //NOTE: due to the way we pick points from texture, rect needs to be smaller, otherwise it goes outside 1 pixel
+    auto fixedRect = Rect(rect.origin, rect.size-Size(2,2));
+    
+    Vec2 tl = Vec2(x-1.0f, y-1.0f);
+    sv += (fixedRect.containsPoint(tl) && getAlphaByPos(tl) > threshold)? 1 : 0;
+    Vec2 tr = Vec2(x-0.0f, y-1.0f);
+    sv += (fixedRect.containsPoint(tr) && getAlphaByPos(tr) > threshold)? 2 : 0;
+    Vec2 bl = Vec2(x-1.0f, y-0.0f);
+    sv += (fixedRect.containsPoint(bl) && getAlphaByPos(bl) > threshold)? 4 : 0;
+    Vec2 br = Vec2(x-0.0f, y-0.0f);
+    sv += (fixedRect.containsPoint(br) && getAlphaByPos(br) > threshold)? 8 : 0;
+    CCASSERT(sv != 0 && sv != 15, "square value should not be 0, or 15");
+    return sv;
+}
+
+std::vector<cocos2d::Vec2> AutoPolygon::marchSquare(const Rect& rect, const Vec2& start, float threshold)
+{
+    int stepx = 0;
+    int stepy = 0;
+    int prevx = 0;
+    int prevy = 0;
+    int startx = (int)start.x;
+    int starty = (int)start.y;
+    int curx = startx;
+    int cury = starty;
+    unsigned int count = 0;
+    std::vector<int> case9s;
+    std::vector<int> case6s;
+    int i;
+    std::vector<int>::iterator it;
+    std::vector<cocos2d::Vec2> _points;
+    do{
+        int sv = getSquareValue(curx, cury, rect, threshold);
+        switch(sv){
+
+            case 1:
+            case 5:
+            case 13:
+                /* going UP with these cases:
+                 1          5           13
+                 +---+---+  +---+---+  +---+---+ 
+                 | 1 |   |  | 1 |   |  | 1 |   | 
+                 +---+---+  +---+---+  +---+---+ 
+                 |   |   |  | 4 |   |  | 4 | 8 | 
+                 +---+---+  +---+---+  +---+---+
+                 */
+                stepx = 0;
+                stepy = -1;
+                break;
+
+                
+            case 8:
+            case 10:
+            case 11:
+                /* going DOWN with these cases:
+                 8          10          11
+                 +---+---+  +---+---+   +---+---+
+                 |   |   |  |   | 2 |   | 1 | 2 |
+                 +---+---+  +---+---+   +---+---+
+                 |   | 8 |  |   | 8 |   |   | 8 |
+                 +---+---+  +---+---+  	+---+---+
+                 */
+                stepx = 0;
+                stepy = 1;
+                break;
+
+                
+            case 4:
+            case 12:
+            case 14:
+                /* going LEFT with these cases:
+                 4          12          14
+                 +---+---+  +---+---+   +---+---+
+                 |   |   |  |   |   |   |   | 2 |
+                 +---+---+  +---+---+   +---+---+
+                 | 4 |   |  | 4 | 8 |   | 4 | 8 |
+                 +---+---+  +---+---+  	+---+---+
+                 */
+                stepx = -1;
+                stepy = 0;
+                break;
+                
+                
+            case 2 :
+            case 3 :
+            case 7 :
+                /* going RIGHT with these cases:
+                 2          3           7        
+                 +---+---+  +---+---+   +---+---+
+                 |   | 2 |  | 1 | 2 |   | 1 | 2 |
+                 +---+---+  +---+---+   +---+---+
+                 |   |   |  |   |   |   | 4 |   |
+                 +---+---+  +---+---+  	+---+---+
+                 */
+                stepx=1;
+                stepy=0;
+                break;
+            case 9 :
+                /*
+                 +---+---+
+                 | 1 |   |
+                 +---+---+
+                 |   | 8 |
+                 +---+---+
+                 this should normally go UP, but if we already been here, we go down
+                */
+                //find index from xy;
+                i = getIndexFromPos(curx, cury);
+                it = find (case9s.begin(), case9s.end(), i);
+                if (it != case9s.end())
+                {
+                    //found, so we go down, and delete from case9s;
+                    stepx = 0;
+                    stepy = 1;
+                    case9s.erase(it);
+                }
+                else
+                {
+                    //not found, we go up, and add to case9s;
+                    stepx = 0;
+                    stepy = -1;
+                    case9s.push_back(i);
+                }
+                break;
+            case 6 :
+                /*
+                 6
+                 +---+---+
+                 |   | 2 |
+                 +---+---+
+                 | 4 |   |
+                 +---+---+
+                 this normally go RIGHT, but if its coming from UP, it should go LEFT
+                 */
+                i = getIndexFromPos(curx, cury);
+                it = find (case6s.begin(), case6s.end(), i);
+                if (it != case6s.end())
+                {
+                    //found, so we go down, and delete from case9s;
+                    stepx = -1;
+                    stepy = 0;
+                    case6s.erase(it);
+                }
+                else{
+                    //not found, we go up, and add to case9s;
+                    stepx = 1;
+                    stepy = 0;
+                    case6s.push_back(i);
+                }
+                break;
+            default:
+                CCLOG("this shouldn't happen.");
+        }
+        //little optimization
+        // if previous direction is same as current direction,
+        // then we should modify the last vec to current
+        curx += stepx;
+        cury += stepy;
+        if(stepx == prevx && stepy == prevy)
+        {
+            _points.back().x = (float)(curx-rect.origin.x) / _scaleFactor;
+            _points.back().y = (float)(rect.size.height - cury + rect.origin.y) / _scaleFactor;
+        }
+        else
+        {
+            _points.push_back(Vec2((float)(curx - rect.origin.x) / _scaleFactor, (float)(rect.size.height - cury + rect.origin.y) / _scaleFactor));
+        }
+
+        count++;
+        prevx = stepx;
+        prevy = stepy;
+
+#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
+        const auto totalPixel = _width * _height;
+        CCASSERT(count <= totalPixel, "oh no, marching square cannot find starting position");
+#endif
+    } while(curx != startx || cury != starty);
+    return _points;
+}
+
+float AutoPolygon::perpendicularDistance(const cocos2d::Vec2& i, const cocos2d::Vec2& start, const cocos2d::Vec2& end)
+{
+    float res;
+    float slope;
+    float intercept;
+    
+    if(start.x == end.x)
+    {
+        res = fabsf(i.x- end.x);
+    }
+    else if (start.y == end.y)
+    {
+        res = fabsf(i.y - end.y);
+    }
+    else{
+        slope = (end.y - start.y) / (end.x - start.x);
+        intercept = start.y - (slope*start.x);
+        res = fabsf(slope * i.x - i.y + intercept) / sqrtf(powf(slope, 2)+1);
+    }
+    return res;
+}
+std::vector<cocos2d::Vec2> AutoPolygon::rdp(const std::vector<cocos2d::Vec2>& v, float optimization)
+{
+    if(v.size() < 3)
+        return v;
+    
+    int index = -1;
+    float dist = 0;
+    //not looping first and last point
+    for(size_t i = 1, size = v.size(); i < size-1; ++i)
+    {
+        float cdist = perpendicularDistance(v[i], v.front(), v.back());
+        if(cdist > dist)
+        {
+            dist = cdist;
+            index = static_cast<int>(i);
+        }
+    }
+    if (dist>optimization)
+    {
+        std::vector<Vec2>::const_iterator begin = v.begin();
+        std::vector<Vec2>::const_iterator end   = v.end();
+        std::vector<Vec2> l1(begin, begin+index+1);
+        std::vector<Vec2> l2(begin+index, end);
+        
+        std::vector<Vec2> r1 = rdp(l1, optimization);
+        std::vector<Vec2> r2 = rdp(l2, optimization);
+        
+        r1.insert(r1.end(), r2.begin()+1, r2.end());
+        return r1;
+    }
+    else {
+        std::vector<Vec2> ret;
+        ret.push_back(v.front());
+        ret.push_back(v.back());
+        return ret;
+    }
+}
+std::vector<Vec2> AutoPolygon::reduce(const std::vector<Vec2>& points, const Rect& rect, float epsilon)
+{
+    auto size = points.size();
+    // if there are less than 3 points, then we have nothing
+    if(size<3)
+    {
+        log("AUTOPOLYGON: cannot reduce points for %s that has less than 3 points in input, e: %f", _filename.c_str(), epsilon);
+        return std::vector<Vec2>();
+    }
+    // if there are less than 9 points (but more than 3), then we don't need to reduce it
+    else if (size < 9)
+    {
+        log("AUTOPOLYGON: cannot reduce points for %s e: %f",_filename.c_str(), epsilon);
+        return points;
+    }
+    float maxEp = MIN(rect.size.width, rect.size.height);
+    float ep = clampf(epsilon, 0.0, maxEp/_scaleFactor/2);
+    std::vector<Vec2> result = rdp(points, ep);
+    
+    auto last = result.back();
+    if (last.y > result.front().y && last.getDistance(result.front()) < ep * 0.5f)
+    {
+        result.front().y = last.y;
+        result.pop_back();
+    }
+    return result;
+}
+
+std::vector<Vec2> AutoPolygon::expand(const std::vector<Vec2>& points, const cocos2d::Rect &rect, float epsilon)
+{
+    auto size = points.size();
+    // if there are less than 3 points, then we have nothing
+    if(size<3)
+    {
+        log("AUTOPOLYGON: cannot expand points for %s with less than 3 points, e: %f", _filename.c_str(), epsilon);
+        return std::vector<Vec2>();
+    }
+    ClipperLib::Path subj;
+    ClipperLib::PolyTree solution;
+    ClipperLib::PolyTree out;
+    for(const auto& pt : points)
+    {
+        subj << ClipperLib::IntPoint(static_cast<ClipperLib::cInt>(pt.x* PRECISION), static_cast<ClipperLib::cInt>(pt.y * PRECISION));
+    }
+    ClipperLib::ClipperOffset co;
+    co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
+    co.Execute(solution, epsilon * PRECISION);
+    
+    ClipperLib::PolyNode* p = solution.GetFirst();
+    if(!p)
+    {
+        log("AUTOPOLYGON: Clipper failed to expand the points");
+        return points;
+    }
+    while(p->IsHole()){
+        p = p->GetNext();
+    }
+
+    //turn the result into simply polygon (AKA, fix overlap)
+    
+    //clamp into the specified rect
+    ClipperLib::Clipper cl;
+    cl.StrictlySimple(true);
+    cl.AddPath(p->Contour, ClipperLib::ptSubject, true);
+    //create the clipping rect
+    ClipperLib::Path clamp;
+    clamp.push_back(ClipperLib::IntPoint(0, 0));
+    clamp.push_back(ClipperLib::IntPoint(static_cast<ClipperLib::cInt>(rect.size.width/_scaleFactor * PRECISION), 0));
+    clamp.push_back(ClipperLib::IntPoint(static_cast<ClipperLib::cInt>(rect.size.width/_scaleFactor * PRECISION), 
+                                         static_cast<ClipperLib::cInt>(rect.size.height/_scaleFactor * PRECISION)));
+    clamp.push_back(ClipperLib::IntPoint(0, static_cast<ClipperLib::cInt>(rect.size.height/_scaleFactor * PRECISION)));
+    cl.AddPath(clamp, ClipperLib::ptClip, true);
+    cl.Execute(ClipperLib::ctIntersection, out);
+    
+    std::vector<Vec2> outPoints;
+    ClipperLib::PolyNode* p2 = out.GetFirst();
+    while(p2->IsHole()){
+        p2 = p2->GetNext();
+    }
+    for(const auto& pt : p2->Contour)
+    {
+        outPoints.push_back(Vec2(pt.X/PRECISION, pt.Y/PRECISION));
+    }
+    return outPoints;
+}
+
+TrianglesCommand::Triangles AutoPolygon::triangulate(const std::vector<Vec2>& points)
+{
+    // if there are less than 3 points, then we can't triangulate
+    if(points.size()<3)
+    {
+        log("AUTOPOLYGON: cannot triangulate %s with less than 3 points", _filename.c_str());
+        return TrianglesCommand::Triangles();
+    }
+    std::vector<p2t::Point*> p2points;
+    for(const auto& pt : points)
+    {
+        p2t::Point * p = new (std::nothrow) p2t::Point(pt.x, pt.y);
+        p2points.push_back(p);
+    }
+    p2t::CDT cdt(p2points);
+    cdt.Triangulate();
+    std::vector<p2t::Triangle*> tris = cdt.GetTriangles();
+    
+    // we won't know the size of verts and indices until we process all of the triangles!
+    std::vector<V3F_C4B_T2F> verts;
+    std::vector<unsigned short> indices;
+
+    unsigned short idx = 0;
+    unsigned short vdx = 0;
+
+    for(const auto& ite : tris)
+    {
+        for(int i = 0; i < 3; ++i)
+        {
+            auto p = ite->GetPoint(i);
+            auto v3 = Vec3((float)p->x, (float)p->y, 0);
+            bool found = false;
+            unsigned short j;
+            auto length = vdx;
+            for(j = 0; j < length; j++)
+            {
+                if(verts[j].vertices == v3)
+                {
+                    found = true;
+                    break;
+                }
+            }
+            if(found)
+            {
+                //if we found the same vertex, don't add to verts, but use the same vertex with indices
+                indices.push_back(j);
+                idx++;
+            }
+            else
+            {
+                //vert does not exist yet, so we need to create a new one,
+                auto c4b = Color4B::WHITE;
+                auto t2f = Tex2F(0,0); // don't worry about tex coords now, we calculate that later
+                V3F_C4B_T2F vert = {v3,c4b,t2f};
+                verts.push_back(vert);
+                indices.push_back(vdx);
+                idx++;
+                vdx++;
+            }
+        }
+    }
+    for(auto j : p2points)
+    {
+        delete j;
+    };
+
+    // now that we know the size of verts and indices we can create the buffers
+    V3F_C4B_T2F* vertsBuf = new (std::nothrow) V3F_C4B_T2F[verts.size()];
+    memcpy(vertsBuf, verts.data(), verts.size() * sizeof(V3F_C4B_T2F));
+
+    unsigned short* indicesBuf = new (std::nothrow) unsigned short[indices.size()];
+    memcpy(indicesBuf, indices.data(), indices.size() * sizeof(short));
+
+    // Triangles should really use std::vector and not arrays for verts and indices. 
+    // Then the above memcpy would not be necessary
+    TrianglesCommand::Triangles triangles = { vertsBuf, indicesBuf, (unsigned int)verts.size(), (unsigned int)indices.size() };
+    return triangles;
+}
+
+void AutoPolygon::calculateUV(const Rect& rect, V3F_C4B_T2F* verts, ssize_t count)
+{
+    /*
+     whole texture UV coordination
+     0,0                  1,0
+     +---------------------+
+     |                     |0.1
+     |                     |0.2
+     |     +--------+      |0.3
+     |     |texRect |      |0.4
+     |     |        |      |0.5
+     |     |        |      |0.6
+     |     +--------+      |0.7
+     |                     |0.8
+     |                     |0.9
+     +---------------------+
+     0,1                  1,1
+     */
+    
+    CCASSERT(_width && _height, "please specify width and height for this AutoPolygon instance");
+    auto texWidth  = _width;
+    auto texHeight = _height;
+
+    auto end = &verts[count];
+    for(auto i = verts; i != end; ++i)
+    {
+        // for every point, offset with the center point
+        float u = (i->vertices.x*_scaleFactor + rect.origin.x) / texWidth;
+        float v = (rect.origin.y+rect.size.height - i->vertices.y*_scaleFactor) / texHeight;
+        i->texCoords.u = u;
+        i->texCoords.v = v;
+    }
+}
+
+Rect AutoPolygon::getRealRect(const Rect& rect)
+{
+    Rect realRect = rect;
+    //check rect to see if its zero
+    if(realRect.equals(Rect::ZERO))
+    {
+        //if the instance doesn't have width and height, then the whole operation is kaput
+        CCASSERT(_height && _width, "Please specify a width and height for this instance before using its functions");
+        realRect = Rect(0,0, (float)_width, (float)_height);
+    }
+    else{
+        //rect is specified, so convert to real rect
+        realRect = CC_RECT_POINTS_TO_PIXELS(rect);
+    }
+    return realRect;
+}
+
+PolygonInfo AutoPolygon::generateTriangles(const Rect& rect, float epsilon, float threshold)
+{
+    Rect realRect = getRealRect(rect);
+    auto p = trace(realRect, threshold);
+    p = reduce(p, realRect, epsilon);
+    p = expand(p, realRect, epsilon);
+    auto tri = triangulate(p);
+    calculateUV(realRect, tri.verts, tri.vertCount);
+    PolygonInfo ret;
+    ret.triangles = tri;
+    ret.setFilename(_filename);
+    ret.setRect(realRect);
+    return ret;
+}
+
+PolygonInfo AutoPolygon::generatePolygon(const std::string& filename, const Rect& rect, float epsilon, float threshold)
+{
+    AutoPolygon ap(filename);
+    return ap.generateTriangles(rect, epsilon, threshold);
+}

+ 285 - 0
3rdparty/cocos2dx/include/cocos/2d/CCAutoPolygon.h

@@ -0,0 +1,285 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef COCOS_2D_CCAUTOPOLYGON_H__
+#define COCOS_2D_CCAUTOPOLYGON_H__
+
+#include <string>
+#include <vector>
+#include "platform/CCImage.h"
+#include "renderer/CCTrianglesCommand.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/**
+ * PolygonInfo is an object holding the required data to display Sprites.
+ * It can be a simple as a triangle, or as complex as a whole 3D mesh
+ */
+class CC_DLL PolygonInfo
+{
+public:
+    /// @name Creators
+    /// @{
+    /**
+     * Creates an empty Polygon info
+     * @memberof PolygonInfo
+     * @return PolygonInfo object
+     */
+    PolygonInfo();
+
+    /**
+     * Create an polygoninfo from the data of another Polygoninfo
+     * @param other     another PolygonInfo to be copied
+     * @return duplicate of the other PolygonInfo
+     */
+    PolygonInfo(const PolygonInfo& other);
+    //  end of creators group
+    /// @}
+    
+    /**
+     * Copy the member of the other PolygonInfo
+     * @param other     another PolygonInfo to be copied
+     */
+    PolygonInfo& operator= (const PolygonInfo &other);
+    ~PolygonInfo();
+    
+    /**
+     * set the data to be a pointer to a quad
+     * the member verts will not be released when this PolygonInfo destructs
+     * as the verts memory are managed by other objects
+     * @param quad  a pointer to the V3F_C4B_T2F_Quad object
+     */
+    void setQuad(V3F_C4B_T2F_Quad *quad);
+
+    /**
+     * set the data to be a pointer to a number of Quads
+     * the member verts will not be released when this PolygonInfo destructs
+     * as the verts memory are managed by other objects
+     * @param quad  a pointer to the V3F_C4B_T2F_Quad quads
+     */
+    void setQuads(V3F_C4B_T2F_Quad *quads, int numberOfQuads);
+
+
+    /**
+     * set the data to be a pointer to a triangles
+     * the member verts will not be released when this PolygonInfo destructs
+     * as the verts memory are managed by other objects
+     * @param triangles  a pointer to the TrianglesCommand::Triangles object
+     */
+    void setTriangles(const TrianglesCommand::Triangles& triangles);
+
+    /**
+     * get vertex count
+     * @return number of vertices
+     */
+    unsigned int getVertCount() const;
+    
+    /**
+     * get triangles count
+     * @return number of triangles
+     */
+    unsigned int getTrianglesCount() const;
+    
+    /**
+     * get sum of all triangle area size
+     * @return sum of all triangle area size
+     */
+    float getArea() const;
+
+    const Rect& getRect() const { return _rect; }
+    void setRect(const Rect& rect) { _rect = rect; }
+    const std::string& getFilename() const { return _filename; }
+    void setFilename(const std::string& filename ) { _filename = filename; }
+
+    // FIXME: this should be a property, not a public ivar
+    TrianglesCommand::Triangles triangles;
+
+protected:
+    bool _isVertsOwner;
+    Rect _rect;
+    std::string _filename;
+
+private:
+    void releaseVertsAndIndices();
+};
+
+
+/**
+ * AutoPolygon is a helper Object
+ * AutoPolygon's purpose is to process an image into 2d polygon mesh in runtime
+ * It has functions for each step in the process, from tracing all the points, to triangulation
+ * the result can be then passed to Sprite::create() to create a Polygon Sprite
+ */
+class CC_DLL AutoPolygon
+{
+public:
+    /**
+     * create an AutoPolygon and initialize it with an image file
+     * the image must be a 32bit PNG for current version 3.7
+     * @param   filename    a path to image file, e.g., "scene1/monster.png".
+     * @return  an AutoPolygon object;
+     */
+    AutoPolygon(const std::string &filename);
+    
+    /**
+     * Destructor of AutoPolygon.
+     */
+    ~AutoPolygon();
+    
+    /**
+     * trace all the points along the outline of the image, 
+     * @warning must create AutoPolygon with filename to use this function
+     * @param   rect    a texture rect for specify an area of the image
+     * @param   threshold   the value when alpha is greater than this value will be counted as opaque, default to 0.0
+     * @return  a vector of vec2 of all the points found in clockwise order
+     * @code
+     * auto ap = AutoPolygon("grossini.png");
+     * auto rect = Rect(100, 100, 200, 200);
+     * std::vector<Vec2> points = ap.trace(rect);//default threshold is 0.0
+     * @endcode
+     */
+     std::vector<Vec2> trace(const cocos2d::Rect& rect, float threshold = 0.0f);
+    
+    /**
+     * reduce the amount of points so its faster for GPU to process and draw
+     * based on Ramer-Douglas-Peucker algorithm
+     * @param   points  a vector of Vec2 points as input
+     * @param   rect    a texture rect for specify an area of the image to avoid over reduction
+     * @param   epsilon the perpendicular distance where points smaller than this value will be discarded
+     * @return  a vector of Vec2 of the remaining points in clockwise order
+     * @code
+     * auto ap = AutoPolygon();
+     * std::vector<Vec2> reduced = ap.reduce(inputPoints, rect);//default epsilon is 2
+     * @endcode
+     */
+    std::vector<Vec2> reduce(const std::vector<Vec2>& points, const Rect& rect, float epsilon = 2.0f);
+    
+    /**
+     * expand the points along their edge, useful after you reduce the points that cuts into the sprite
+     * using ClipperLib
+     * @param   points  a vector of Vec2 points as input
+     * @param   rect    a texture rect for specify an area of the image, the expanded points will be clamped in this rect, ultimately resulting in a quad if the expansion is too great
+     * @param   epsilon the distance which the edges will expand
+     * @return  a vector of Vec2 as the result of the expansion
+     * @code
+     * auto ap = AutoPolygon();
+     * std::vector<Vec2> expanded = ap.expand(inputPoints, rect, 2.0);
+     * @endcode
+     */
+    std::vector<Vec2> expand(const std::vector<Vec2>& points, const Rect& rect, float epsilon);
+    
+    /**
+     * Triangulate the input points into triangles for rendering
+     * using poly2tri
+     * @warning points must be closed loop, cannot have 2 points sharing the same position and cannot intersect itself
+     * @param   points  a vector of vec2 points as input
+     * @return  a Triangles object with points and indices
+     * @code
+     * auto ap = AutoPolygon();
+     * TrianglesCommand::Triangles myPolygons = ap.triangulate(myPoints);
+     * @endcode
+     */
+    TrianglesCommand::Triangles triangulate(const std::vector<Vec2>& points);
+    
+    /**
+     * calculate the UV coordinates for each points based on a texture rect
+     * @warning This method requires the AutoPolygon object to know the texture file dimension
+     * @param   rect    a texture rect to specify where to map the UV
+     * @param   verts   a pointer to the verts array, served both as input and output verts
+     * @param   count   the count for the verts array
+     * @code
+     * auto ap = AutoPolygon("grossini.png");
+     * TrianglesCommand::Triangles myPolygons = ap.triangulate(myPoints);
+     * ap.calculateUV(rect, myPolygons.verts, 20);
+     * @endcode
+     */
+    void calculateUV(const Rect& rect, V3F_C4B_T2F* verts, ssize_t count);
+    
+    /**
+     * a helper function, packing trace, reduce, expand, triangulate and calculate uv in one function
+     * @param   rect    texture rect, use Rect::ZERO for the size of the texture, default is Rect::ZERO
+     * @param   epsilon the value used to reduce and expand, default to 2.0
+     * @param   threshold   the value where bigger than the threshold will be counted as opaque, used in trace
+     * @return  a PolygonInfo, to use with sprite
+     * @code
+     * auto ap = AutoPolygon("grossini.png");
+     * PolygonInfo myInfo = ap.generateTriangles();//use all default values
+     * auto sp1 = Sprite::create(myInfo);
+     * polygonInfo myInfo2 = ap.generateTriangles(Rect::ZERO, 5.0, 0.1);//ap can be reused to generate another set of PolygonInfo with different settings
+     * auto sp2 = Sprite::create(myInfo2);
+     * @endcode
+     */
+    PolygonInfo generateTriangles(const Rect& rect = Rect::ZERO, float epsilon = 2.0f, float threshold = 0.05f);
+    
+    /**
+     * a helper function, packing autoPolygon creation, trace, reduce, expand, triangulate and calculate uv in one function
+     * @warning if you want to repetitively generate polygons, consider create an AutoPolygon object, and use generateTriangles function, as it only reads the file once
+     * @param   filename     A path to image file, e.g., "scene1/monster.png".
+     * @param   rect    texture rect, use Rect::ZERO for the size of the texture, default is Rect::ZERO
+     * @param   epsilon the value used to reduce and expand, default to 2.0
+     * @param   threshold   the value where bigger than the threshold will be counted as opaque, used in trace
+     * @return  a PolygonInfo, to use with sprite
+     * @code
+     * auto sp = Sprite::create(AutoPolygon::generatePolygon("grossini.png"));
+     * @endcode
+     */
+    static PolygonInfo generatePolygon(const std::string& filename, const Rect& rect = Rect::ZERO, float epsilon = 2.0f, float threshold = 0.05f);
+protected:
+    Vec2 findFirstNoneTransparentPixel(const Rect& rect, float threshold);
+    std::vector<cocos2d::Vec2> marchSquare(const Rect& rect, const Vec2& first, float threshold);
+    unsigned int getSquareValue(unsigned int x, unsigned int y, const Rect& rect, float threshold);
+
+    unsigned char getAlphaByIndex(unsigned int i);
+    unsigned char getAlphaByPos(const Vec2& pos);
+
+    int getIndexFromPos(unsigned int x, unsigned int y) { return y*_width+x; }
+    cocos2d::Vec2 getPosFromIndex(unsigned int i) { return cocos2d::Vec2(static_cast<float>(i%_width), static_cast<float>(i/_width)); }
+
+    std::vector<cocos2d::Vec2> rdp(const std::vector<cocos2d::Vec2>& v, float optimization);
+    float perpendicularDistance(const cocos2d::Vec2& i, const cocos2d::Vec2& start, const cocos2d::Vec2& end);
+
+    //real rect is the size that is in scale with the texture file
+    Rect getRealRect(const Rect& rect);
+    
+    Image* _image;
+    unsigned char * _data;
+    std::string _filename;
+    unsigned int _width;
+    unsigned int _height;
+    float _scaleFactor;
+    unsigned int _threshold;
+};
+
+NS_CC_END
+
+#endif // #ifndef COCOS_2D_CCAUTOPOLYGON_H__

+ 459 - 0
3rdparty/cocos2dx/include/cocos/2d/CCCamera.cpp

@@ -0,0 +1,459 @@
+/****************************************************************************
+ Copyright (c) 2014-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2019 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+
+ Code based GamePlay3D's Camera: http://gameplay3d.org
+
+ ****************************************************************************/
+#include "2d/CCCamera.h"
+#include "2d/CCCameraBackgroundBrush.h"
+#include "base/CCDirector.h"
+#include "platform/CCGLView.h"
+#include "2d/CCScene.h"
+#include "renderer/CCRenderer.h"
+#include "renderer/CCQuadCommand.h"
+
+NS_CC_BEGIN
+
+Camera* Camera::_visitingCamera = nullptr;
+Viewport Camera::_defaultViewport;
+
+// start static methods
+
+Camera* Camera::create()
+{
+    Camera* camera = new (std::nothrow) Camera();
+    camera->initDefault();
+    camera->autorelease();
+    camera->setDepth(0);
+    
+    return camera;
+}
+
+Camera* Camera::createPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
+{
+    auto ret = new (std::nothrow) Camera();
+    if (ret)
+    {
+        ret->initPerspective(fieldOfView, aspectRatio, nearPlane, farPlane);
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+Camera* Camera::createOrthographic(float zoomX, float zoomY, float nearPlane, float farPlane)
+{
+    auto ret = new (std::nothrow) Camera();
+    if (ret)
+    {
+        ret->initOrthographic(zoomX, zoomY, nearPlane, farPlane);
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+Camera* Camera::getDefaultCamera()
+{
+    auto scene = Director::getInstance()->getRunningScene();
+    if(scene)
+    {
+        return scene->getDefaultCamera();
+    }
+
+    return nullptr;
+}
+
+const Viewport& Camera::getDefaultViewport()
+{
+    return _defaultViewport;
+}
+void Camera::setDefaultViewport(const Viewport& vp)
+{
+    _defaultViewport = vp;
+}
+
+const Camera* Camera::getVisitingCamera()
+{
+    return _visitingCamera;
+}
+
+// end static methods
+
+Camera::Camera()
+{
+    // minggo comment
+    // _frustum.setClipZ(true);
+}
+
+Camera::~Camera()
+{
+    CC_SAFE_RELEASE(_clearBrush);
+}
+
+const Mat4& Camera::getProjectionMatrix() const
+{
+    return _projection;
+}
+const Mat4& Camera::getViewMatrix() const
+{
+    Mat4 viewInv(getNodeToWorldTransform());
+    static int count = sizeof(float) * 16;
+    if (memcmp(viewInv.m, _viewInv.m, count) != 0)
+    {
+        _viewProjectionDirty = true;
+        _frustumDirty = true;
+        _viewInv = viewInv;
+        _view = viewInv.getInversed();
+    }
+    return _view;
+}
+void Camera::lookAt(const Vec3& lookAtPos, const Vec3& up)
+{
+    Vec3 upv = up;
+    upv.normalize();
+    Vec3 zaxis;
+    Vec3::subtract(this->getPosition3D(), lookAtPos, &zaxis);
+    zaxis.normalize();
+    
+    Vec3 xaxis;
+    Vec3::cross(upv, zaxis, &xaxis);
+    xaxis.normalize();
+    
+    Vec3 yaxis;
+    Vec3::cross(zaxis, xaxis, &yaxis);
+    yaxis.normalize();
+    Mat4  rotation;
+    rotation.m[0] = xaxis.x;
+    rotation.m[1] = xaxis.y;
+    rotation.m[2] = xaxis.z;
+    rotation.m[3] = 0;
+    
+    rotation.m[4] = yaxis.x;
+    rotation.m[5] = yaxis.y;
+    rotation.m[6] = yaxis.z;
+    rotation.m[7] = 0;
+    rotation.m[8] = zaxis.x;
+    rotation.m[9] = zaxis.y;
+    rotation.m[10] = zaxis.z;
+    rotation.m[11] = 0;
+    
+    Quaternion  quaternion;
+    Quaternion::createFromRotationMatrix(rotation,&quaternion);
+    quaternion.normalize();
+    setRotationQuat(quaternion);
+}
+
+const Mat4& Camera::getViewProjectionMatrix() const
+{
+    getViewMatrix();
+    if (_viewProjectionDirty)
+    {
+        _viewProjectionDirty = false;
+        Mat4::multiply(_projection, _view, &_viewProjection);
+    }
+    
+    return _viewProjection;
+}
+
+void Camera::setAdditionalProjection(const Mat4& mat)
+{
+    _projection = mat * _projection;
+    getViewProjectionMatrix();
+}
+
+bool Camera::initDefault()
+{
+    auto size = Director::getInstance()->getWinSize();
+    //create default camera
+    auto projection = Director::getInstance()->getProjection();
+    switch (projection)
+    {
+        case Director::Projection::_2D:
+        {
+            initOrthographic(size.width, size.height, -1024, 1024);
+            setPosition3D(Vec3(0.0f, 0.0f, 0.0f));
+            setRotation3D(Vec3(0.f, 0.f, 0.f));
+            break;
+        }
+        case Director::Projection::_3D:
+        {
+            float zeye = Director::getInstance()->getZEye();
+            initPerspective(60, (float)size.width / size.height, 10, zeye + size.height / 2.0f);
+            Vec3 eye(size.width/2, size.height/2.0f, zeye), center(size.width/2, size.height/2, 0.0f), up(0.0f, 1.0f, 0.0f);
+            setPosition3D(eye);
+            lookAt(center, up);
+            break;
+        }
+        default:
+            CCLOG("unrecognized projection");
+            break;
+    }
+    return true;
+}
+
+bool Camera::initPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
+{
+    _fieldOfView = fieldOfView;
+    _aspectRatio = aspectRatio;
+    _nearPlane = nearPlane;
+    _farPlane = farPlane;
+    Mat4::createPerspective(_fieldOfView, _aspectRatio, _nearPlane, _farPlane, &_projection);
+    _viewProjectionDirty = true;
+    _frustumDirty = true;
+    _type = Type::PERSPECTIVE;
+    
+    return true;
+}
+
+bool Camera::initOrthographic(float zoomX, float zoomY, float nearPlane, float farPlane)
+{
+    _zoom[0] = zoomX;
+    _zoom[1] = zoomY;
+    _nearPlane = nearPlane;
+    _farPlane = farPlane;
+    Mat4::createOrthographicOffCenter(0, _zoom[0], 0, _zoom[1], _nearPlane, _farPlane, &_projection);
+    _viewProjectionDirty = true;
+    _frustumDirty = true;
+    _type = Type::ORTHOGRAPHIC;
+    
+    return true;
+}
+
+Vec2 Camera::project(const Vec3& src) const
+{
+    Vec2 screenPos;
+    
+    auto viewport = Director::getInstance()->getWinSize();
+    Vec4 clipPos;
+    getViewProjectionMatrix().transformVector(Vec4(src.x, src.y, src.z, 1.0f), &clipPos);
+    
+    CCASSERT(clipPos.w != 0.0f, "clipPos.w can't be 0.0f!");
+    float ndcX = clipPos.x / clipPos.w;
+    float ndcY = clipPos.y / clipPos.w;
+    
+    screenPos.x = (ndcX + 1.0f) * 0.5f * viewport.width;
+    screenPos.y = (1.0f - (ndcY + 1.0f) * 0.5f) * viewport.height;
+    return screenPos;
+}
+
+Vec2 Camera::projectGL(const Vec3& src) const
+{
+    Vec2 screenPos;
+    
+    auto viewport = Director::getInstance()->getWinSize();
+    Vec4 clipPos;
+    getViewProjectionMatrix().transformVector(Vec4(src.x, src.y, src.z, 1.0f), &clipPos);
+    
+    CCASSERT(clipPos.w != 0.0f, "clipPos.w can't be 0.0f!");
+    float ndcX = clipPos.x / clipPos.w;
+    float ndcY = clipPos.y / clipPos.w;
+    
+    screenPos.x = (ndcX + 1.0f) * 0.5f * viewport.width;
+    screenPos.y = (ndcY + 1.0f) * 0.5f * viewport.height;
+    return screenPos;
+}
+
+Vec3 Camera::unproject(const Vec3& src) const
+{
+    Vec3 dst;
+    unproject(Director::getInstance()->getWinSize(), &src, &dst);
+    return dst;
+}
+
+Vec3 Camera::unprojectGL(const Vec3& src) const
+{
+    Vec3 dst;
+    unprojectGL(Director::getInstance()->getWinSize(), &src, &dst);
+    return dst;
+}
+
+void Camera::unproject(const Size& viewport, const Vec3* src, Vec3* dst) const
+{
+    CCASSERT(src && dst, "vec3 can not be null");
+    
+    Vec4 screen(src->x / viewport.width, ((viewport.height - src->y)) / viewport.height, src->z, 1.0f);
+    screen.x = screen.x * 2.0f - 1.0f;
+    screen.y = screen.y * 2.0f - 1.0f;
+    screen.z = screen.z * 2.0f - 1.0f;
+    
+    getViewProjectionMatrix().getInversed().transformVector(screen, &screen);
+    if (screen.w != 0.0f)
+    {
+        screen.x /= screen.w;
+        screen.y /= screen.w;
+        screen.z /= screen.w;
+    }
+    
+    dst->set(screen.x, screen.y, screen.z);
+}
+
+void Camera::unprojectGL(const Size& viewport, const Vec3* src, Vec3* dst) const
+{
+    CCASSERT(src && dst, "vec3 can not be null");
+    
+    Vec4 screen(src->x / viewport.width, src->y / viewport.height, src->z, 1.0f);
+    screen.x = screen.x * 2.0f - 1.0f;
+    screen.y = screen.y * 2.0f - 1.0f;
+    screen.z = screen.z * 2.0f - 1.0f;
+    
+    getViewProjectionMatrix().getInversed().transformVector(screen, &screen);
+    if (screen.w != 0.0f)
+    {
+        screen.x /= screen.w;
+        screen.y /= screen.w;
+        screen.z /= screen.w;
+    }
+    
+    dst->set(screen.x, screen.y, screen.z);
+}
+
+ bool Camera::isVisibleInFrustum(const AABB* aabb) const
+ {
+     if (_frustumDirty)
+     {
+         _frustum.initFrustum(this);
+         _frustumDirty = false;
+     }
+     return !_frustum.isOutOfFrustum(*aabb);
+ }
+
+float Camera::getDepthInView(const Mat4& transform) const
+{
+    Mat4 camWorldMat = getNodeToWorldTransform();
+    const Mat4 &viewMat = camWorldMat.getInversed();
+    float depth = -(viewMat.m[2] * transform.m[12] + viewMat.m[6] * transform.m[13] + viewMat.m[10] * transform.m[14] + viewMat.m[14]);
+    return depth;
+}
+
+void Camera::setDepth(int8_t depth)
+{
+    if (_depth != depth)
+    {
+        _depth = depth;
+        if (_scene)
+        {
+            //notify scene that the camera order is dirty
+            _scene->setCameraOrderDirty();
+        }
+    }
+}
+
+void Camera::onEnter()
+{
+    if (_scene == nullptr)
+    {
+        auto scene = getScene();
+        if (scene)
+        {
+            setScene(scene);
+        }
+    }
+    Node::onEnter();
+}
+
+void Camera::onExit()
+{
+    // remove this camera from scene
+    setScene(nullptr);
+    Node::onExit();
+}
+
+void Camera::setScene(Scene* scene)
+{
+    if (_scene != scene)
+    {
+        //remove old scene
+        if (_scene)
+        {
+            auto& cameras = _scene->_cameras;
+            auto it = std::find(cameras.begin(), cameras.end(), this);
+            if (it != cameras.end())
+                cameras.erase(it);
+            _scene = nullptr;
+        }
+        //set new scene
+        if (scene)
+        {
+            _scene = scene;
+            auto& cameras = _scene->_cameras;
+            auto it = std::find(cameras.begin(), cameras.end(), this);
+            if (it == cameras.end())
+            {
+                _scene->_cameras.push_back(this);
+                //notify scene that the camera order is dirty
+                _scene->setCameraOrderDirty();
+            }
+        }
+    }
+}
+
+void Camera::clearBackground()
+{
+    if (_clearBrush)
+    {
+        _clearBrush->drawBackground(this);
+    }
+}
+
+void Camera::apply()
+{
+    _viewProjectionUpdated = _transformUpdated;
+    applyViewport();
+}
+
+void Camera::applyViewport()
+{
+    Director::getInstance()->getRenderer()->setViewPort(_defaultViewport.x, _defaultViewport.y, _defaultViewport.w, _defaultViewport.h);
+}
+
+int Camera::getRenderOrder() const
+{
+    int result(0);
+    result = 127 <<8;
+    result += _depth;
+    return result;
+}
+
+void Camera::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
+{
+    _viewProjectionUpdated = _transformUpdated;
+    return Node::visit(renderer, parentTransform, parentFlags);
+}
+
+void Camera::setBackgroundBrush(CameraBackgroundBrush* clearBrush)
+{
+    CC_SAFE_RETAIN(clearBrush);
+    CC_SAFE_RELEASE(_clearBrush);
+    _clearBrush = clearBrush;
+}
+
+bool Camera::isBrushValid()
+{
+    return _clearBrush != nullptr && _clearBrush->isValid();
+}
+
+NS_CC_END

+ 317 - 0
3rdparty/cocos2dx/include/cocos/2d/CCCamera.h

@@ -0,0 +1,317 @@
+/****************************************************************************
+Copyright (c) 2014-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2019 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+
+ Code based GamePlay3D's Camera: http://gameplay3d.org
+
+ ****************************************************************************/
+#pragma once
+
+#include "2d/CCNode.h"
+#include "3d/CCFrustum.h"
+#include "renderer/CCQuadCommand.h"
+#include "renderer/CCCustomCommand.h"
+
+NS_CC_BEGIN
+
+class Scene;
+class CameraBackgroundBrush;
+
+/**
+ * Note: 
+ * Scene creates a default camera. And the default camera mask of Node is 1, therefore it can be seen by the default camera.
+ * During rendering the scene, it draws the objects seen by each camera in the added order except default camera. The default camera is the last one being drawn with.
+ * It's usually a good idea to render 3D objects in a separate camera.
+ * And set the 3d camera flag to CameraFlag::USER1 or anything else except DEFAULT. Dedicate The DEFAULT camera for UI, because it is rendered at last.
+ * You can change the camera order to get different result when depth test is not enabled.
+ * For each camera, transparent 3d sprite is rendered after opaque 3d sprite and other 2d objects.
+ */
+enum class CameraFlag
+{
+    DEFAULT = 1,
+    USER1 = 1 << 1,
+    USER2 = 1 << 2,
+    USER3 = 1 << 3,
+    USER4 = 1 << 4,
+    USER5 = 1 << 5,
+    USER6 = 1 << 6,
+    USER7 = 1 << 7,
+    USER8 = 1 << 8,
+};
+/**
+ * Defines a camera .
+ */
+class CC_DLL Camera :public Node
+{
+    friend class Scene;
+    friend class Director;
+    friend class EventDispatcher;
+public:
+    /**
+    * The type of camera.
+    */
+    enum class Type
+    {
+        PERSPECTIVE = 1,
+        ORTHOGRAPHIC = 2
+    };
+public:
+    /**
+    * Creates a perspective camera.
+    *
+    * @param fieldOfView The field of view for the perspective camera (normally in the range of 40-60 degrees).
+    * @param aspectRatio The aspect ratio of the camera (normally the width of the viewport divided by the height of the viewport).
+    * @param nearPlane The near plane distance.
+    * @param farPlane The far plane distance.
+    */
+    static Camera* createPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane);
+    /**
+    * Creates an orthographic camera.
+    *
+    * @param zoomX The zoom factor along the X-axis of the orthographic projection (the width of the ortho projection).
+    * @param zoomY The zoom factor along the Y-axis of the orthographic projection (the height of the ortho projection).
+    * @param nearPlane The near plane distance.
+    * @param farPlane The far plane distance.
+    */
+    static Camera* createOrthographic(float zoomX, float zoomY, float nearPlane, float farPlane);
+
+    /** create default camera, the camera type depends on Director::getProjection, the depth of the default camera is 0 */
+    static Camera* create();
+
+    /**
+     * Get the visiting camera , the visiting camera shall be set on Scene::render
+     */
+    static const Camera* getVisitingCamera();
+
+    static const Viewport& getDefaultViewport();
+    static void setDefaultViewport(const Viewport& vp);
+
+    /**
+     * Get the default camera of the current running scene.
+     */
+    static Camera* getDefaultCamera();
+
+    /**
+    * Gets the type of camera.
+    *
+    * @return The camera type.
+    */
+    Camera::Type getType() const { return _type; }
+
+    /**get & set Camera flag*/
+    CameraFlag getCameraFlag() const { return _cameraFlag; }
+    void setCameraFlag(CameraFlag flag) { _cameraFlag = flag; }
+
+    /**
+    * Make Camera looks at target
+    *
+    * @param target The target camera is point at
+    * @param up The up vector, usually it's Y axis
+    */
+    virtual void lookAt(const Vec3& target, const Vec3& up = Vec3::UNIT_Y);
+
+    /**
+    * Gets the camera's projection matrix.
+    *
+    * @return The camera projection matrix.
+    */
+    const Mat4& getProjectionMatrix() const;
+    /**
+    * Gets the camera's view matrix.
+    *
+    * @return The camera view matrix.
+    */
+    const Mat4& getViewMatrix() const;
+
+    /**get view projection matrix*/
+    const Mat4& getViewProjectionMatrix() const;
+    
+    /* convert the specified point in 3D world-space coordinates into the screen-space coordinates.
+     *
+     * Origin point at left top corner in screen-space.
+     * @param src The world-space position.
+     * @return The screen-space position.
+     */
+    Vec2 project(const Vec3& src) const;
+    
+    /* convert the specified point in 3D world-space coordinates into the GL-screen-space coordinates.
+     *
+     * Origin point at left bottom corner in GL-screen-space.
+     * @param src The 3D world-space position.
+     * @return The GL-screen-space position.
+     */
+    Vec2 projectGL(const Vec3& src) const;
+    
+    /**
+     * Convert the specified point of screen-space coordinate into the 3D world-space coordinate.
+     *
+     * Origin point at left top corner in screen-space.
+     * @param src The screen-space position.
+     * @return The 3D world-space position.
+     */
+    Vec3 unproject(const Vec3& src) const;
+    
+    /**
+     * Convert the specified point of GL-screen-space coordinate into the 3D world-space coordinate.
+     *
+     * Origin point at left bottom corner in GL-screen-space.
+     * @param src The GL-screen-space position.
+     * @return The 3D world-space position.
+     */
+    Vec3 unprojectGL(const Vec3& src) const;
+    
+    /**
+     * Convert the specified point of screen-space coordinate into the 3D world-space coordinate.
+     *
+     * Origin point at left top corner in screen-space.
+     * @param size The window size to use.
+     * @param src  The screen-space position.
+     * @param dst  The 3D world-space position.
+     */
+    void unproject(const Size& size, const Vec3* src, Vec3* dst) const;
+    
+    /**
+     * Convert the specified point of GL-screen-space coordinate into the 3D world-space coordinate.
+     *
+     * Origin point at left bottom corner in GL-screen-space.
+     * @param size The window size to use.
+     * @param src  The GL-screen-space position.
+     * @param dst  The 3D world-space position.
+     */
+    void unprojectGL(const Size& size, const Vec3* src, Vec3* dst) const;
+
+    /**
+     * Is this aabb visible in frustum
+     */
+    bool isVisibleInFrustum(const AABB* aabb) const;
+    
+    /**
+     * Get object depth towards camera
+     */
+    float getDepthInView(const Mat4& transform) const;
+    
+    /**
+     * set depth, camera with larger depth is drawn on top of camera with smaller depth, the depth of camera with CameraFlag::DEFAULT is 0, user defined camera is -1 by default
+     */
+    void setDepth(int8_t depth);
+    
+    /**
+     * get depth, camera with larger depth is drawn on top of camera with smaller depth, the depth of camera with CameraFlag::DEFAULT is 0, user defined camera is -1 by default
+     */
+    int8_t getDepth() const { return _depth; }
+    
+    /**
+     get rendered order
+     */
+    int getRenderOrder() const;
+    
+    /**
+     * Get the frustum's far plane.
+     */
+    float getFarPlane() const { return _farPlane; }
+
+    /**
+     * Get the frustum's near plane.
+     */
+    float getNearPlane() const { return _nearPlane; }
+    
+    //override
+    virtual void onEnter() override;
+    virtual void onExit() override;
+
+    /**
+     Before rendering scene with this camera, the background need to be cleared. It clears the depth buffer with max depth by default. Use setBackgroundBrush to modify the default behavior
+     */
+    void clearBackground();
+    /**
+     Apply the FBO, RenderTargets and viewport.
+     */
+    void apply();
+
+    /**
+     * Whether or not the viewprojection matrix was updated since the last frame.
+     * @return True if the viewprojection matrix was updated since the last frame.
+     */
+    bool isViewProjectionUpdated() const {return _viewProjectionUpdated;}
+
+    /**
+     * set the background brush. See CameraBackgroundBrush for more information.
+     * @param clearBrush Brush used to clear the background
+     */
+    void setBackgroundBrush(CameraBackgroundBrush* clearBrush);
+
+    /**
+     * Get clear brush
+     */
+    CameraBackgroundBrush* getBackgroundBrush() const { return _clearBrush; }
+
+    virtual void visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;
+
+    bool isBrushValid();
+
+CC_CONSTRUCTOR_ACCESS:
+    Camera();
+    ~Camera();
+
+    /**
+     * Set the scene,this method shall not be invoke manually
+     */
+    void setScene(Scene* scene);
+
+    /**set additional matrix for the projection matrix, it multiplies mat to projection matrix when called, used by WP8*/
+    void setAdditionalProjection(const Mat4& mat);
+
+    /** init camera */
+    bool initDefault();
+    bool initPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane);
+    bool initOrthographic(float zoomX, float zoomY, float nearPlane, float farPlane);
+    void applyViewport();
+
+protected:
+    static Camera* _visitingCamera;
+    static Viewport _defaultViewport;
+
+    Scene* _scene = nullptr; //Scene camera belongs to
+    Mat4 _projection;
+    mutable Mat4 _view;
+    mutable Mat4 _viewInv;
+    mutable Mat4 _viewProjection;
+
+    Vec3 _up;
+    Camera::Type _type;
+    float _fieldOfView = 0.f;
+    float _zoom[2] = {0.f};
+    float _aspectRatio = 0.f;
+    float _nearPlane = 0.f;
+    float _farPlane = 0.f;
+    mutable bool  _viewProjectionDirty = true;
+    bool _viewProjectionUpdated = false; //Whether or not the viewprojection matrix was updated since the last frame.
+    CameraFlag _cameraFlag = CameraFlag::DEFAULT; // camera flag
+    mutable Frustum _frustum;   // camera frustum
+    mutable bool _frustumDirty = true;
+    int8_t  _depth = -1;                 //camera depth, the depth of camera with CameraFlag::DEFAULT flag is 0 by default, a camera with larger depth is drawn on top of camera with smaller depth
+
+    CameraBackgroundBrush* _clearBrush = nullptr; //brush used to clear the back ground
+};
+
+NS_CC_END

+ 506 - 0
3rdparty/cocos2dx/include/cocos/2d/CCCameraBackgroundBrush.cpp

@@ -0,0 +1,506 @@
+/****************************************************************************
+ Copyright (c) 2015-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+
+ ****************************************************************************/
+#include "2d/CCCameraBackgroundBrush.h"
+#include <stddef.h> // offsetof
+#include "base/ccTypes.h"
+#include "2d/CCCamera.h"
+#include "base/ccMacros.h"
+#include "base/ccUtils.h"
+#include "base/CCConfiguration.h"
+#include "base/CCDirector.h"
+#include "renderer/CCRenderer.h"
+#include "renderer/CCRenderState.h"
+#include "renderer/CCTextureCube.h"
+#include "renderer/ccShaders.h"
+
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+#include "base/CCEventCustom.h"
+#include "base/CCEventListenerCustom.h"
+#include "base/CCEventType.h"
+#include "base/CCEventDispatcher.h"
+#endif
+
+NS_CC_BEGIN
+
+CameraBackgroundBrush::CameraBackgroundBrush()
+{
+    
+}
+
+CameraBackgroundBrush::~CameraBackgroundBrush()
+{
+    CC_SAFE_RELEASE_NULL(_programState);
+}
+
+CameraBackgroundBrush* CameraBackgroundBrush::createNoneBrush()
+{
+    auto ret = new (std::nothrow) CameraBackgroundBrush();
+    ret->init();
+    
+    ret->autorelease();
+    return ret;
+}
+
+CameraBackgroundColorBrush* CameraBackgroundBrush::createColorBrush(const Color4F& color, float depth)
+{
+    return CameraBackgroundColorBrush::create(color, depth);
+}
+
+CameraBackgroundDepthBrush* CameraBackgroundBrush::createDepthBrush(float depth)
+{
+    return CameraBackgroundDepthBrush::create(depth);
+}
+
+CameraBackgroundSkyBoxBrush* CameraBackgroundBrush::createSkyboxBrush(const std::string& positive_x, const std::string& negative_x, const std::string& positive_y, const std::string& negative_y, const std::string& positive_z, const std::string& negative_z)
+{
+    return CameraBackgroundSkyBoxBrush::create(positive_x, negative_x, positive_y, negative_y, positive_z, negative_z);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+CameraBackgroundDepthBrush::CameraBackgroundDepthBrush()
+: _depth(0.f)
+, _clearColor(false)
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+    , _backToForegroundListener(nullptr)
+#endif
+{
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+    _backToForegroundListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom*)
+        {
+            initBuffer();
+        }
+    );
+    Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_backToForegroundListener, -1);
+#endif
+}
+CameraBackgroundDepthBrush::~CameraBackgroundDepthBrush()
+{
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+    Director::getInstance()->getEventDispatcher()->removeEventListener(_backToForegroundListener);
+#endif
+}
+
+CameraBackgroundDepthBrush* CameraBackgroundDepthBrush::create(float depth)
+{
+    auto ret = new (std::nothrow) CameraBackgroundDepthBrush();
+    
+    if (nullptr != ret && ret->init())
+    {
+        ret->_depth = depth;
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+
+    return ret;
+}
+
+bool CameraBackgroundDepthBrush::init()
+{
+    CC_SAFE_RELEASE_NULL(_programState);
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::CAMERA_CLEAR);
+    _programState = new backend::ProgramState(program);
+
+    _locDepth = _programState->getUniformLocation("dpeth");
+
+    auto &pipelineDescriptor = _customCommand.getPipelineDescriptor();
+    pipelineDescriptor.programState = _programState;
+
+    auto layout = _programState->getVertexLayout();
+    const auto& attributeInfo = _programState->getProgram()->getActiveAttributes();
+    auto iter = attributeInfo.find("a_position");
+    if(iter != attributeInfo.end())
+    {
+        layout->setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT3, offsetof(V3F_C4B_T2F, vertices), false);
+    }
+    iter = attributeInfo.find("a_color");
+    if(iter != attributeInfo.end())
+    {
+        layout->setAttribute("a_color", iter->second.location, backend::VertexFormat::UBYTE4, offsetof(V3F_C4B_T2F, colors), true);
+    }
+    iter = attributeInfo.find("a_texCoord");
+    if(iter != attributeInfo.end())
+    {
+        layout->setAttribute("a_texCoord", iter->second.location, backend::VertexFormat::FLOAT2, offsetof(V3F_C4B_T2F, texCoords), true);
+    }
+    layout->setLayout(sizeof(_vertices[0]));
+
+    _vertices.resize(4);
+    _vertices[0].vertices = Vec3(-1, -1, 0);
+    _vertices[1].vertices = Vec3(1, -1, 0);
+    _vertices[2].vertices = Vec3(1, 1, 0);
+    _vertices[3].vertices = Vec3(-1, 1, 0);
+
+    _vertices[0].colors = _vertices[1].colors = _vertices[2].colors = _vertices[3].colors = Color4B(0, 0, 0, 1);
+
+    _vertices[0].texCoords = Tex2F(0, 0);
+    _vertices[1].texCoords = Tex2F(1, 0);
+    _vertices[2].texCoords = Tex2F(1, 1);
+    _vertices[3].texCoords = Tex2F(0, 1);
+
+    _customCommand.setBeforeCallback(CC_CALLBACK_0(CameraBackgroundDepthBrush::onBeforeDraw, this));
+    _customCommand.setAfterCallback(CC_CALLBACK_0(CameraBackgroundDepthBrush::onAfterDraw, this));
+
+    initBuffer();
+    return true;
+}
+
+void CameraBackgroundDepthBrush::initBuffer()
+{
+    uint16_t indices[6] = { 0, 1, 2, 2, 3, 0 };
+    _customCommand.createVertexBuffer(sizeof(_vertices[0]), _vertices.size(), CustomCommand::BufferUsage::STATIC);
+    _customCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, sizeof(indices) / sizeof(indices[0]), CustomCommand::BufferUsage::STATIC);
+
+    _customCommand.updateVertexBuffer(_vertices.data(), sizeof(_vertices[0]) * _vertices.size());
+    _customCommand.updateIndexBuffer(indices, sizeof(indices));
+}
+
+void CameraBackgroundDepthBrush::drawBackground(Camera* /*camera*/)
+{
+    // `clear screen` should be executed before other commands
+    _groupCommand.init(-1.0f);
+    _customCommand.init(0.0f);
+
+    auto *renderer = Director::getInstance()->getRenderer();
+
+    renderer->addCommand(&_groupCommand);
+    renderer->pushGroup(_groupCommand.getRenderQueueID());
+
+    auto &pipelineDescriptor    = _customCommand.getPipelineDescriptor();
+    auto &blend                 = pipelineDescriptor.blendDescriptor;
+    blend.writeMask             = _clearColor ? backend::ColorWriteMask::ALL : backend::ColorWriteMask::NONE;
+
+    //draw
+    _programState->setUniform(_locDepth, &_depth, sizeof(_depth));
+    
+    renderer->addCommand(&_customCommand);
+
+    renderer->popGroup();
+}
+
+void CameraBackgroundDepthBrush::onBeforeDraw()
+{
+    auto *renderer = Director::getInstance()->getRenderer();
+    _stateBlock.stencilWriteMask    = renderer->getStencilWriteMask();
+    _stateBlock.depthTest           = renderer->getDepthTest();
+    _stateBlock.compareFunc         = renderer->getDepthCompareFunction();
+
+    renderer->setStencilWriteMask(0);
+    renderer->setDepthTest(true);
+    renderer->setDepthCompareFunction(backend::CompareFunction::ALWAYS);
+}
+
+void CameraBackgroundDepthBrush::onAfterDraw()
+{
+    auto *renderer = Director::getInstance()->getRenderer();
+    renderer->setStencilWriteMask(_stateBlock.stencilWriteMask);
+    renderer->setDepthTest(_stateBlock.depthTest);
+    renderer->setDepthCompareFunction(_stateBlock.compareFunc);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+CameraBackgroundColorBrush::CameraBackgroundColorBrush()
+: _color(0.f, 0.f, 0.f, 0.f)
+{
+    
+}
+
+CameraBackgroundColorBrush::~CameraBackgroundColorBrush()
+{
+    
+}
+
+bool CameraBackgroundColorBrush::init()
+{
+    CameraBackgroundDepthBrush::init();
+    this->_clearColor = true;
+    return true;
+}
+
+void CameraBackgroundColorBrush::drawBackground(Camera* camera)
+{
+    BlendFunc op = { BlendFunc::ALPHA_NON_PREMULTIPLIED.src, BlendFunc::ALPHA_NON_PREMULTIPLIED.dst };
+
+    auto &blend = _customCommand.getPipelineDescriptor().blendDescriptor;
+    blend.sourceRGBBlendFactor      = blend.sourceAlphaBlendFactor = op.src;
+    blend.destinationRGBBlendFactor = blend.destinationAlphaBlendFactor = op.dst;
+    blend.blendEnabled              = true;
+
+    CameraBackgroundDepthBrush::drawBackground(camera);
+}
+
+void CameraBackgroundColorBrush::setColor(const Color4F& color)
+{
+    for (auto &vert : _vertices)
+    {
+        vert.colors = Color4B(color);
+    }
+    _customCommand.updateVertexBuffer(_vertices.data(), sizeof(_vertices[0]) * _vertices.size());
+}
+
+CameraBackgroundColorBrush* CameraBackgroundColorBrush::create(const Color4F& color, float depth)
+{
+    auto ret = new (std::nothrow) CameraBackgroundColorBrush();
+
+    if (nullptr != ret && ret->init())
+    {
+        ret->setColor(color);
+        ret->setDepth(depth);
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+
+    return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+CameraBackgroundSkyBoxBrush::CameraBackgroundSkyBoxBrush()
+: _texture(nullptr)
+, _actived(true)
+, _textureValid(true)
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+, _backToForegroundListener(nullptr)
+#endif
+{
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+    _backToForegroundListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED,
+                                                            [this](EventCustom*)
+                                                            {
+                                                                initBuffer();
+                                                            }
+                                                            );
+    Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_backToForegroundListener, -1);
+#endif
+}
+
+CameraBackgroundSkyBoxBrush::~CameraBackgroundSkyBoxBrush()
+{
+    CC_SAFE_RELEASE(_texture);
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+    Director::getInstance()->getEventDispatcher()->removeEventListener(_backToForegroundListener);
+#endif
+}
+
+CameraBackgroundSkyBoxBrush* CameraBackgroundSkyBoxBrush::create(
+    const std::string& positive_x,
+    const std::string& negative_x,
+    const std::string& positive_y,
+    const std::string& negative_y,
+    const std::string& positive_z,
+    const std::string& negative_z
+    )
+{
+    CameraBackgroundSkyBoxBrush* ret = nullptr;
+
+    auto texture = TextureCube::create(positive_x,
+                                       negative_x,
+                                       positive_y,
+                                       negative_y,
+                                       positive_z,
+                                       negative_z);
+
+    if (texture != nullptr)
+    {
+
+        Texture2D::TexParams tRepeatParams;
+        tRepeatParams.magFilter = backend::SamplerFilter::LINEAR;
+        tRepeatParams.minFilter = backend::SamplerFilter::LINEAR;
+        tRepeatParams.sAddressMode = backend::SamplerAddressMode::CLAMP_TO_EDGE;
+        tRepeatParams.tAddressMode = backend::SamplerAddressMode::CLAMP_TO_EDGE;
+        texture->setTexParameters(tRepeatParams);
+
+        ret = new (std::nothrow) CameraBackgroundSkyBoxBrush;
+
+        if (nullptr != ret && ret->init())
+        {
+            ret->setTexture(texture);
+            ret->autorelease();
+        }
+        else
+        {
+            CC_SAFE_DELETE(texture);
+            CC_SAFE_DELETE(ret);
+        }
+    }
+
+    return ret;
+}
+
+CameraBackgroundSkyBoxBrush* CameraBackgroundSkyBoxBrush::create()
+{
+    auto ret = new (std::nothrow) CameraBackgroundSkyBoxBrush();
+    
+    if (nullptr != ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+
+    return ret;
+}
+
+void CameraBackgroundSkyBoxBrush::drawBackground(Camera* camera)
+{
+    // `clear screen` should be executed before other commands
+    _groupCommand.init(-1.0f);
+    _customCommand.init(0.0f);
+
+    if (!_actived)
+        return;
+
+    Mat4 cameraModelMat = camera->getNodeToWorldTransform();
+
+    auto &pipelineDescriptor = _customCommand.getPipelineDescriptor();
+    pipelineDescriptor.blendDescriptor.blendEnabled = false;
+
+    Vec4 color(1.f, 1.f, 1.f, 1.f);
+    cameraModelMat.m[12] = cameraModelMat.m[13] = cameraModelMat.m[14] = 0;
+
+    _programState->setUniform(_uniformColorLoc, &color, sizeof(color));
+    _programState->setUniform(_uniformCameraRotLoc, cameraModelMat.m, sizeof(cameraModelMat.m));
+
+    auto *renderer = Director::getInstance()->getRenderer();
+
+    renderer->addCommand(&_groupCommand);
+    renderer->pushGroup(_groupCommand.getRenderQueueID());
+
+    renderer->addCommand(&_customCommand);
+
+    renderer->popGroup();
+   
+    CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 8);
+    
+}
+
+bool CameraBackgroundSkyBoxBrush::init()
+{
+
+    _customCommand.setBeforeCallback(CC_CALLBACK_0(CameraBackgroundSkyBoxBrush::onBeforeDraw, this));
+    _customCommand.setAfterCallback(CC_CALLBACK_0(CameraBackgroundSkyBoxBrush::onAfterDraw, this));
+
+    CC_SAFE_RELEASE_NULL(_programState);
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::SKYBOX_3D);
+    _programState           = new backend::ProgramState(program);
+    _uniformColorLoc        = _programState->getUniformLocation("u_color");
+    _uniformCameraRotLoc    = _programState->getUniformLocation("u_cameraRot");
+    _uniformEnvLoc          = _programState->getUniformLocation("u_Env");
+
+
+    auto &pipelineDescriptor                            = _customCommand.getPipelineDescriptor();
+    auto layout                                         = _programState->getVertexLayout();
+    pipelineDescriptor.programState                     = _programState;
+    // disable blend
+    pipelineDescriptor.blendDescriptor.blendEnabled     = false;
+
+    layout->setAttribute(shaderinfos::attribute::ATTRIBUTE_NAME_POSITION, 0, backend::VertexFormat::FLOAT3, 0, false);
+    layout->setLayout(sizeof(Vec3));
+
+    initBuffer();
+
+    return true;
+}
+
+void CameraBackgroundSkyBoxBrush::initBuffer()
+{
+    // init vertex buffer object
+    Vec3 vexBuf[] =
+    {
+        Vec3(1, -1, 1),  Vec3(1, 1, 1),  Vec3(-1, 1, 1),  Vec3(-1, -1, 1),
+        Vec3(1, -1, -1), Vec3(1, 1, -1), Vec3(-1, 1, -1), Vec3(-1, -1, -1)
+    };
+
+    _customCommand.createVertexBuffer(sizeof(vexBuf[0]), sizeof(vexBuf) / sizeof(vexBuf[0]), CustomCommand::BufferUsage::STATIC);
+    _customCommand.updateVertexBuffer(vexBuf, sizeof(vexBuf));
+
+    // init index buffer object
+    uint16_t idxBuf[] = { 
+        2, 1, 0, 3, 2, 0, // font
+        1, 5, 4, 1, 4, 0, // right
+        4, 5, 6, 4, 6, 7, // back
+        7, 6, 2, 7, 2, 3, // left
+        2, 6, 5, 2, 5, 1, // up
+        3, 0, 4, 3, 4, 7  // down
+    };
+    _customCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, sizeof(idxBuf) / sizeof(idxBuf[0]), CustomCommand::BufferUsage::STATIC);
+    _customCommand.updateIndexBuffer(idxBuf, sizeof(idxBuf));
+}
+
+void CameraBackgroundSkyBoxBrush::setTexture(TextureCube*  texture)
+{
+    CC_SAFE_RETAIN(texture);
+    CC_SAFE_RELEASE(_texture);
+    _texture = texture;
+    _programState->setTexture(_uniformEnvLoc, 0, _texture->getBackendTexture());
+}
+
+bool CameraBackgroundSkyBoxBrush::isActived() const
+{
+    return _actived;
+}
+void CameraBackgroundSkyBoxBrush::setActived(bool actived)
+{
+    _actived        = actived;
+}
+
+void CameraBackgroundSkyBoxBrush::setTextureValid(bool valid)
+{
+    _textureValid   = valid;
+}
+
+bool CameraBackgroundSkyBoxBrush::isValid()
+{
+    return _actived;
+}
+
+void CameraBackgroundSkyBoxBrush::onBeforeDraw()
+{
+    auto *renderer = Director::getInstance()->getRenderer();
+    _stateBlock.depthTest   = renderer->getDepthTest();
+    _stateBlock.depthWrite  = renderer->getDepthWrite();
+    _stateBlock.depthFunc   = renderer->getDepthCompareFunction();
+    _stateBlock.cullMode    = renderer->getCullMode();
+}
+
+void CameraBackgroundSkyBoxBrush::onAfterDraw()
+{
+    auto *renderer = Director::getInstance()->getRenderer();
+    renderer->setDepthTest(_stateBlock.depthTest);
+    renderer->setDepthWrite(_stateBlock.depthWrite);
+    renderer->setDepthCompareFunction(_stateBlock.depthFunc);
+    renderer->setCullMode(_stateBlock.cullMode);
+}
+
+NS_CC_END

+ 316 - 0
3rdparty/cocos2dx/include/cocos/2d/CCCameraBackgroundBrush.h

@@ -0,0 +1,316 @@
+/****************************************************************************
+ Copyright (c) 2015-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ 
+ ****************************************************************************/
+#pragma once
+
+#include "base/ccTypes.h"
+#include "base/CCRef.h"
+#include "base/CCEventListenerCustom.h"
+#include "3d/CCFrustum.h"
+#include "renderer/CCQuadCommand.h"
+#include "renderer/CCCustomCommand.h"
+#include "renderer/CCGroupCommand.h"
+#include "renderer/backend/Types.h"
+#include <vector>
+
+NS_CC_BEGIN
+
+class CameraBackgroundColorBrush;
+class CameraBackgroundDepthBrush;
+class CameraBackgroundSkyBoxBrush;
+class Camera;
+
+namespace backend {
+    class ProgramState;
+    class Buffer;
+}
+
+/**
+ * Defines a brush to clear the background of camera.
+ * There are 4 types of brush. None brush do nothing, Depth brush clear background with given depth, Color brush clear background with given color and depth, Skybox brush clear the background with a skybox. Camera uses depth brush by default.
+ */
+class CC_DLL CameraBackgroundBrush : public Ref
+{
+public:
+    /**
+     * Brush types. There are 4 types of brush. See CameraBackgroundDepthBrush, CameraBackgroundColorBrush, CameraBackgroundSkyBoxBrush for more information.
+     */
+    enum class BrushType
+    {
+        NONE, //none brush
+        DEPTH, // depth brush. See CameraBackgroundDepthBrush
+        COLOR, // color brush. See CameraBackgroundColorBrush
+        SKYBOX, // skybox brush. See CameraBackgroundSkyBoxBrush
+    };
+    
+    /**
+     * get brush type
+     * @return BrushType
+     */
+    virtual BrushType getBrushType() const { return BrushType::NONE; }
+    
+    /**
+     * Creates a none brush, it does nothing when clear the background
+     * @return Created brush.
+     */
+    static CameraBackgroundBrush* createNoneBrush();
+    
+    /**
+     * Creates a depth brush, which clears depth buffer with a given depth.
+     * @param depth Depth used to clear depth buffer
+     * @return Created brush
+     */
+    static CameraBackgroundDepthBrush* createDepthBrush(float depth = 1.f);
+    
+    /**
+     * Creates a color brush
+     * @param color Color of brush
+     * @param depth Depth used to clear depth buffer
+     * @return Created brush
+     */
+    static CameraBackgroundColorBrush* createColorBrush(const Color4F& color, float depth);
+    
+    
+    /** Creates a Skybox brush with 6 textures.
+     @param positive_x texture for the right side of the texture cube face.
+     @param negative_x texture for the up side of the texture cube face.
+     @param positive_y texture for the top side of the texture cube face
+     @param negative_y texture for the bottom side of the texture cube face
+     @param positive_z texture for the forward side of the texture cube face.
+     @param negative_z texture for the rear side of the texture cube face.
+     @return  A new brush inited with given parameters.
+     */
+    static CameraBackgroundSkyBoxBrush* createSkyboxBrush(const std::string& positive_x, const std::string& negative_x,
+                                                          const std::string& positive_y, const std::string& negative_y,
+                                                          const std::string& positive_z, const std::string& negative_z);
+    /**
+     * draw the background
+     */
+    virtual void drawBackground(Camera* /*camera*/) {}
+
+    virtual bool isValid() { return true; }
+
+CC_CONSTRUCTOR_ACCESS :
+    CameraBackgroundBrush();
+    virtual ~CameraBackgroundBrush();
+
+    virtual bool init() { return true; }
+    
+protected:
+    backend::ProgramState* _programState = nullptr;
+};
+
+/**
+ * Depth brush clear depth buffer with given depth
+ */
+class CC_DLL CameraBackgroundDepthBrush : public CameraBackgroundBrush
+{
+public:
+    /**
+     * Create a depth brush
+     * @param depth Depth used to clear the depth buffer
+     * @return Created brush
+     */
+    static CameraBackgroundDepthBrush* create(float depth);
+    
+    /**
+     * Get brush type. Should be BrushType::DEPTH
+     * @return brush type
+     */
+    virtual BrushType getBrushType() const override { return BrushType::DEPTH; }
+    
+    /**
+     * Draw background
+     */
+    virtual void drawBackground(Camera* camera) override;
+    
+    /**
+     * Set depth
+     * @param depth Depth used to clear depth buffer
+     */
+    void setDepth(float depth) { _depth = depth; }
+    
+CC_CONSTRUCTOR_ACCESS:
+    CameraBackgroundDepthBrush();
+    virtual ~CameraBackgroundDepthBrush();
+
+    virtual bool init() override;
+private:
+    void onBeforeDraw();
+    void onAfterDraw();
+protected:
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+    EventListenerCustom* _backToForegroundListener;
+#endif
+    void initBuffer();
+
+protected:
+    float _depth;
+    backend::UniformLocation _locDepth;
+    CustomCommand _customCommand;
+    GroupCommand _groupCommand;
+
+    bool _clearColor;
+    std::vector<V3F_C4B_T2F> _vertices;
+    struct {
+        uint32_t stencilWriteMask = 0;
+        bool depthTest = true;
+        backend::CompareFunction compareFunc = backend::CompareFunction::ALWAYS;
+    } _stateBlock;
+
+};
+
+/**
+ * Color brush clear buffer with given depth and color
+ */
+class CC_DLL CameraBackgroundColorBrush : public CameraBackgroundDepthBrush
+{
+public:
+    /**
+     * Get brush type. Should be BrushType::COLOR
+     * @return brush type
+     */
+    virtual BrushType getBrushType() const override { return BrushType::COLOR; }
+    
+    /**
+     * Create a color brush
+     * @param color Color used to clear the color buffer
+     * @param depth Depth used to clear the depth buffer
+     * @return Created brush
+     */
+    static CameraBackgroundColorBrush* create(const Color4F& color, float depth);
+    
+    /**
+     * Draw background
+     */
+    virtual void drawBackground(Camera* camera) override;
+    
+    /**
+     * Set clear color
+     * @param color Color used to clear the color buffer
+     */
+    void setColor(const Color4F& color);
+
+CC_CONSTRUCTOR_ACCESS:
+    CameraBackgroundColorBrush();
+    virtual ~CameraBackgroundColorBrush();
+
+    virtual bool init() override;
+    
+protected:
+    Color4F _color;
+};
+
+class TextureCube;
+class GLProgramState;
+class EventListenerCustom;
+
+/**
+ * Skybox brush clear buffer with a skybox
+ */
+class CC_DLL CameraBackgroundSkyBoxBrush : public CameraBackgroundBrush
+{
+public:
+    /**
+     * Get brush type. Should be BrushType::SKYBOX
+     * @return brush type
+     */
+    virtual BrushType getBrushType() const override { return BrushType::SKYBOX; }
+    
+    /** Creates a Skybox brush with 6 textures.
+     @param positive_x texture for the right side of the texture cube face.
+     @param negative_x texture for the up side of the texture cube face.
+     @param positive_y texture for the top side of the texture cube face
+     @param negative_y texture for the bottom side of the texture cube face
+     @param positive_z texture for the forward side of the texture cube face.
+     @param negative_z texture for the rear side of the texture cube face.
+     @return  A new brush inited with given parameters.
+     */
+    static CameraBackgroundSkyBoxBrush* create(const std::string& positive_x, const std::string& negative_x,
+                                        const std::string& positive_y, const std::string& negative_y,
+                                        const std::string& positive_z, const std::string& negative_z);
+    
+    /** Creates a Skybox brush with 6 textures.
+     */
+    static CameraBackgroundSkyBoxBrush* create();
+    /**
+     * Set skybox texture 
+     * @param texture Skybox texture
+     */
+    void setTexture(TextureCube*  texture);
+    
+    /**
+     * Draw background
+     */
+    virtual void drawBackground(Camera* camera) override;
+
+    bool isActived() const;
+    void setActived(bool actived);
+    virtual void setTextureValid(bool valid);
+    virtual bool isValid()override;
+
+CC_CONSTRUCTOR_ACCESS :
+    CameraBackgroundSkyBoxBrush();
+    virtual ~CameraBackgroundSkyBoxBrush();
+    
+    /**
+     * init Skybox.
+     */
+    virtual bool init() override;
+
+private:
+    void onBeforeDraw();
+    void onAfterDraw();
+
+protected:
+    void initBuffer();
+    
+    TextureCube*  _texture;
+    
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+    EventListenerCustom* _backToForegroundListener;
+#endif
+
+private:
+    bool _actived;
+    bool _textureValid;
+
+    CustomCommand _customCommand;
+    GroupCommand _groupCommand;
+
+    backend::UniformLocation _uniformColorLoc;
+    backend::UniformLocation _uniformCameraRotLoc;
+    backend::UniformLocation _uniformEnvLoc;
+
+    struct {
+        bool depthTest = true;
+        bool depthWrite = true;
+        backend::CompareFunction depthFunc = backend::CompareFunction::ALWAYS;
+        backend::CullMode cullMode = backend::CullMode::BACK;
+    }_stateBlock;
+};
+
+NS_CC_END
+

+ 376 - 0
3rdparty/cocos2dx/include/cocos/2d/CCClippingNode.cpp

@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2012      Pierre-David Bélanger
+ * Copyright (c) 2012      cocos2d-x.org
+ * Copyright (c) 2013-2016 Chukong Technologies Inc.
+ * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ *
+ * cocos2d-x: http://www.cocos2d-x.org
+ *
+ * Permission is hereby granted, free 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.
+ *
+ */
+#include "2d/CCClippingNode.h"
+#include "renderer/CCRenderer.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/ProgramState.h"
+#include "base/CCDirector.h"
+#include "base/CCStencilStateManager.h"
+
+NS_CC_BEGIN
+
+ClippingNode::ClippingNode()
+: _stencil(nullptr)
+, _stencilStateManager(new StencilStateManager())
+{
+}
+
+ClippingNode::~ClippingNode()
+{
+    if (_stencil)
+    {
+        _stencil->stopAllActions();
+        _stencil->release();
+    }
+    CC_SAFE_DELETE(_stencilStateManager);
+}
+
+ClippingNode* ClippingNode::create()
+{
+    ClippingNode *ret = new (std::nothrow) ClippingNode();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    
+    return ret;
+}
+
+ClippingNode* ClippingNode::create(Node *pStencil)
+{
+    ClippingNode *ret = new (std::nothrow) ClippingNode();
+    if (ret && ret->init(pStencil))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    
+    return ret;
+}
+
+bool ClippingNode::init()
+{
+    return init(nullptr);
+}
+
+bool ClippingNode::init(Node *stencil)
+{
+    setStencil(stencil);
+    return true;
+}
+
+void ClippingNode::onEnter()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnter))
+            return;
+    }
+#endif
+    
+    Node::onEnter();
+    
+    if (_stencil != nullptr)
+    {
+        _stencil->onEnter();
+    }
+    else
+    {
+        CCLOG("ClippingNode warning: _stencil is nil.");
+    }
+}
+
+void ClippingNode::onEnterTransitionDidFinish()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnterTransitionDidFinish))
+            return;
+    }
+#endif
+    
+    Node::onEnterTransitionDidFinish();
+    
+    if (_stencil != nullptr)
+    {
+        _stencil->onEnterTransitionDidFinish();
+    }
+}
+
+void ClippingNode::onExitTransitionDidStart()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExitTransitionDidStart))
+            return;
+    }
+#endif
+    
+    if (_stencil != nullptr)
+    {
+        _stencil->onExitTransitionDidStart();
+    }
+   
+    Node::onExitTransitionDidStart();
+}
+
+void ClippingNode::onExit()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit))
+            return;
+    }
+#endif
+    
+    if (_stencil != nullptr)
+    {
+        _stencil->onExit();
+    }
+    
+    Node::onExit();
+}
+
+
+void ClippingNode::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
+{
+    if (!_visible || !hasContent())
+        return;
+    
+    uint32_t flags = processParentFlags(parentTransform, parentFlags);
+
+    // IMPORTANT:
+    // To ease the migration to v3.0, we still support the Mat4 stack,
+    // but it is deprecated and your code should not rely on it
+    Director* director = Director::getInstance();
+    CCASSERT(nullptr != director, "Director is null when setting matrix stack");
+    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+    director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
+
+    //Add group command
+        
+    _groupCommandStencil.init(_globalZOrder);
+    renderer->addCommand(&_groupCommandStencil);
+
+    renderer->pushGroup(_groupCommandStencil.getRenderQueueID());
+
+    // _beforeVisitCmd.init(_globalZOrder);
+    // _beforeVisitCmd.func = CC_CALLBACK_0(StencilStateManager::onBeforeVisit, _stencilStateManager);
+    // renderer->addCommand(&_beforeVisitCmd);
+    _stencilStateManager->onBeforeVisit(_globalZOrder);
+    
+    auto alphaThreshold = this->getAlphaThreshold();
+    if (alphaThreshold < 1)
+    {
+        auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR_ALPHA_TEST);
+        auto programState = new (std::nothrow) backend::ProgramState(program);
+        auto alphaLocation = programState->getUniformLocation("u_alpha_value");
+        programState->setUniform(alphaLocation, &alphaThreshold, sizeof(alphaThreshold));
+        setProgramStateRecursively(_stencil, programState);
+
+        CC_SAFE_RELEASE_NULL(programState);
+    }
+    _stencil->visit(renderer, _modelViewTransform, flags);
+
+    _afterDrawStencilCmd.init(_globalZOrder);
+    _afterDrawStencilCmd.func = CC_CALLBACK_0(StencilStateManager::onAfterDrawStencil, _stencilStateManager);
+    renderer->addCommand(&_afterDrawStencilCmd);
+
+    int i = 0;
+    bool visibleByCamera = isVisitableByVisitingCamera();
+    
+
+    // `_groupCommandChildren` is used as a barrier
+    // to ensure commands above be executed before children nodes
+    _groupCommandChildren.init(_globalZOrder);
+    renderer->addCommand(&_groupCommandChildren);
+
+    renderer->pushGroup(_groupCommandChildren.getRenderQueueID());
+
+    if(!_children.empty())
+    {
+        sortAllChildren();
+        // draw children zOrder < 0
+        for(auto size = _children.size(); i < size; ++i)
+        {
+            auto node = _children.at(i);
+            
+            if ( node && node->getLocalZOrder() < 0 )
+                node->visit(renderer, _modelViewTransform, flags);
+            else
+                break;
+        }
+        // self draw
+        if (visibleByCamera)
+            this->draw(renderer, _modelViewTransform, flags);
+
+        for(auto it=_children.cbegin()+i, itCend = _children.cend(); it != itCend; ++it)
+            (*it)->visit(renderer, _modelViewTransform, flags);
+    }
+    else if (visibleByCamera)
+    {
+        this->draw(renderer, _modelViewTransform, flags);
+    }
+
+
+    renderer->popGroup();
+
+    _afterVisitCmd.init(_globalZOrder);
+    _afterVisitCmd.func = CC_CALLBACK_0(StencilStateManager::onAfterVisit, _stencilStateManager);
+    renderer->addCommand(&_afterVisitCmd);
+
+    renderer->popGroup();
+    
+    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+}
+
+void ClippingNode::setCameraMask(unsigned short mask, bool applyChildren)
+{
+    Node::setCameraMask(mask, applyChildren);
+    
+    if (_stencil)
+        _stencil->setCameraMask(mask, applyChildren);
+}
+
+Node* ClippingNode::getStencil() const
+{
+    return _stencil;
+}
+
+void ClippingNode::setStencil(Node *stencil)
+{
+    //early out if the stencil is already set
+    if (_stencil == stencil)
+        return;
+    
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    if (sEngine)
+    {
+        if (_stencil)
+            sEngine->releaseScriptObject(this, _stencil);
+        if (stencil)
+            sEngine->retainScriptObject(this, stencil);
+    }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    
+    //cleanup current stencil
+    if(_stencil != nullptr && _stencil->isRunning())
+    {
+        _stencil->onExitTransitionDidStart();
+        _stencil->onExit();
+    }
+    CC_SAFE_RELEASE_NULL(_stencil);
+    
+    //initialise new stencil
+    _stencil = stencil;
+    CC_SAFE_RETAIN(_stencil);
+    if(_stencil != nullptr && this->isRunning())
+    {
+        _stencil->onEnter();
+        if(this->_isTransitionFinished)
+        {
+            _stencil->onEnterTransitionDidFinish();
+        }
+    }
+
+    if (_stencil != nullptr)
+    {
+        _originalStencilProgramState[_stencil] = _stencil->getProgramState();
+        auto& children = _stencil->getChildren();
+        for (const auto &child : children) {
+            _originalStencilProgramState[child] = child->getProgramState();
+        }
+    }
+}
+
+bool ClippingNode::hasContent() const
+{
+    return _children.size() > 0;
+}
+
+float ClippingNode::getAlphaThreshold() const
+{
+    return _stencilStateManager->getAlphaThreshold();
+}
+
+void ClippingNode::setAlphaThreshold(float alphaThreshold)
+{
+    if (alphaThreshold == 1 && alphaThreshold != _stencilStateManager->getAlphaThreshold())
+    {
+        // should reset program used by _stencil
+        if (_stencil)
+            restoreAllProgramStates();
+    }
+    _stencilStateManager->setAlphaThreshold(alphaThreshold);
+}
+
+bool ClippingNode::isInverted() const
+{
+    return _stencilStateManager->isInverted();
+}
+
+void ClippingNode::setInverted(bool inverted)
+{
+    _stencilStateManager->setInverted(inverted);
+}
+
+void ClippingNode::setProgramStateRecursively(Node* node, backend::ProgramState* programState)
+{
+    _originalStencilProgramState[node] = node->getProgramState();
+    node->setProgramState(programState);
+
+    auto& children = node->getChildren();
+    for (const auto &child : children) {
+        setProgramStateRecursively(child, programState);
+    }
+}
+
+void ClippingNode::restoreAllProgramStates()
+{
+    for (auto item : _originalStencilProgramState)
+    {
+        auto node = item.first;
+        auto programState = item.second;
+        node->setProgramState(programState);
+    }
+}
+
+
+NS_CC_END

+ 173 - 0
3rdparty/cocos2dx/include/cocos/2d/CCClippingNode.h

@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2012      Pierre-David Bélanger
+ * Copyright (c) 2012      cocos2d-x.org
+ * Copyright (c) 2013-2016 Chukong Technologies Inc.
+ * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ *
+ * http://www.cocos2d-x.org
+ *
+ * Permission is hereby granted, free 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.
+ *
+ */
+#pragma once
+
+#include "2d/CCNode.h"
+#include "renderer/CCGroupCommand.h"
+#include "renderer/CCCustomCommand.h"
+#include "renderer/CCCallbackCommand.h"
+#include <unordered_map>
+NS_CC_BEGIN
+
+class StencilStateManager;
+/**
+ *  @addtogroup _2d
+ *  @{
+ */
+/** ClippingNode is a subclass of Node.
+ * It draws its content (children) clipped using a stencil.
+ * The stencil is an other Node that will not be drawn.
+ * The clipping is done using the alpha part of the stencil (adjusted with an alphaThreshold).
+ */
+class CC_DLL ClippingNode : public Node
+{
+public:
+    /** Creates and initializes a clipping node without a stencil.
+     *
+     * @return An autorelease ClippingNode.
+     */
+    static ClippingNode* create();
+    
+    /** Creates and initializes a clipping node with an other node as its stencil.
+     * The stencil node will be retained.
+     * @param stencil The stencil node.
+     */
+    static ClippingNode* create(Node *stencil);
+
+    /** The Node to use as a stencil to do the clipping.
+     * The stencil node will be retained.
+     * This default to nil.
+     *
+     * @return The stencil node.
+     */
+    Node* getStencil() const;
+    
+    /** Set the Node to use as a stencil to do the clipping.
+     *
+     * @param stencil The Node to use as a stencil to do the clipping.
+     */
+    void setStencil(Node *stencil);
+
+    /** If stencil has no children it will not be drawn.
+     * If you have custom stencil-based node with stencil drawing mechanics other then children-based,
+     * then this method should return true every time you wish stencil to be visited.
+     * By default returns true if has any children attached.
+     *
+     * @return If you have custom stencil-based node with stencil drawing mechanics other then children-based,
+     *         then this method should return true every time you wish stencil to be visited.
+     *         By default returns true if has any children attached.
+     * @js NA
+     */
+    virtual bool hasContent() const;
+
+    /** The alpha threshold.
+     * The content is drawn only where the stencil have pixel with alpha greater than the alphaThreshold.
+     * Should be a float between 0 and 1.
+     * This default to 1 (so alpha test is disabled).
+     *
+     * @return The alpha threshold value,Should be a float between 0 and 1.
+     */
+    float getAlphaThreshold() const;
+    
+    /** Set the alpha threshold. 
+     * 
+     * @param alphaThreshold The alpha threshold.
+     */
+    void setAlphaThreshold(float alphaThreshold);
+    
+    /** Inverted. If this is set to true,
+     * the stencil is inverted, so the content is drawn where the stencil is NOT drawn.
+     * This default to false.
+     *
+     * @return If the clippingNode is Inverted, it will be return true.
+     */
+    bool isInverted() const;
+    
+    /** Set the ClippingNode whether or not invert.
+     *
+     * @param inverted A bool Type,to set the ClippingNode whether or not invert.
+     */
+    void setInverted(bool inverted);
+
+    // Overrides
+    /**
+     * @lua NA
+     */
+    virtual void onEnter() override;
+    /**
+     * @lua NA
+     */
+    virtual void onEnterTransitionDidFinish() override;
+    /**
+     * @lua NA
+     */
+    virtual void onExitTransitionDidStart() override;
+    /**
+     * @lua NA
+     */
+    virtual void onExit() override;
+    virtual void visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;
+    
+    virtual void setCameraMask(unsigned short mask, bool applyChildren = true) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    ClippingNode();
+    
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ClippingNode();
+
+    /** Initializes a clipping node without a stencil.
+     */
+    virtual bool init() override;
+    
+    /** Initializes a clipping node with an other node as its stencil.
+     The stencil node will be retained, and its parent will be set to this clipping node.
+     */
+    virtual bool init(Node *stencil);
+
+protected:
+    void setProgramStateRecursively(Node* node, backend::ProgramState* programState);
+    void restoreAllProgramStates();
+
+    Node* _stencil                              = nullptr;
+    StencilStateManager* _stencilStateManager   = nullptr;
+    
+    GroupCommand _groupCommandStencil;
+    GroupCommand _groupCommandChildren;
+    CallbackCommand _afterDrawStencilCmd;
+    CallbackCommand _afterVisitCmd;
+    std::unordered_map<Node*, backend::ProgramState*> _originalStencilProgramState;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ClippingNode);
+};
+/** @} */
+NS_CC_END

+ 106 - 0
3rdparty/cocos2dx/include/cocos/2d/CCClippingRectangleNode.cpp

@@ -0,0 +1,106 @@
+/****************************************************************************
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+#include "2d/CCClippingRectangleNode.h"
+#include "base/CCDirector.h"
+#include "renderer/CCRenderer.h"
+#include "math/Vec2.h"
+#include "platform/CCGLView.h"
+
+NS_CC_BEGIN
+
+ClippingRectangleNode* ClippingRectangleNode::create(const Rect& clippingRegion)
+{
+    ClippingRectangleNode* node = new (std::nothrow) ClippingRectangleNode();
+    if (node && node->init())
+    {
+        node->setClippingRegion(clippingRegion);
+        node->autorelease();
+    } else
+        CC_SAFE_DELETE(node);
+
+    return node;
+}
+
+ClippingRectangleNode* ClippingRectangleNode::create()
+{
+    ClippingRectangleNode* node = new (std::nothrow) ClippingRectangleNode();
+    if (node && node->init())
+        node->autorelease();
+    else
+        CC_SAFE_DELETE(node);
+
+    return node;
+}
+
+void ClippingRectangleNode::setClippingRegion(const Rect &clippingRegion)
+{
+    _clippingRegion = clippingRegion;
+}
+
+void ClippingRectangleNode::onBeforeVisitScissor()
+{
+    if (_clippingEnabled)
+    {
+        auto renderer = Director::getInstance()->getRenderer();
+        _oldScissorTest = renderer->getScissorTest();
+        renderer->setScissorTest(true);
+
+        float scaleX = _scaleX;
+        float scaleY = _scaleY;
+        Node *parent = this->getParent();
+        while (parent) {
+            scaleX *= parent->getScaleX();
+            scaleY *= parent->getScaleY();
+            parent = parent->getParent();
+        }
+        
+        const Point pos = convertToWorldSpace(Point(_clippingRegion.origin.x, _clippingRegion.origin.y));
+        GLView* glView = Director::getInstance()->getOpenGLView();
+        glView->setScissorInPoints(pos.x,
+                                   pos.y,
+                                   _clippingRegion.size.width * scaleX,
+                                   _clippingRegion.size.height * scaleY);
+    }
+}
+
+void ClippingRectangleNode::onAfterVisitScissor()
+{
+    if (_clippingEnabled)
+        Director::getInstance()->getRenderer()->setScissorTest(_oldScissorTest);
+}
+
+void ClippingRectangleNode::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
+{
+    _beforeVisitCmdScissor.init(_globalZOrder);
+    _beforeVisitCmdScissor.func = CC_CALLBACK_0(ClippingRectangleNode::onBeforeVisitScissor, this);
+    renderer->addCommand(&_beforeVisitCmdScissor);
+    
+    Node::visit(renderer, parentTransform, parentFlags);
+    
+    _afterVisitCmdScissor.init(_globalZOrder);
+    _afterVisitCmdScissor.func = CC_CALLBACK_0(ClippingRectangleNode::onAfterVisitScissor, this);
+    renderer->addCommand(&_afterVisitCmdScissor);
+}
+
+NS_CC_END

+ 111 - 0
3rdparty/cocos2dx/include/cocos/2d/CCClippingRectangleNode.h

@@ -0,0 +1,111 @@
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ * cocos2d-x: http://www.cocos2d-x.org
+ *
+ * Copyright (c) 2012 Pierre-David Bélanger
+ * Copyright (c) 2012 cocos2d-x.org
+ * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ *
+ * Permission is hereby granted, free 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.
+ *
+ */
+#pragma once
+
+#include "2d/CCNode.h"
+#include "renderer/CCCallbackCommand.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/**
+@brief Clipping Rectangle Node.
+@details A node that clipped with specified rectangle.
+ The region of ClippingRectangleNode doesn't support any transform except scale.
+@js NA
+*/
+class CC_DLL ClippingRectangleNode : public Node
+{    
+public:
+    /**
+    @brief Create node with specified clipping region.
+    @param clippingRegion Specify the clipping rectangle.
+    @return If the creation success, return a pointer of ClippingRectangleNode; otherwise return nil.
+    */
+    static ClippingRectangleNode* create(const Rect& clippingRegion);
+    /**
+    @brief Create a clipping rectangle node.
+    @return If the creation success, return a pointer of ClippingRectangleNode; otherwise return nil.
+    */
+    static ClippingRectangleNode* create();
+    
+    /**
+    @brief Get the clipping rectangle.
+    @return The clipping rectangle.
+    */
+    const Rect& getClippingRegion() const {
+        return _clippingRegion;
+    }
+    /**
+    @brief Set the clipping rectangle.
+    @param clippingRegion Specify the clipping rectangle.
+    */
+    void setClippingRegion(const Rect& clippingRegion);
+    
+    /**
+    @brief Get whether the clipping is enabled or not.
+    @return Whether the clipping is enabled or not. Default is true.
+    */
+    bool isClippingEnabled() const {
+        return _clippingEnabled;
+    }
+
+    /**
+    @brief Enable/Disable the clipping.
+    @param enabled Pass true to enable clipping. Pass false to disable clipping.
+    */
+    void setClippingEnabled(bool enabled) {
+        _clippingEnabled = enabled;
+    }
+
+    //virtual void draw(Renderer* renderer, const Mat4 &transform, uint32_t flags) override;
+    virtual void visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;
+
+protected:
+    ClippingRectangleNode() = default;
+    
+    void onBeforeVisitScissor();
+    void onAfterVisitScissor();
+    
+    Rect _clippingRegion;
+    bool _clippingEnabled = true;
+
+    bool _oldScissorTest = false;
+    
+    CallbackCommand _beforeVisitCmdScissor;
+    CallbackCommand _afterVisitCmdScissor;
+};
+
+// end of _2d group
+/// @}
+
+NS_CC_END

+ 153 - 0
3rdparty/cocos2dx/include/cocos/2d/CCComponent.cpp

@@ -0,0 +1,153 @@
+/****************************************************************************
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCComponent.h"
+
+NS_CC_BEGIN
+
+Component::Component()
+: _owner(nullptr)
+, _enabled(true)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    ScriptEngineProtocol* engine = ScriptEngineManager::getInstance()->getScriptEngine();
+    _scriptType = engine != nullptr ? engine->getScriptType() : kScriptTypeNone;
+#endif
+}
+
+Component::~Component()
+{
+}
+
+bool Component::init()
+{
+    return true;
+}
+
+#if CC_ENABLE_SCRIPT_BINDING
+
+static bool sendComponentEventToJS(Component* node, int action)
+{
+    auto scriptEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    
+    if (scriptEngine->isCalledFromScript())
+    {
+        scriptEngine->setCalledFromScript(false);
+    }
+    else
+    {
+        BasicScriptData data(node,(void*)&action);
+        ScriptEvent scriptEvent(kComponentEvent,(void*)&data);
+        if (scriptEngine->sendEvent(&scriptEvent))
+            return true;
+    }
+    
+    return false;
+}
+
+#endif
+
+void Component::onEnter()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        sendComponentEventToJS(this, kComponentOnEnter);
+    }
+#endif
+}
+
+void Component::onExit()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        sendComponentEventToJS(this, kComponentOnExit);
+    }
+#endif
+}
+
+void Component::onAdd()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        sendComponentEventToJS(this, kComponentOnAdd);
+    }
+#endif
+}
+
+void Component::onRemove()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        sendComponentEventToJS(this, kComponentOnRemove);
+    }
+#endif
+}
+
+void Component::update(float /*delta*/)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        sendComponentEventToJS(this, kComponentOnUpdate);
+    }
+#endif
+}
+
+bool Component::serialize(void* /*ar*/)
+{
+    return true;
+}
+
+Component* Component::create()
+{
+    Component * ret = new (std::nothrow) Component();
+
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+
+    return ret;
+}
+
+void Component::setOwner(Node *owner)
+{
+    _owner = owner;
+}
+
+void Component::setEnabled(bool enabled)
+{
+    _enabled = enabled;
+}
+
+NS_CC_END

+ 95 - 0
3rdparty/cocos2dx/include/cocos/2d/CCComponent.h

@@ -0,0 +1,95 @@
+/****************************************************************************
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef __CC_FRAMEWORK_COMPONENT_H__
+#define __CC_FRAMEWORK_COMPONENT_H__
+
+/// @cond DO_NOT_SHOW
+#include <string>
+#include "base/CCRef.h"
+#include "base/CCScriptSupport.h"
+
+NS_CC_BEGIN
+
+class Node;
+
+enum {
+    kComponentOnEnter,
+    kComponentOnExit,
+    kComponentOnAdd,
+    kComponentOnRemove,
+    kComponentOnUpdate
+};
+
+class CC_DLL Component : public Ref
+{
+public:
+    static Component* create();
+
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~Component();
+
+    virtual bool init();
+
+    bool isEnabled() const { return _enabled; }
+    virtual void setEnabled(bool enabled);
+    
+    const std::string& getName() const { return _name; }
+    virtual void setName(const std::string& name) { _name = name; }
+    
+    Node* getOwner() const { return _owner; }
+    virtual void setOwner(Node *owner);
+
+    virtual void update(float delta);
+    virtual bool serialize(void* r);
+
+    virtual void onEnter();
+    virtual void onExit();
+    virtual void onAdd();
+    virtual void onRemove();
+
+CC_CONSTRUCTOR_ACCESS:
+    /**
+    * @js ctor
+    */
+    Component();
+
+protected:
+    Node* _owner;
+    std::string _name;
+    bool _enabled;
+    
+#if CC_ENABLE_SCRIPT_BINDING
+    ccScriptType _scriptType;         ///< type of script binding, lua or javascript
+#endif
+};
+
+NS_CC_END
+
+/// @endcond
+#endif  // __CC_FRAMEWORK_COMPONENT_H__

+ 150 - 0
3rdparty/cocos2dx/include/cocos/2d/CCComponentContainer.cpp

@@ -0,0 +1,150 @@
+/****************************************************************************
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+
+#include "2d/CCComponentContainer.h"
+#include "2d/CCComponent.h"
+#include "2d/CCNode.h"
+
+NS_CC_BEGIN
+
+ComponentContainer::ComponentContainer(Node* node)
+: _owner(node)
+{
+}
+
+ComponentContainer::~ComponentContainer()
+{  
+}
+
+Component* ComponentContainer::get(const std::string& name) const
+{
+    Component* ret = nullptr;
+
+    auto it = _componentMap.find(name);
+    if (it != _componentMap.end())
+    {
+        ret = it->second;
+    }
+
+    return ret;
+}
+
+bool ComponentContainer::add(Component *com)
+{
+    bool ret = false;
+    CCASSERT(com != nullptr, "Component must be non-nil");
+    CCASSERT(com->getOwner() == nullptr, "Component already added. It can't be added again");
+    do
+    {
+        auto componentName = com->getName();
+
+        if (_componentMap.find(componentName) != _componentMap.end())
+        {
+            CCASSERT(false, "ComponentContainer already have this kind of component");
+            break;
+        }
+        _componentMap[componentName] = com;
+        com->retain();
+        com->setOwner(_owner);
+        com->onAdd();
+
+        ret = true;
+    } while(0);
+    return ret;
+}
+
+bool ComponentContainer::remove(const std::string& componentName)
+{
+    bool ret = false;
+    do 
+    {        
+        auto iter = _componentMap.find(componentName);
+        CC_BREAK_IF(iter == _componentMap.end());
+
+        auto component = iter->second;
+        _componentMap.erase(componentName);
+
+        component->onRemove();
+        component->setOwner(nullptr);
+        component->release();
+
+        ret = true;
+    } while(0);
+
+    return ret;
+ }
+
+bool ComponentContainer::remove(Component *com)
+{
+    return remove(com->getName());
+}
+
+void ComponentContainer::removeAll()
+{
+    if (!_componentMap.empty())
+    {
+        for (auto& iter : _componentMap)
+        {
+            iter.second->onRemove();
+            iter.second->setOwner(nullptr);
+            iter.second->release();
+        }
+        
+        _componentMap.clear();
+        _owner->unscheduleUpdate();
+    }
+}
+
+void ComponentContainer::visit(float delta)
+{
+    if (!_componentMap.empty())
+    {
+        CC_SAFE_RETAIN(_owner);
+        for (auto& iter : _componentMap)
+        {
+            iter.second->update(delta);
+        }
+        CC_SAFE_RELEASE(_owner);
+    }
+}
+
+void ComponentContainer::onEnter()
+{
+    for (auto& iter : _componentMap)
+    {
+        iter.second->onEnter();
+    }
+}
+
+void ComponentContainer::onExit()
+{
+    for (auto& iter : _componentMap)
+    {
+        iter.second->onExit();
+    }
+}
+
+NS_CC_END

+ 79 - 0
3rdparty/cocos2dx/include/cocos/2d/CCComponentContainer.h

@@ -0,0 +1,79 @@
+/****************************************************************************
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef __CC_FRAMEWORK_COMCONTAINER_H__
+#define __CC_FRAMEWORK_COMCONTAINER_H__
+
+/// @cond DO_NOT_SHOW
+
+#include "base/CCMap.h"
+#include <string>
+
+NS_CC_BEGIN
+
+class Component;
+class Node;
+
+class CC_DLL ComponentContainer
+{
+protected:
+    /**
+     * @js ctor
+     */
+    ComponentContainer(Node* node);
+    
+public:
+    /**
+     * @js NA
+     * @lua NA
+     */
+    ~ComponentContainer();
+    
+	/**
+     * @js getComponent
+     */
+	Component* get(const std::string& name) const;
+
+    bool add(Component *com);
+    bool remove(const std::string& name);
+    bool remove(Component *com);
+    void removeAll();
+    void visit(float delta);
+    
+    void onEnter();
+    void onExit();
+    
+    bool isEmpty() const { return _componentMap.empty(); } 
+private:
+    std::unordered_map<std::string, Component*> _componentMap;
+    Node *_owner;
+    
+    friend class Node;
+};
+
+NS_CC_END
+
+/// @endcond
+#endif  // __CC_FRAMEWORK_COMCONTAINER_H__

+ 771 - 0
3rdparty/cocos2dx/include/cocos/2d/CCDrawNode.cpp

@@ -0,0 +1,771 @@
+/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software
+ * Copyright (c) 2012 cocos2d-x.org
+ * Copyright (c) 2013-2016 Chukong Technologies Inc.
+ * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ *
+ * Permission is hereby granted, free 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.
+ */
+
+#include "2d/CCDrawNode.h"
+#include <stddef.h> // offsetof
+#include "base/ccTypes.h"
+#include "base/CCEventType.h"
+#include "base/CCConfiguration.h"
+#include "renderer/CCRenderer.h"
+#include "base/CCDirector.h"
+#include "base/CCEventListenerCustom.h"
+#include "base/CCEventDispatcher.h"
+#include "2d/CCActionCatmullRom.h"
+#include "base/ccUtils.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/ProgramState.h"
+
+NS_CC_BEGIN
+
+static inline Tex2F v2ToTex2F(const Vec2 &v)
+{
+    return {v.x, v.y};
+}
+
+// implementation of DrawNode
+
+DrawNode::DrawNode(float lineWidth)
+: _lineWidth(lineWidth)
+{
+    _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+    //TODO new-renderer: interface setupBuffer removal
+
+    // Need to listen the event only when not use batchnode, because it will use VBO
+//    auto listener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom* event){
+//        /** listen the event that renderer was recreated on Android/WP8 */
+//        this->setupBuffer();
+//    });
+
+//    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
+#endif
+}
+
+DrawNode::~DrawNode()
+{
+    free(_buffer);
+    _buffer = nullptr;
+    free(_bufferGLPoint);
+    _bufferGLPoint = nullptr;
+    free(_bufferGLLine);
+    _bufferGLLine = nullptr;
+    
+    CC_SAFE_RELEASE(_programState);
+    CC_SAFE_RELEASE(_programStatePoint);
+    CC_SAFE_RELEASE(_programStateLine);
+}
+
+DrawNode* DrawNode::create(float defaultLineWidth)
+{
+    DrawNode* ret = new (std::nothrow) DrawNode(defaultLineWidth);
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    
+    return ret;
+}
+
+void DrawNode::ensureCapacity(int count)
+{
+    CCASSERT(count>=0, "capacity must be >= 0");
+    
+    if(_bufferCount + count > _bufferCapacity)
+    {
+        _bufferCapacity += MAX(_bufferCapacity, count);
+        _buffer = (V2F_C4B_T2F*)realloc(_buffer, _bufferCapacity*sizeof(V2F_C4B_T2F));
+        
+        _customCommand.createVertexBuffer(sizeof(V2F_C4B_T2F), _bufferCapacity, CustomCommand::BufferUsage::STATIC);
+        _customCommand.updateVertexBuffer(_buffer, _bufferCapacity*sizeof(V2F_C4B_T2F));
+    }
+}
+
+void DrawNode::ensureCapacityGLPoint(int count)
+{
+    CCASSERT(count>=0, "capacity must be >= 0");
+    
+    if(_bufferCountGLPoint + count > _bufferCapacityGLPoint)
+    {
+        _bufferCapacityGLPoint += MAX(_bufferCapacityGLPoint, count);
+        _bufferGLPoint = (V2F_C4B_T2F*)realloc(_bufferGLPoint, _bufferCapacityGLPoint*sizeof(V2F_C4B_T2F));
+        
+        _customCommandGLPoint.createVertexBuffer(sizeof(V2F_C4B_T2F), _bufferCapacityGLPoint, CustomCommand::BufferUsage::STATIC);
+        _customCommandGLPoint.updateVertexBuffer(_bufferGLPoint, _bufferCapacityGLPoint*sizeof(V2F_C4B_T2F));
+    }
+}
+
+void DrawNode::ensureCapacityGLLine(int count)
+{
+    CCASSERT(count>=0, "capacity must be >= 0");
+    
+    if(_bufferCountGLLine + count > _bufferCapacityGLLine)
+    {
+        _bufferCapacityGLLine += MAX(_bufferCapacityGLLine, count);
+        _bufferGLLine = (V2F_C4B_T2F*)realloc(_bufferGLLine, _bufferCapacityGLLine*sizeof(V2F_C4B_T2F));
+        
+        _customCommandGLLine.createVertexBuffer(sizeof(V2F_C4B_T2F), _bufferCapacityGLLine, CustomCommand::BufferUsage::STATIC);
+        _customCommandGLLine.updateVertexBuffer(_bufferGLLine, _bufferCapacityGLLine*sizeof(V2F_C4B_T2F));
+    }
+}
+
+bool DrawNode::init()
+{
+    _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+    updateShader();
+    ensureCapacity(512);
+    ensureCapacityGLPoint(64);
+    ensureCapacityGLLine(256);
+    
+    _dirty = true;
+    _dirtyGLLine = true;
+    _dirtyGLPoint = true;
+    return true;
+}
+
+void DrawNode::updateShader()
+{
+    CC_SAFE_RELEASE(_programState);
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_COLOR_LENGTH_TEXTURE);
+    _programState = new (std::nothrow) backend::ProgramState(program);
+    _customCommand.getPipelineDescriptor().programState = _programState;
+    setVertexLayout(_customCommand);
+    _customCommand.setDrawType(CustomCommand::DrawType::ARRAY);
+    _customCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
+
+    CC_SAFE_RELEASE(_programStatePoint);
+    program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_COLOR_TEXTURE_AS_POINTSIZE);
+    _programStatePoint = new (std::nothrow) backend::ProgramState(program);
+    _customCommandGLPoint.getPipelineDescriptor().programState = _programStatePoint;
+    setVertexLayout(_customCommandGLPoint);
+    _customCommandGLPoint.setDrawType(CustomCommand::DrawType::ARRAY);
+    _customCommandGLPoint.setPrimitiveType(CustomCommand::PrimitiveType::POINT);
+
+    CC_SAFE_RELEASE(_programStateLine);
+    program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_COLOR_LENGTH_TEXTURE);
+    _programStateLine = new (std::nothrow) backend::ProgramState(program);
+    _customCommandGLLine.getPipelineDescriptor().programState = _programStateLine;
+    setVertexLayout(_customCommandGLLine);
+    _customCommandGLLine.setDrawType(CustomCommand::DrawType::ARRAY);
+    _customCommandGLLine.setPrimitiveType(CustomCommand::PrimitiveType::LINE);
+}
+
+void DrawNode::setVertexLayout(CustomCommand& cmd)
+{
+    auto* programState = cmd.getPipelineDescriptor().programState;
+    auto layout = programState->getVertexLayout();
+    const auto& attributeInfo = programState->getProgram()->getActiveAttributes();
+    auto iter = attributeInfo.find("a_position");
+    if(iter != attributeInfo.end())
+    {
+        layout->setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT2, 0, false);
+    }
+    
+    iter = attributeInfo.find("a_texCoord");
+    if(iter != attributeInfo.end())
+    {
+        layout->setAttribute("a_texCoord", iter->second.location, backend::VertexFormat::FLOAT2, offsetof(V2F_C4B_T2F, texCoords), false);
+    }
+    
+    iter = attributeInfo.find("a_color");
+    if(iter != attributeInfo.end())
+    {
+        layout->setAttribute("a_color", iter->second.location, backend::VertexFormat::UBYTE4, offsetof(V2F_C4B_T2F, colors), true);
+    }
+    layout->setLayout(sizeof(V2F_C4B_T2F));
+}
+
+void DrawNode::updateBlendState(CustomCommand& cmd)
+{
+    backend::BlendDescriptor& blendDescriptor = cmd.getPipelineDescriptor().blendDescriptor;
+    blendDescriptor.blendEnabled = true;
+    if (_blendFunc == BlendFunc::ALPHA_NON_PREMULTIPLIED)
+    {
+        blendDescriptor.sourceRGBBlendFactor = backend::BlendFactor::SRC_ALPHA;
+        blendDescriptor.destinationRGBBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
+        blendDescriptor.sourceAlphaBlendFactor = backend::BlendFactor::SRC_ALPHA;
+        blendDescriptor.destinationAlphaBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
+        setOpacityModifyRGB(false);
+    }
+    else
+    {
+        blendDescriptor.sourceRGBBlendFactor = backend::BlendFactor::ONE;
+        blendDescriptor.destinationRGBBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
+        blendDescriptor.sourceAlphaBlendFactor = backend::BlendFactor::ONE;
+        blendDescriptor.destinationAlphaBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
+        setOpacityModifyRGB(true);
+    }
+}
+
+void DrawNode::updateUniforms(const Mat4 &transform, CustomCommand& cmd)
+{
+    auto& pipelineDescriptor = cmd.getPipelineDescriptor();
+    const auto& matrixP = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    Mat4 matrixMVP = matrixP * transform;
+    auto mvpLocation = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix");
+    pipelineDescriptor.programState->setUniform(mvpLocation, matrixMVP.m, sizeof(matrixMVP.m));
+
+    float alpha = _displayedOpacity / 255.0f;
+    auto alphaUniformLocation = pipelineDescriptor.programState->getUniformLocation("u_alpha");
+    pipelineDescriptor.programState->setUniform(alphaUniformLocation, &alpha, sizeof(alpha));
+}
+
+void DrawNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
+{
+    if(_bufferCount)
+    {
+        updateBlendState(_customCommand);
+        updateUniforms(transform, _customCommand);
+        _customCommand.init(_globalZOrder);
+        renderer->addCommand(&_customCommand);
+    }
+    
+    if(_bufferCountGLPoint)
+    {
+        updateBlendState(_customCommandGLPoint);
+        updateUniforms(transform, _customCommandGLPoint);
+        _customCommandGLPoint.init(_globalZOrder);
+        renderer->addCommand(&_customCommandGLPoint);
+    }
+    
+    if(_bufferCountGLLine)
+    {
+        updateBlendState(_customCommandGLLine);
+        updateUniforms(transform, _customCommandGLLine);
+        _customCommandGLLine.setLineWidth(_lineWidth);
+        _customCommandGLLine.init(_globalZOrder);
+        renderer->addCommand(&_customCommandGLLine);
+    }
+}
+
+void DrawNode::drawPoint(const Vec2& position, const float pointSize, const Color4F &color)
+{
+    ensureCapacityGLPoint(1);
+    
+    V2F_C4B_T2F *point = _bufferGLPoint + _bufferCountGLPoint;
+    *point = {position, Color4B(color), Tex2F(pointSize,0)};
+    
+    _customCommandGLPoint.updateVertexBuffer(point, _bufferCountGLPoint*sizeof(V2F_C4B_T2F), sizeof(V2F_C4B_T2F));
+    _bufferCountGLPoint += 1;
+    _dirtyGLPoint = true;
+    _customCommandGLPoint.setVertexDrawInfo(0, _bufferCountGLPoint);
+}
+
+void DrawNode::drawPoints(const Vec2 *position, unsigned int numberOfPoints, const Color4F &color)
+{
+    drawPoints(position, numberOfPoints, 1.0, color);
+}
+
+void DrawNode::drawPoints(const Vec2 *position, unsigned int numberOfPoints, const float pointSize, const Color4F &color)
+{
+    ensureCapacityGLPoint(numberOfPoints);
+    
+    V2F_C4B_T2F *point = _bufferGLPoint + _bufferCountGLPoint;
+    for(unsigned int i=0; i < numberOfPoints; i++)
+    {
+        *(point + i) = {position[i], Color4B(color), Tex2F(pointSize,0)};
+    }
+    
+    _customCommandGLPoint.updateVertexBuffer(point, _bufferCountGLPoint*sizeof(V2F_C4B_T2F), numberOfPoints*sizeof(V2F_C4B_T2F));
+    _bufferCountGLPoint += numberOfPoints;
+    _dirtyGLPoint = true;
+    _customCommandGLPoint.setVertexDrawInfo(0, _bufferCountGLPoint);
+}
+
+void DrawNode::drawLine(const Vec2 &origin, const Vec2 &destination, const Color4F &color)
+{
+    ensureCapacityGLLine(2);
+    
+    V2F_C4B_T2F *point = _bufferGLLine + _bufferCountGLLine;
+    
+    *point = {origin, Color4B(color), Tex2F(0.0, 0.0)};
+    *(point+1) = {destination, Color4B(color), Tex2F(0.0, 0.0)};
+    
+    _customCommandGLLine.updateVertexBuffer(point, _bufferCountGLLine*sizeof(V2F_C4B_T2F), 2*sizeof(V2F_C4B_T2F));
+    _bufferCountGLLine += 2;
+    _dirtyGLLine = true;
+    _customCommandGLLine.setVertexDrawInfo(0, _bufferCountGLLine);
+}
+
+void DrawNode::drawRect(const Vec2 &origin, const Vec2 &destination, const Color4F &color)
+{
+    drawLine(origin, Vec2(destination.x, origin.y), color);
+    drawLine(Vec2(destination.x, origin.y), destination, color);
+    drawLine(destination, Vec2(origin.x, destination.y), color);
+    drawLine(Vec2(origin.x, destination.y), origin, color);
+}
+
+void DrawNode::drawPoly(const Vec2 *poli, unsigned int numberOfPoints, bool closePolygon, const Color4F &color)
+{
+    unsigned int vertex_count;
+    if(closePolygon)
+    {
+        vertex_count = 2 * numberOfPoints;
+        ensureCapacityGLLine(vertex_count);
+    }
+    else
+    {
+        vertex_count = 2 * (numberOfPoints - 1);
+        ensureCapacityGLLine(vertex_count);
+    }
+    
+    V2F_C4B_T2F *point = _bufferGLLine + _bufferCountGLLine;
+    V2F_C4B_T2F *cursor = point;
+    
+    unsigned int i = 0;
+    for(; i < numberOfPoints - 1; i++)
+    {
+        *point = {poli[i], Color4B(color), Tex2F(0.0, 0.0)};
+        *(point + 1) = {poli[i+1], Color4B(color), Tex2F(0.0, 0.0)};
+        point += 2;
+    }
+    if(closePolygon)
+    {
+        *point = {poli[i], Color4B(color), Tex2F(0.0, 0.0)};
+        *(point + 1) = {poli[0], Color4B(color), Tex2F(0.0, 0.0)};
+    }
+    
+    _customCommandGLLine.updateVertexBuffer(cursor, _bufferCountGLLine*sizeof(V2F_C4B_T2F), vertex_count*sizeof(V2F_C4B_T2F));
+    _bufferCountGLLine += vertex_count;
+    _customCommandGLLine.setVertexDrawInfo(0, _bufferCountGLLine);
+}
+
+void DrawNode::drawCircle(const Vec2& center, float radius, float angle, unsigned int segments, bool drawLineToCenter, float scaleX, float scaleY, const Color4F &color)
+{
+    const float coef = 2.0f * (float)M_PI/segments;
+    
+    Vec2 *vertices = new (std::nothrow) Vec2[segments+2];
+    if( ! vertices )
+        return;
+    
+    for(unsigned int i = 0;i <= segments; i++) {
+        float rads = i*coef;
+        float j = radius * cosf(rads + angle) * scaleX + center.x;
+        float k = radius * sinf(rads + angle) * scaleY + center.y;
+        
+        vertices[i].x = j;
+        vertices[i].y = k;
+    }
+    if(drawLineToCenter)
+    {
+        vertices[segments+1].x = center.x;
+        vertices[segments+1].y = center.y;
+        drawPoly(vertices, segments+2, true, color);
+    }
+    else
+        drawPoly(vertices, segments+1, true, color);
+    
+    CC_SAFE_DELETE_ARRAY(vertices);
+}
+
+void DrawNode::drawCircle(const Vec2 &center, float radius, float angle, unsigned int segments, bool drawLineToCenter, const Color4F &color)
+{
+    drawCircle(center, radius, angle, segments, drawLineToCenter, 1.0f, 1.0f, color);
+}
+
+void DrawNode::drawQuadBezier(const Vec2 &origin, const Vec2 &control, const Vec2 &destination, unsigned int segments, const Color4F &color)
+{
+    Vec2* vertices = new (std::nothrow) Vec2[segments + 1];
+    if( ! vertices )
+        return;
+    
+    float t = 0.0f;
+    for(unsigned int i = 0; i < segments; i++)
+    {
+        vertices[i].x = powf(1 - t, 2) * origin.x + 2.0f * (1 - t) * t * control.x + t * t * destination.x;
+        vertices[i].y = powf(1 - t, 2) * origin.y + 2.0f * (1 - t) * t * control.y + t * t * destination.y;
+        t += 1.0f / segments;
+    }
+    vertices[segments].x = destination.x;
+    vertices[segments].y = destination.y;
+    
+    drawPoly(vertices, segments+1, false, color);
+
+    CC_SAFE_DELETE_ARRAY(vertices);
+}
+
+void DrawNode::drawCubicBezier(const Vec2 &origin, const Vec2 &control1, const Vec2 &control2, const Vec2 &destination, unsigned int segments, const Color4F &color)
+{
+    Vec2* vertices = new (std::nothrow) Vec2[segments + 1];
+    if( ! vertices )
+        return;
+    
+    float t = 0;
+    for (unsigned int i = 0; i < segments; i++)
+    {
+        vertices[i].x = powf(1 - t, 3) * origin.x + 3.0f * powf(1 - t, 2) * t * control1.x + 3.0f * (1 - t) * t * t * control2.x + t * t * t * destination.x;
+        vertices[i].y = powf(1 - t, 3) * origin.y + 3.0f * powf(1 - t, 2) * t * control1.y + 3.0f * (1 - t) * t * t * control2.y + t * t * t * destination.y;
+        t += 1.0f / segments;
+    }
+    vertices[segments].x = destination.x;
+    vertices[segments].y = destination.y;
+    
+    drawPoly(vertices, segments+1, false, color);
+
+    CC_SAFE_DELETE_ARRAY(vertices);
+}
+
+void DrawNode::drawCardinalSpline(PointArray *config, float tension,  unsigned int segments, const Color4F &color)
+{
+    Vec2* vertices = new (std::nothrow) Vec2[segments + 1];
+    if( ! vertices )
+        return;
+    
+    ssize_t p;
+    float lt;
+    float deltaT = 1.0f / config->count();
+    
+    for( unsigned int i=0; i < segments+1;i++) {
+        
+        float dt = (float)i / segments;
+        
+        // border
+        if( dt == 1 ) {
+            p = config->count() - 1;
+            lt = 1;
+        } else {
+            p = static_cast<ssize_t>(dt / deltaT);
+            lt = (dt - deltaT * (float)p) / deltaT;
+        }
+        
+        // Interpolate
+        Vec2 pp0 = config->getControlPointAtIndex(p-1);
+        Vec2 pp1 = config->getControlPointAtIndex(p+0);
+        Vec2 pp2 = config->getControlPointAtIndex(p+1);
+        Vec2 pp3 = config->getControlPointAtIndex(p+2);
+        
+        Vec2 newPos = ccCardinalSplineAt( pp0, pp1, pp2, pp3, tension, lt);
+        vertices[i].x = newPos.x;
+        vertices[i].y = newPos.y;
+    }
+    
+    drawPoly(vertices, segments+1, false, color);
+    
+    CC_SAFE_DELETE_ARRAY(vertices);
+}
+
+void DrawNode::drawCatmullRom(PointArray *points, unsigned int segments, const Color4F &color)
+{
+    drawCardinalSpline( points, 0.5f, segments, color);
+}
+
+void DrawNode::drawDot(const Vec2 &pos, float radius, const Color4F &color)
+{
+    unsigned int vertex_count = 2*3;
+    ensureCapacity(vertex_count);
+    
+    V2F_C4B_T2F a = {Vec2(pos.x - radius, pos.y - radius), Color4B(color), Tex2F(-1.0, -1.0) };
+    V2F_C4B_T2F b = {Vec2(pos.x - radius, pos.y + radius), Color4B(color), Tex2F(-1.0,  1.0) };
+    V2F_C4B_T2F c = {Vec2(pos.x + radius, pos.y + radius), Color4B(color), Tex2F( 1.0,  1.0) };
+    V2F_C4B_T2F d = {Vec2(pos.x + radius, pos.y - radius), Color4B(color), Tex2F( 1.0, -1.0) };
+    
+    V2F_C4B_T2F_Triangle *triangles = (V2F_C4B_T2F_Triangle *)(_buffer + _bufferCount);
+    V2F_C4B_T2F_Triangle triangle0 = {a, b, c};
+    V2F_C4B_T2F_Triangle triangle1 = {a, c, d};
+    triangles[0] = triangle0;
+    triangles[1] = triangle1;
+    
+    _customCommand.updateVertexBuffer(triangles, _bufferCount*sizeof(V2F_C4B_T2F), vertex_count*sizeof(V2F_C4B_T2F));
+    _bufferCount += vertex_count;
+    _dirty = true;
+    _customCommand.setVertexDrawInfo(0, _bufferCount);
+}
+
+void DrawNode::drawRect(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, const Vec2& p4, const Color4F &color)
+{
+    drawLine(p1, p2, color);
+    drawLine(p2, p3, color);
+    drawLine(p3, p4, color);
+    drawLine(p4, p1, color);
+}
+
+void DrawNode::drawSegment(const Vec2 &from, const Vec2 &to, float radius, const Color4F &color)
+{
+    unsigned int vertex_count = 6*3;
+    ensureCapacity(vertex_count);
+    
+    Vec2 a = from;
+    Vec2 b = to;
+
+
+    Vec2 n = ((b - a).getPerp()).getNormalized();
+    Vec2 t = n.getPerp();
+
+    Vec2 nw = n * radius;
+    Vec2 tw = t * radius;
+    Vec2 v0 = b - (nw + tw);
+    Vec2 v1 = b + (nw - tw);
+    Vec2 v2 = b - nw;
+    Vec2 v3 = b + nw;
+    Vec2 v4 = a - nw;
+    Vec2 v5 = a + nw;
+    Vec2 v6 = a - (nw - tw);
+    Vec2 v7 = a + (nw + tw);
+    
+    
+    V2F_C4B_T2F_Triangle *triangles = (V2F_C4B_T2F_Triangle *)(_buffer + _bufferCount);
+    
+    V2F_C4B_T2F_Triangle triangles0 = {
+        {v0, Color4B(color), v2ToTex2F(-(n + t))},
+        {v1, Color4B(color), v2ToTex2F(n - t)},
+        {v2, Color4B(color), v2ToTex2F(-n)},
+    };
+    triangles[0] = triangles0;
+
+    V2F_C4B_T2F_Triangle triangles1 = {
+        {v3, Color4B(color), v2ToTex2F(n)},
+        {v1, Color4B(color), v2ToTex2F(n - t)},
+        {v2, Color4B(color), v2ToTex2F(-n)},
+    };
+    triangles[1] = triangles1;
+
+    V2F_C4B_T2F_Triangle triangles2 = {
+        {v3, Color4B(color), v2ToTex2F(n)},
+        {v4, Color4B(color), v2ToTex2F(-n)},
+        {v2, Color4B(color), v2ToTex2F(-n)},
+    };
+    triangles[2] = triangles2;
+
+    V2F_C4B_T2F_Triangle triangles3 = {
+        {v3, Color4B(color), v2ToTex2F(n)},
+        {v4, Color4B(color), v2ToTex2F(-n)},
+        {v5, Color4B(color), v2ToTex2F(n) },
+    };
+    triangles[3] = triangles3;
+
+    V2F_C4B_T2F_Triangle triangles4 = {
+        {v6, Color4B(color), v2ToTex2F(t - n)},
+        {v4, Color4B(color), v2ToTex2F(-n) },
+        {v5, Color4B(color), v2ToTex2F(n)},
+    };
+    triangles[4] = triangles4;
+
+    V2F_C4B_T2F_Triangle triangles5 = {
+        {v6, Color4B(color), v2ToTex2F(t - n)},
+        {v7, Color4B(color), v2ToTex2F(t + n)},
+        {v5, Color4B(color), v2ToTex2F(n)},
+    };
+    triangles[5] = triangles5;
+    
+    _customCommand.updateVertexBuffer(triangles, _bufferCount*sizeof(V2F_C4B_T2F), vertex_count*sizeof(V2F_C4B_T2F));
+    _bufferCount += vertex_count;
+    _dirty = true;
+    _customCommand.setVertexDrawInfo(0, _bufferCount);
+}
+
+void DrawNode::drawPolygon(const Vec2 *verts, int count, const Color4F &fillColor, float borderWidth, const Color4F &borderColor)
+{
+    CCASSERT(count >= 0, "invalid count value");
+    
+    bool outline = (borderColor.a > 0.0f && borderWidth > 0.0f);
+    
+    auto  triangle_count = outline ? (3*count - 2) : (count - 2);
+    auto vertex_count = 3*triangle_count;
+    ensureCapacity(vertex_count);
+    
+    V2F_C4B_T2F_Triangle *triangles = (V2F_C4B_T2F_Triangle *)(_buffer + _bufferCount);
+    V2F_C4B_T2F_Triangle *cursor = triangles;
+    
+    for (int i = 0; i < count-2; i++)
+    {
+        V2F_C4B_T2F_Triangle tmp = {
+            {verts[0], Color4B(fillColor), v2ToTex2F(Vec2::ZERO)},
+            {verts[i+1], Color4B(fillColor), v2ToTex2F(Vec2::ZERO)},
+            {verts[i+2], Color4B(fillColor), v2ToTex2F(Vec2::ZERO)},
+        };
+        
+        *cursor++ = tmp;
+    }
+    
+    if(outline)
+    {
+        struct ExtrudeVerts {Vec2 offset, n;};
+        struct ExtrudeVerts* extrude = (struct ExtrudeVerts*)malloc(sizeof(struct ExtrudeVerts) * count);
+        
+        for (int i = 0; i < count; i++)
+        {
+            Vec2 v0 = verts[(i-1+count)%count];
+            Vec2 v1 = verts[i];
+            Vec2 v2 = verts[(i+1)%count];
+            
+            Vec2 n1 = ((v1 - v0).getPerp()).getNormalized();
+            Vec2 n2 = ((v2 - v1).getPerp()).getNormalized();
+            
+            Vec2 offset = (n1 + n2) * (1.0f / (Vec2::dot(n1, n2) + 1.0f));
+            extrude[i] = {offset, n2};
+        }
+        
+        for(int i = 0; i < count; i++)
+        {
+            int j = (i+1)%count;
+            Vec2 v0 = verts[i];
+            Vec2 v1 = verts[j];
+            
+            Vec2 n0 = extrude[i].n;
+            
+            Vec2 offset0 = extrude[i].offset;
+            Vec2 offset1 = extrude[j].offset;
+            
+            Vec2 inner0 = v0 - offset0 * borderWidth;
+            Vec2 inner1 = v1 - offset1 * borderWidth;
+            Vec2 outer0 = v0 + offset0 * borderWidth;
+            Vec2 outer1 = v1 + offset1 * borderWidth;
+            
+            V2F_C4B_T2F_Triangle tmp1 = {
+                {inner0, Color4B(borderColor), v2ToTex2F(-n0)},
+                {inner1, Color4B(borderColor), v2ToTex2F(-n0)},
+                {outer1, Color4B(borderColor), v2ToTex2F(n0)}
+            };
+            *cursor++ = tmp1;
+            
+            V2F_C4B_T2F_Triangle tmp2 = {
+                {inner0, Color4B(borderColor), v2ToTex2F(-n0)},
+                {outer0, Color4B(borderColor), v2ToTex2F(n0)},
+                {outer1, Color4B(borderColor), v2ToTex2F(n0)}
+            };
+            *cursor++ = tmp2;
+        }
+        
+        free(extrude);
+    }
+    
+    _customCommand.updateVertexBuffer(triangles, _bufferCount*sizeof(V2F_C4B_T2F), vertex_count*sizeof(V2F_C4B_T2F));
+    _bufferCount += vertex_count;
+    _customCommand.setVertexDrawInfo(0, _bufferCount);
+    _dirty = true;
+}
+
+void DrawNode::drawSolidRect(const Vec2 &origin, const Vec2 &destination, const Color4F &color)
+{
+    Vec2 vertices[] = {
+        origin,
+        Vec2(destination.x, origin.y),
+        destination,
+        Vec2(origin.x, destination.y)
+    };
+    
+    drawSolidPoly(vertices, 4, color);
+}
+
+void DrawNode::drawSolidPoly(const Vec2 *poli, unsigned int numberOfPoints, const Color4F &color)
+{
+    drawPolygon(poli, numberOfPoints, color, 0.0, Color4F());
+}
+
+void DrawNode::drawSolidCircle(const Vec2& center, float radius, float angle, unsigned int segments, float scaleX, float scaleY, const Color4F &color)
+{
+    const float coef = 2.0f * (float)M_PI/segments;
+    
+    Vec2 *vertices = new (std::nothrow) Vec2[segments];
+    if( ! vertices )
+        return;
+    
+    for(unsigned int i = 0;i < segments; i++)
+    {
+        float rads = i*coef;
+        float j = radius * cosf(rads + angle) * scaleX + center.x;
+        float k = radius * sinf(rads + angle) * scaleY + center.y;
+        
+        vertices[i].x = j;
+        vertices[i].y = k;
+    }
+    
+    drawSolidPoly(vertices, segments, color);
+    
+    CC_SAFE_DELETE_ARRAY(vertices);
+}
+
+void DrawNode::drawSolidCircle( const Vec2& center, float radius, float angle, unsigned int segments, const Color4F& color)
+{
+    drawSolidCircle(center, radius, angle, segments, 1.0f, 1.0f, color);
+}
+
+void DrawNode::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, const Color4F &color)
+{
+    unsigned int vertex_count = 3;
+    ensureCapacity(vertex_count);
+
+    Color4B col = Color4B(color);
+    V2F_C4B_T2F a = {p1, col, Tex2F(0.0, 0.0) };
+    V2F_C4B_T2F b = {p2, col, Tex2F(0.0,  0.0) };
+    V2F_C4B_T2F c = {p3, col, Tex2F(0.0,  0.0) };
+
+    V2F_C4B_T2F_Triangle *triangles = (V2F_C4B_T2F_Triangle *)(_buffer + _bufferCount);
+    V2F_C4B_T2F_Triangle triangle = {a, b, c};
+    triangles[0] = triangle;
+
+    _customCommand.updateVertexBuffer(triangles, _bufferCount*sizeof(V2F_C4B_T2F), vertex_count*sizeof(V2F_C4B_T2F));
+    _bufferCount += vertex_count;
+    _dirty = true;
+    _customCommand.setVertexDrawInfo(0, _bufferCount);
+}
+
+void DrawNode::clear()
+{
+    _bufferCount = 0;
+    _dirty = true;
+    _bufferCountGLLine = 0;
+    _dirtyGLLine = true;
+    _bufferCountGLPoint = 0;
+    _dirtyGLPoint = true;
+    _lineWidth = 0;
+}
+
+const BlendFunc& DrawNode::getBlendFunc() const
+{
+    return _blendFunc;
+}
+
+void DrawNode::setBlendFunc(const BlendFunc &blendFunc)
+{
+    _blendFunc = blendFunc;
+}
+
+void DrawNode::setLineWidth(float lineWidth)
+{
+    _lineWidth = lineWidth;
+}
+
+float DrawNode::getLineWidth()
+{
+    return this->_lineWidth;
+}
+
+void DrawNode::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
+{
+    if (_isolated)
+    {
+        //ignore `parentTransform` from parent
+        Node::visit(renderer, Mat4::IDENTITY, parentFlags);
+    }
+    else
+    {
+        Node::visit(renderer, parentTransform, parentFlags);
+    }
+}
+
+NS_CC_END

+ 359 - 0
3rdparty/cocos2dx/include/cocos/2d/CCDrawNode.h

@@ -0,0 +1,359 @@
+/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software
+ * Copyright (c) 2012 cocos2d-x.org
+ * Copyright (c) 2013-2016 Chukong Technologies Inc.
+ * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ *
+ * Permission is hereby granted, free 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.
+ */
+
+/*
+ * Code copied & pasted from SpacePatrol game https://github.com/slembcke/SpacePatrol
+ *
+ * Renamed and added some changes for cocos2d
+ *
+ */
+
+#ifndef __CCDRAWNODES_CCDRAW_NODE_H__
+#define __CCDRAWNODES_CCDRAW_NODE_H__
+
+#include "2d/CCNode.h"
+#include "base/ccTypes.h"
+#include "renderer/CCCustomCommand.h"
+#include "math/CCMath.h"
+
+NS_CC_BEGIN
+
+static const int DEFAULT_LINE_WIDTH = 2;
+
+class PointArray;
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** @class DrawNode
+ * @brief Node that draws dots, segments and polygons.
+ * Faster than the "drawing primitives" since they draws everything in one single batch.
+ * @since v2.1
+ */
+class CC_DLL DrawNode : public Node
+{
+public:
+    /** creates and initialize a DrawNode node.
+     *
+     * @return Return an autorelease object.
+     */
+    static DrawNode* create(float defaultLineWidth = DEFAULT_LINE_WIDTH);
+    
+    /** Draw a point.
+     *
+     * @param point A Vec2 used to point.
+     * @param pointSize The point size.
+     * @param color The point color.
+     * @js NA
+     */
+    void drawPoint(const Vec2& point, const float pointSize, const Color4F &color);
+    
+    /** Draw a group point.
+     *
+     * @param position A Vec2 pointer.
+     * @param numberOfPoints The number of points.
+     * @param color The point color.
+     * @js NA
+     */
+    void drawPoints(const Vec2 *position, unsigned int numberOfPoints, const Color4F &color);
+    
+    /** Draw a group point.
+     *
+     * @param position A Vec2 pointer.
+     * @param numberOfPoints The number of points.
+     * @param pointSize The point size.
+     * @param color The point color.
+     * @js NA
+     */
+    void drawPoints(const Vec2 *position, unsigned int numberOfPoints, const float pointSize, const Color4F &color);
+    
+    /** Draw an line from origin to destination with color. 
+     * 
+     * @param origin The line origin.
+     * @param destination The line destination.
+     * @param color The line color.
+     * @js NA
+     */
+    void drawLine(const Vec2 &origin, const Vec2 &destination, const Color4F &color);
+    
+    /** Draws a rectangle given the origin and destination point measured in points.
+     * The origin and the destination can not have the same x and y coordinate.
+     *
+     * @param origin The rectangle origin.
+     * @param destination The rectangle destination.
+     * @param color The rectangle color.
+     */
+    void drawRect(const Vec2 &origin, const Vec2 &destination, const Color4F &color);
+    
+    /** Draws a polygon given a pointer to point coordinates and the number of vertices measured in points.
+     * The polygon can be closed or open.
+     *
+     * @param poli A pointer to point coordinates.
+     * @param numberOfPoints The number of vertices measured in points.
+     * @param closePolygon The polygon can be closed or open.
+     * @param color The polygon color.
+     */
+    void drawPoly(const Vec2 *poli, unsigned int numberOfPoints, bool closePolygon, const Color4F &color);
+    
+    /** Draws a circle given the center, radius and number of segments.
+     *
+     * @param center The circle center point.
+     * @param radius The circle rotate of radius.
+     * @param angle  The circle angle.
+     * @param segments The number of segments.
+     * @param drawLineToCenter Whether or not draw the line from the origin to center.
+     * @param scaleX The scale value in x.
+     * @param scaleY The scale value in y.
+     * @param color Set the circle color.
+     */
+    void drawCircle( const Vec2& center, float radius, float angle, unsigned int segments, bool drawLineToCenter, float scaleX, float scaleY, const Color4F &color);
+    
+    /** Draws a circle given the center, radius and number of segments.
+     *
+     * @param center The circle center point.
+     * @param radius The circle rotate of radius.
+     * @param angle  The circle angle.
+     * @param segments The number of segments.
+     * @param drawLineToCenter Whether or not draw the line from the origin to center.
+     * @param color Set the circle color.
+     */
+    void drawCircle(const Vec2 &center, float radius, float angle, unsigned int segments, bool drawLineToCenter, const Color4F &color);
+    
+    /** Draws a quad bezier path.
+     *
+     * @param origin The origin of the bezier path.
+     * @param control The control of the bezier path.
+     * @param destination The destination of the bezier path.
+     * @param segments The number of segments.
+     * @param color Set the quad bezier color.
+     */
+    void drawQuadBezier(const Vec2 &origin, const Vec2 &control, const Vec2 &destination, unsigned int segments, const Color4F &color);
+
+    /** Draw a cubic bezier curve with color and number of segments
+     *
+     * @param origin The origin of the bezier path.
+     * @param control1 The first control of the bezier path.
+     * @param control2 The second control of the bezier path.
+     * @param destination The destination of the bezier path.
+     * @param segments The number of segments.
+     * @param color Set the cubic bezier color.
+     */
+    void drawCubicBezier(const Vec2 &origin, const Vec2 &control1, const Vec2 &control2, const Vec2 &destination, unsigned int segments, const Color4F &color);
+    
+    /** Draws a Cardinal Spline path.
+     *
+     * @param config A array point.
+     * @param tension The tension of the spline.
+     * @param segments The number of segments.
+     * @param color Set the Spline color.
+     */
+    void drawCardinalSpline(PointArray *config, float tension,  unsigned int segments, const Color4F &color);
+    
+    /** Draws a Catmull Rom path.
+     *
+     * @param points A point array  of control point.
+     * @param segments The number of segments.
+     * @param color The Catmull Rom color.
+     */
+    void drawCatmullRom(PointArray *points, unsigned int segments, const Color4F &color);
+    
+    /** draw a dot at a position, with a given radius and color. 
+     *
+     * @param pos The dot center.
+     * @param radius The dot radius.
+     * @param color The dot color.
+     */
+    void drawDot(const Vec2 &pos, float radius, const Color4F &color);
+    
+    /** Draws a rectangle with 4 points.
+     *
+     * @param p1 The rectangle vertex point.
+     * @param p2 The rectangle vertex point.
+     * @param p3 The rectangle vertex point.
+     * @param p4 The rectangle vertex point.
+     * @param color The rectangle color.
+     */
+    void drawRect(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, const Vec2& p4, const Color4F &color);
+    
+    /** Draws a solid rectangle given the origin and destination point measured in points.
+     * The origin and the destination can not have the same x and y coordinate.
+     *
+     * @param origin The rectangle origin.
+     * @param destination The rectangle destination.
+     * @param color The rectangle color.
+     * @js NA
+     */
+    void drawSolidRect(const Vec2 &origin, const Vec2 &destination, const Color4F &color);
+    
+    /** Draws a solid polygon given a pointer to CGPoint coordinates, the number of vertices measured in points, and a color.
+     *
+     * @param poli A solid polygon given a pointer to CGPoint coordinates.
+     * @param numberOfPoints The number of vertices measured in points.
+     * @param color The solid polygon color.
+     * @js NA
+     */
+    void drawSolidPoly(const Vec2 *poli, unsigned int numberOfPoints, const Color4F &color);
+    
+    /** Draws a solid circle given the center, radius and number of segments.
+     * @param center The circle center point.
+     * @param radius The circle rotate of radius.
+     * @param angle  The circle angle.
+     * @param segments The number of segments.
+     * @param scaleX The scale value in x.
+     * @param scaleY The scale value in y.
+     * @param color The solid circle color.
+     * @js NA
+     */
+    void drawSolidCircle(const Vec2& center, float radius, float angle, unsigned int segments, float scaleX, float scaleY, const Color4F &color);
+    
+    /** Draws a solid circle given the center, radius and number of segments.
+     * @param center The circle center point.
+     * @param radius The circle rotate of radius.
+     * @param angle  The circle angle.
+     * @param segments The number of segments.
+     * @param color The solid circle color.
+     * @js NA
+     */
+    void drawSolidCircle(const Vec2& center, float radius, float angle, unsigned int segments, const Color4F& color);
+    
+    /** draw a segment with a radius and color. 
+     *
+     * @param from The segment origin.
+     * @param to The segment destination.
+     * @param radius The segment radius.
+     * @param color The segment color.
+     */
+    void drawSegment(const Vec2 &from, const Vec2 &to, float radius, const Color4F &color);
+    
+    /** draw a polygon with a fill color and line color
+    * @code
+    * When this function bound into js or lua,the parameter will be changed
+    * In js: var drawPolygon(var Arrayofpoints, var fillColor, var width, var borderColor)
+    * In lua:local drawPolygon(local pointTable,local tableCount,local fillColor,local width,local borderColor)
+    * @endcode
+    * @param verts A pointer to point coordinates.
+    * @param count The number of verts measured in points.
+    * @param fillColor The color will fill in polygon.
+    * @param borderWidth The border of line width.
+    * @param borderColor The border of line color.
+    * @js NA
+    */
+    void drawPolygon(const Vec2 *verts, int count, const Color4F &fillColor, float borderWidth, const Color4F &borderColor);
+	
+    /** draw a triangle with color. 
+     *
+     * @param p1 The triangle vertex point.
+     * @param p2 The triangle vertex point.
+     * @param p3 The triangle vertex point.
+     * @param color The triangle color.
+     * @js NA
+     */
+    void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, const Color4F &color);
+
+    /** Clear the geometry in the node's buffer. */
+    void clear();
+    /** Get the color mixed mode.
+    * @lua NA
+    */
+    const BlendFunc& getBlendFunc() const;
+    /** Set the color mixed mode.
+    * @code
+    * When this function bound into js or lua,the parameter will be changed
+    * In js: var setBlendFunc(var src, var dst)
+    * @endcode
+    * @lua NA
+    */
+    void setBlendFunc(const BlendFunc &blendFunc);
+    
+    // Overrides
+    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
+
+    virtual void visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;
+    
+    void setLineWidth(float lineWidth);
+
+    // Get CocosStudio guide lines width.
+    float getLineWidth();
+
+    /**
+    * When isolated is set, the position of the node is no longer affected by parent nodes.
+    * Which means it will be drawn just like a root node.
+    */
+    void setIsolated(bool isolated) { _isolated = isolated; }
+
+    bool isIsolated() const { return _isolated; }
+
+CC_CONSTRUCTOR_ACCESS:
+    DrawNode(float lineWidth = DEFAULT_LINE_WIDTH);
+    virtual ~DrawNode();
+    virtual bool init() override;
+
+protected:
+    void ensureCapacity(int count);
+    void ensureCapacityGLPoint(int count);
+    void ensureCapacityGLLine(int count);
+
+    void updateShader();
+    void setVertexLayout(CustomCommand& cmd);
+    void updateBlendState(CustomCommand& cmd);
+    void updateUniforms(const Mat4 &transform, CustomCommand& cmd);
+
+    int         _bufferCapacity = 0;
+    int         _bufferCount = 0;
+    V2F_C4B_T2F *_buffer = nullptr;
+    
+    int         _bufferCapacityGLPoint = 0;
+    int         _bufferCountGLPoint = 0;
+    V2F_C4B_T2F *_bufferGLPoint = nullptr;
+    Color4F     _pointColor;
+    int         _pointSize = 0;
+    
+    int         _bufferCapacityGLLine = 0;
+    int         _bufferCountGLLine = 0;
+    V2F_C4B_T2F *_bufferGLLine = nullptr;
+
+    BlendFunc   _blendFunc;
+    
+    backend::ProgramState* _programState = nullptr;
+    backend::ProgramState* _programStatePoint = nullptr;
+    backend::ProgramState* _programStateLine = nullptr;
+    
+    CustomCommand _customCommand;
+    CustomCommand _customCommandGLPoint;
+    CustomCommand _customCommandGLLine;
+
+    bool        _dirty = false;
+    bool        _dirtyGLPoint = false;
+    bool        _dirtyGLLine = false;
+    bool        _isolated = false;
+    float       _lineWidth = 0.0f;
+    
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(DrawNode);
+};
+/** @} */
+NS_CC_END
+
+#endif // __CCDRAWNODES_CCDRAW_NODE_H__

+ 914 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFastTMXLayer.cpp

@@ -0,0 +1,914 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+Copyright (c) 2011 HKASoftware
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+/*
+ Original rewrite of TMXLayer was based on HKTMXTiledMap by HKASoftware http://hkasoftware.com
+ Further info: http://www.cocos2d-iphone.org/forums/topic/hktmxtiledmap/
+
+ It was rewritten again, and only a small part of the original HK ideas/code remains in this implementation
+
+ */
+#include "2d/CCFastTMXLayer.h"
+#include <stddef.h> // offsetof
+#include "base/ccTypes.h"
+#include "2d/CCFastTMXTiledMap.h"
+#include "2d/CCSprite.h"
+#include "2d/CCCamera.h"
+#include "renderer/CCTextureCache.h"
+#include "renderer/CCRenderer.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/Device.h"
+#include "renderer/backend/Buffer.h"
+#include "base/CCDirector.h"
+#include "base/ccUTF8.h"
+#include "renderer/backend/ProgramState.h"
+
+NS_CC_BEGIN
+
+const int FastTMXLayer::FAST_TMX_ORIENTATION_ORTHO = 0;
+const int FastTMXLayer::FAST_TMX_ORIENTATION_HEX = 1;
+const int FastTMXLayer::FAST_TMX_ORIENTATION_ISO = 2;
+
+// FastTMXLayer - init & alloc & dealloc
+FastTMXLayer * FastTMXLayer::create(TMXTilesetInfo *tilesetInfo, TMXLayerInfo *layerInfo, TMXMapInfo *mapInfo)
+{
+    FastTMXLayer *ret = new (std::nothrow) FastTMXLayer();
+    if (ret->initWithTilesetInfo(tilesetInfo, layerInfo, mapInfo))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool FastTMXLayer::initWithTilesetInfo(TMXTilesetInfo *tilesetInfo, TMXLayerInfo *layerInfo, TMXMapInfo *mapInfo)
+{    
+
+    if( tilesetInfo )
+    {
+        _texture = Director::getInstance()->getTextureCache()->addImage(tilesetInfo->_sourceImage);
+        _texture->retain();
+    }
+
+    // layerInfo
+    _layerName = layerInfo->_name;
+    _layerSize = layerInfo->_layerSize;
+    _tiles = layerInfo->_tiles;
+    _quadsDirty = true;
+    setOpacity( layerInfo->_opacity );
+    setProperties(layerInfo->getProperties());
+
+    // tilesetInfo
+    _tileSet = tilesetInfo;
+    CC_SAFE_RETAIN(_tileSet);
+
+    // mapInfo
+    _mapTileSize = mapInfo->getTileSize();
+    _layerOrientation = mapInfo->getOrientation();
+
+    // offset (after layer orientation is set);
+    Vec2 offset = this->calculateLayerOffset(layerInfo->_offset);
+    this->setPosition(CC_POINT_PIXELS_TO_POINTS(offset));
+
+    this->setContentSize(CC_SIZE_PIXELS_TO_POINTS(Size(_layerSize.width * _mapTileSize.width, _layerSize.height * _mapTileSize.height)));
+    
+    this->tileToNodeTransform();
+
+    _useAutomaticVertexZ = false;
+    _vertexZvalue = 0;
+
+    return true;
+}
+
+FastTMXLayer::FastTMXLayer()
+{
+}
+
+FastTMXLayer::~FastTMXLayer()
+{
+    CC_SAFE_RELEASE(_tileSet);
+    CC_SAFE_RELEASE(_texture);
+    CC_SAFE_FREE(_tiles);
+    CC_SAFE_RELEASE(_vertexBuffer);
+    CC_SAFE_RELEASE(_indexBuffer);
+
+    for (auto& e : _customCommands)
+    {
+        CC_SAFE_RELEASE(e.second->getPipelineDescriptor().programState);
+        delete e.second;
+    }
+}
+
+void FastTMXLayer::draw(Renderer *renderer, const Mat4& transform, uint32_t flags)
+{
+    updateTotalQuads();
+    
+    if( flags != 0 || _dirty || _quadsDirty)
+    {
+        Size s = Director::getInstance()->getVisibleSize();
+        const Vec2 &anchor = getAnchorPoint();
+        auto rect = Rect(Camera::getVisitingCamera()->getPositionX() - s.width * (anchor.x == 0.0f ? 0.5f : anchor.x),
+                         Camera::getVisitingCamera()->getPositionY() - s.height * (anchor.y == 0.0f ? 0.5f : anchor.y),
+                     s.width,
+                     s.height);
+
+        
+        Mat4 inv = transform;
+        inv.inverse();
+        rect = RectApplyTransform(rect, inv);
+        
+        updateTiles(rect);
+        updateIndexBuffer();
+        updatePrimitives();
+        _dirty = false;
+    }
+
+    const auto& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    Mat4 finalMat = projectionMat * _modelViewTransform;
+    for (const auto& e : _customCommands)
+    {
+        if (e.second->getIndexDrawCount() > 0)
+        {
+            auto mvpmatrixLocation = e.second->getPipelineDescriptor().programState->getUniformLocation("u_MVPMatrix");
+            e.second->getPipelineDescriptor().programState->setUniform(mvpmatrixLocation, finalMat.m, sizeof(finalMat.m));
+            renderer->addCommand(e.second);
+        }
+    }
+}
+
+void FastTMXLayer::updateTiles(const Rect& culledRect)
+{
+    Rect visibleTiles = Rect(culledRect.origin, culledRect.size * Director::getInstance()->getContentScaleFactor());
+    Size mapTileSize = CC_SIZE_PIXELS_TO_POINTS(_mapTileSize);
+    Size tileSize = CC_SIZE_PIXELS_TO_POINTS(_tileSet->_tileSize);
+    Mat4 nodeToTileTransform = _tileToNodeTransform.getInversed();
+    //transform to tile
+    visibleTiles = RectApplyTransform(visibleTiles, nodeToTileTransform);
+    // tile coordinate is upside-down, so we need to make the tile coordinate use top-left for the start point.
+    visibleTiles.origin.y += 1;
+    
+    // if x=0.7, width=9.5, we need to draw number 0~10 of tiles, and so is height.
+    visibleTiles.size.width = ceil(visibleTiles.origin.x + visibleTiles.size.width)  - floor(visibleTiles.origin.x);
+    visibleTiles.size.height = ceil(visibleTiles.origin.y + visibleTiles.size.height) - floor(visibleTiles.origin.y);
+    visibleTiles.origin.x = floor(visibleTiles.origin.x);
+    visibleTiles.origin.y = floor(visibleTiles.origin.y);
+
+    // for the bigger tiles.
+    int tilesOverX = 0;
+    int tilesOverY = 0;
+    // for diagonal orientation tiles
+    float tileSizeMax = std::max(tileSize.width, tileSize.height);
+    if (_layerOrientation == FAST_TMX_ORIENTATION_ORTHO)
+    {
+        tilesOverX = (int)ceil(tileSizeMax / mapTileSize.width) - 1;
+        tilesOverY = (int)ceil(tileSizeMax / mapTileSize.height) - 1;
+        
+        if (tilesOverX < 0) tilesOverX = 0;
+        if (tilesOverY < 0) tilesOverY = 0;
+    }
+    else if(_layerOrientation == FAST_TMX_ORIENTATION_ISO)
+    {
+        Rect overTileRect(0, 0, tileSizeMax - mapTileSize.width, tileSizeMax - mapTileSize.height);
+        if (overTileRect.size.width < 0) overTileRect.size.width = 0;
+        if (overTileRect.size.height < 0) overTileRect.size.height = 0;
+        overTileRect = RectApplyTransform(overTileRect, nodeToTileTransform);
+        
+        tilesOverX = (int)(ceil(overTileRect.origin.x + overTileRect.size.width) - floor(overTileRect.origin.x));
+        tilesOverY = (int)(ceil(overTileRect.origin.y + overTileRect.size.height) - floor(overTileRect.origin.y));
+    }
+    else
+    {
+        //do nothing, do not support
+        //CCASSERT(0, "TMX invalid value");
+    }
+    
+    _indicesVertexZNumber.clear();
+    
+    for(const auto& iter : _indicesVertexZOffsets)
+    {
+        _indicesVertexZNumber[iter.first] = iter.second;
+    }
+    
+    int yBegin = static_cast<int>(std::max(0.f,visibleTiles.origin.y - tilesOverY));
+    int yEnd = static_cast<int>(std::min(_layerSize.height,visibleTiles.origin.y + visibleTiles.size.height + tilesOverY));
+    int xBegin = static_cast<int>(std::max(0.f,visibleTiles.origin.x - tilesOverX));
+    int xEnd = static_cast<int>(std::min(_layerSize.width,visibleTiles.origin.x + visibleTiles.size.width + tilesOverX));
+    
+    for (int y =  yBegin; y < yEnd; ++y)
+    {
+        for (int x = xBegin; x < xEnd; ++x)
+        {
+            int tileIndex = getTileIndexByPos(x, y);
+            if(_tiles[tileIndex] == 0) continue;
+            
+            int vertexZ = getVertexZForPos(Vec2((float)x,(float)y));
+            auto iter = _indicesVertexZNumber.find(vertexZ);
+            int offset = iter->second;
+            iter->second++;
+            
+            unsigned short quadIndex = static_cast<unsigned short>(_tileToQuadIndex[tileIndex]);
+            _indices[6 * offset + 0] = quadIndex * 4 + 0;
+            _indices[6 * offset + 1] = quadIndex * 4 + 1;
+            _indices[6 * offset + 2] = quadIndex * 4 + 2;
+            _indices[6 * offset + 3] = quadIndex * 4 + 3;
+            _indices[6 * offset + 4] = quadIndex * 4 + 2;
+            _indices[6 * offset + 5] = quadIndex * 4 + 1;
+            
+        } // for x
+    } // for y
+    
+    for(const auto& iter : _indicesVertexZOffsets)
+    {
+        _indicesVertexZNumber[iter.first] -= iter.second;
+        if(_indicesVertexZNumber[iter.first] == 0)
+        {
+            _indicesVertexZNumber.erase(iter.first);
+        }
+    }
+    
+}
+
+void FastTMXLayer::updateVertexBuffer()
+{
+    unsigned int vertexBufferSize = (unsigned int)(sizeof(V3F_C4B_T2F) * _totalQuads.size() * 4);
+    if (!_vertexBuffer)
+    {
+        auto device = backend::Device::getInstance();
+        _vertexBuffer = device->newBuffer(vertexBufferSize, backend::BufferType::VERTEX, backend::BufferUsage::STATIC);
+    }
+    _vertexBuffer->updateData(&_totalQuads[0], vertexBufferSize);
+}
+
+void FastTMXLayer::updateIndexBuffer()
+{
+#ifdef CC_FAST_TILEMAP_32_BIT_INDICES
+    unsigned int indexBufferSize = (unsigned int)(sizeof(unsigned int) * _indices.size());
+#else
+    unsigned int indexBufferSize = (unsigned int)(sizeof(unsigned short) * _indices.size());
+#endif
+    if (!_indexBuffer)
+    {
+        auto device = backend::Device::getInstance();
+        _indexBuffer = device->newBuffer(indexBufferSize, backend::BufferType::INDEX, backend::BufferUsage::DYNAMIC);
+    }
+    _indexBuffer->updateData(&_indices[0], indexBufferSize);
+}
+
+// FastTMXLayer - setup Tiles
+void FastTMXLayer::setupTiles()
+{    
+    // Optimization: quick hack that sets the image size on the tileset
+    _tileSet->_imageSize = _texture->getContentSizeInPixels();
+
+    // By default all the tiles are aliased
+    // pros: easier to render
+    // cons: difficult to scale / rotate / etc.
+    _texture->setAliasTexParameters();
+
+    //CFByteOrder o = CFByteOrderGetCurrent();
+
+    // Parse cocos2d properties
+    this->parseInternalProperties();
+
+    Size screenSize = Director::getInstance()->getWinSize();
+
+    switch (_layerOrientation)
+    {
+        case FAST_TMX_ORIENTATION_ORTHO:
+            _screenGridSize.width = ceil(screenSize.width / _mapTileSize.width) + 1;
+            _screenGridSize.height = ceil(screenSize.height / _mapTileSize.height) + 1;
+
+            // tiles could be bigger than the grid, add additional rows if needed
+            _screenGridSize.height += _tileSet->_tileSize.height / _mapTileSize.height;
+            break;
+        case FAST_TMX_ORIENTATION_ISO:
+            _screenGridSize.width = ceil(screenSize.width / _mapTileSize.width) + 2;
+            _screenGridSize.height = ceil(screenSize.height / (_mapTileSize.height/2)) + 4;
+            break;
+        case FAST_TMX_ORIENTATION_HEX:
+        default:
+            CCLOGERROR("FastTMX does not support type %d", _layerOrientation);
+            break;
+    }
+
+    _screenTileCount = (int)(_screenGridSize.width * _screenGridSize.height);
+
+}
+
+Mat4 FastTMXLayer::tileToNodeTransform()
+{
+    float w = _mapTileSize.width / CC_CONTENT_SCALE_FACTOR();
+    float h = _mapTileSize.height / CC_CONTENT_SCALE_FACTOR();
+    float offY = (_layerSize.height - 1) * h;
+    
+    switch(_layerOrientation)
+    {
+        case FAST_TMX_ORIENTATION_ORTHO:
+        {
+            _tileToNodeTransform = Mat4
+            (
+                w, 0.0f, 0.0f, 0.0f,
+                0.0f, -h, 0.0f, offY,
+                0.0f, 0.0f, 1.0f, 0.0f,
+                0.0f, 0.0, 0.0f, 1.0f
+            );
+            
+            return _tileToNodeTransform;
+        }
+        case FAST_TMX_ORIENTATION_ISO:
+        {
+            float offX = (_layerSize.width - 1) * w / 2;
+            _tileToNodeTransform = Mat4
+            (
+                w/2, -w/2, 0.0f, offX,
+                -h/2, -h/2, 0.0f, offY,
+                0.0f, 0.0f, 1.0f, 0.0f,
+                0.0f, 0.0f, 0.0f, 1.0f
+            );
+            return _tileToNodeTransform;
+        }
+        case FAST_TMX_ORIENTATION_HEX:
+        {
+            _tileToNodeTransform = Mat4
+            (
+                h * sqrtf(0.75),    0,   0, 0,
+                -h/2,      -h,      0,   offY,
+                0,               0,     1,  0,
+                0,               0,     0,  1
+            );
+            return _tileToNodeTransform;
+        }
+        default:
+        {
+            _tileToNodeTransform = Mat4::IDENTITY;
+            return _tileToNodeTransform;
+        }
+    }
+    
+}
+
+void FastTMXLayer::updatePrimitives()
+{
+    auto blendfunc = _texture->hasPremultipliedAlpha() ? BlendFunc::ALPHA_PREMULTIPLIED : BlendFunc::ALPHA_NON_PREMULTIPLIED;
+    for(const auto& iter : _indicesVertexZNumber)
+    {
+        int start = _indicesVertexZOffsets.at(iter.first);
+
+        auto commandIter = _customCommands.find(iter.first);
+        if (_customCommands.end() == commandIter)
+        {
+            auto command = new CustomCommand();
+            command->setVertexBuffer(_vertexBuffer);
+
+            CustomCommand::IndexFormat indexFormat = CustomCommand::IndexFormat::U_SHORT;
+#if CC_FAST_TILEMAP_32_BIT_INDICES
+            indexFormat = CustomCommand::IndexFormat::U_INT;
+#endif
+            command->setIndexBuffer(_indexBuffer, indexFormat);
+
+            command->setIndexDrawInfo(start * 6, iter.second * 6);
+
+            auto& pipelineDescriptor = command->getPipelineDescriptor();
+
+            if (_useAutomaticVertexZ)
+            {
+                CC_SAFE_RELEASE(pipelineDescriptor.programState);
+                auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR_ALPHA_TEST);
+                auto programState = new (std::nothrow) backend::ProgramState(program);
+                pipelineDescriptor.programState = programState;
+                _alphaValueLocation = pipelineDescriptor.programState->getUniformLocation("u_alpha_value");
+                pipelineDescriptor.programState->setUniform(_alphaValueLocation, &_alphaFuncValue, sizeof(_alphaFuncValue));
+            }
+            else
+            {
+                CC_SAFE_RELEASE(pipelineDescriptor.programState);
+                auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR);
+                auto programState = new (std::nothrow) backend::ProgramState(program);
+                pipelineDescriptor.programState = programState;
+            }
+            auto vertexLayout = pipelineDescriptor.programState->getVertexLayout();
+            const auto& attributeInfo = pipelineDescriptor.programState->getProgram()->getActiveAttributes();
+            auto iterAttribute = attributeInfo.find("a_position");
+            if(iterAttribute != attributeInfo.end())
+            {
+                vertexLayout->setAttribute("a_position", iterAttribute->second.location, backend::VertexFormat::FLOAT3, 0, false);
+            }
+            iterAttribute = attributeInfo.find("a_texCoord");
+            if(iterAttribute != attributeInfo.end())
+            {
+                vertexLayout->setAttribute("a_texCoord", iterAttribute->second.location, backend::VertexFormat::FLOAT2, offsetof(V3F_C4B_T2F, texCoords), false);
+            }
+            iterAttribute = attributeInfo.find("a_color");
+            if(iterAttribute != attributeInfo.end())
+            {
+                vertexLayout->setAttribute("a_color", iterAttribute->second.location, backend::VertexFormat::UBYTE4, offsetof(V3F_C4B_T2F, colors), true);
+            }
+            vertexLayout->setLayout(sizeof(V3F_C4B_T2F));
+            _mvpMatrixLocaiton = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix");
+            _textureLocation = pipelineDescriptor.programState->getUniformLocation("u_texture");
+            pipelineDescriptor.programState->setTexture(_textureLocation, 0, _texture->getBackendTexture());
+            command->init(_globalZOrder, blendfunc);
+
+            _customCommands[iter.first] = command;
+        }
+        else
+        {
+            commandIter->second->setIndexDrawInfo(start * 6, iter.second * 6);
+        }
+    }
+}
+
+void FastTMXLayer::setOpacity(uint8_t opacity) 
+{
+    Node::setOpacity(opacity);
+    _quadsDirty = true;
+}
+
+
+void FastTMXLayer::updateTotalQuads()
+{
+    if(_quadsDirty)
+    {
+        Size tileSize = CC_SIZE_PIXELS_TO_POINTS(_tileSet->_tileSize);
+        Size texSize = _tileSet->_imageSize;
+        _tileToQuadIndex.clear();
+        _totalQuads.resize(int(_layerSize.width * _layerSize.height));
+        _indices.resize(6 * int(_layerSize.width * _layerSize.height));
+        _tileToQuadIndex.resize(int(_layerSize.width * _layerSize.height),-1);
+        _indicesVertexZOffsets.clear();
+
+        auto color = Color4B::WHITE;
+        color.a = getDisplayedOpacity();
+
+        if (_texture->hasPremultipliedAlpha()) 
+        {
+            auto alpha = color.a / 255.0f;
+            color.r = static_cast<uint8_t>(color.r * alpha);
+            color.g = static_cast<uint8_t>(color.g * alpha);
+            color.b = static_cast<uint8_t>(color.b * alpha);
+        }
+
+        int quadIndex = 0;
+        for(int y = 0; y < _layerSize.height; ++y)
+        {
+            for(int x =0; x < _layerSize.width; ++x)
+            {
+                int tileIndex = getTileIndexByPos(x, y);
+                int tileGID = _tiles[tileIndex];
+                
+                if(tileGID == 0) continue;
+                
+                _tileToQuadIndex[tileIndex] = quadIndex;
+                
+                auto& quad = _totalQuads[quadIndex];
+                
+                Vec3 nodePos(float(x), float(y), 0);
+                _tileToNodeTransform.transformPoint(&nodePos);
+                
+                float left, right, top, bottom, z;
+                
+                int zPos = getVertexZForPos(Vec2((float)x, (float)y));
+                z = (float)zPos;
+                auto iter = _indicesVertexZOffsets.find(zPos);
+                if(iter == _indicesVertexZOffsets.end())
+                {
+                    _indicesVertexZOffsets[zPos] = 1;
+                }
+                else
+                {
+                    iter->second++;
+                }
+                // vertices
+                if (tileGID & kTMXTileDiagonalFlag)
+                {
+                    left = nodePos.x;
+                    right = nodePos.x + tileSize.height;
+                    bottom = nodePos.y + tileSize.width;
+                    top = nodePos.y;
+                }
+                else
+                {
+                    left = nodePos.x;
+                    right = nodePos.x + tileSize.width;
+                    bottom = nodePos.y + tileSize.height;
+                    top = nodePos.y;
+                }
+                
+                if(tileGID & kTMXTileVerticalFlag)
+                    std::swap(top, bottom);
+                if(tileGID & kTMXTileHorizontalFlag)
+                    std::swap(left, right);
+                
+                if(tileGID & kTMXTileDiagonalFlag)
+                {
+                    // FIXME: not working correctly
+                    quad.bl.vertices.x = left;
+                    quad.bl.vertices.y = bottom;
+                    quad.bl.vertices.z = z;
+                    quad.br.vertices.x = left;
+                    quad.br.vertices.y = top;
+                    quad.br.vertices.z = z;
+                    quad.tl.vertices.x = right;
+                    quad.tl.vertices.y = bottom;
+                    quad.tl.vertices.z = z;
+                    quad.tr.vertices.x = right;
+                    quad.tr.vertices.y = top;
+                    quad.tr.vertices.z = z;
+                }
+                else
+                {
+                    quad.bl.vertices.x = left;
+                    quad.bl.vertices.y = bottom;
+                    quad.bl.vertices.z = z;
+                    quad.br.vertices.x = right;
+                    quad.br.vertices.y = bottom;
+                    quad.br.vertices.z = z;
+                    quad.tl.vertices.x = left;
+                    quad.tl.vertices.y = top;
+                    quad.tl.vertices.z = z;
+                    quad.tr.vertices.x = right;
+                    quad.tr.vertices.y = top;
+                    quad.tr.vertices.z = z;
+                }
+                
+                // texcoords
+                Rect tileTexture = _tileSet->getRectForGID(tileGID);
+                left   = (tileTexture.origin.x / texSize.width);
+                right  = left + (tileTexture.size.width / texSize.width);
+                bottom = (tileTexture.origin.y / texSize.height);
+                top    = bottom + (tileTexture.size.height / texSize.height);
+                
+                quad.bl.texCoords.u = left;
+                quad.bl.texCoords.v = bottom;
+                quad.br.texCoords.u = right;
+                quad.br.texCoords.v = bottom;
+                quad.tl.texCoords.u = left;
+                quad.tl.texCoords.v = top;
+                quad.tr.texCoords.u = right;
+                quad.tr.texCoords.v = top;
+                
+                quad.bl.colors = color;
+                quad.br.colors = color;
+                quad.tl.colors = color;
+                quad.tr.colors = color;
+
+                
+                ++quadIndex;
+            }
+        }
+        
+        int offset = 0;
+        for(auto& vertexZOffset : _indicesVertexZOffsets)
+        {
+            std::swap(offset, vertexZOffset.second);
+            offset += vertexZOffset.second;
+        }
+        updateVertexBuffer();
+        
+        _quadsDirty = false;
+    }
+}
+
+// removing / getting tiles
+Sprite* FastTMXLayer::getTileAt(const Vec2& tileCoordinate)
+{
+    CCASSERT( tileCoordinate.x < _layerSize.width && tileCoordinate.y < _layerSize.height && tileCoordinate.x >=0 && tileCoordinate.y >=0, "TMXLayer: invalid position");
+    CCASSERT( _tiles, "TMXLayer: the tiles map has been released");
+    
+    Sprite *tile = nullptr;
+    int gid = this->getTileGIDAt(tileCoordinate);
+    
+    // if GID == 0, then no tile is present
+    if( gid ) {
+        int index = (int) tileCoordinate.x + (int)(tileCoordinate.y * _layerSize.width);
+        
+        auto it = _spriteContainer.find(index);
+        if (it != _spriteContainer.end())
+        {
+            tile = it->second.first;
+        }
+        else
+        {
+            // tile not created yet. create it
+            Rect rect = _tileSet->getRectForGID(gid);
+            rect = CC_RECT_PIXELS_TO_POINTS(rect);
+            tile = Sprite::createWithTexture(_texture, rect);
+            
+            Vec2 p = this->getPositionAt(tileCoordinate);
+            tile->setAnchorPoint(Vec2::ZERO);
+            tile->setPosition(p);
+            tile->setPositionZ((float)getVertexZForPos(tileCoordinate));
+            tile->setOpacity(this->getOpacity());
+            tile->setTag(index);
+            this->addChild(tile, index);
+            _spriteContainer.insert(std::pair<int, std::pair<Sprite*, int> >(index, std::pair<Sprite*, int>(tile, gid)));
+            
+            // tile is converted to sprite.
+            setFlaggedTileGIDByIndex(index, 0);
+        }
+    }
+    return tile;
+}
+
+int FastTMXLayer::getTileGIDAt(const Vec2& tileCoordinate, TMXTileFlags* flags/* = nullptr*/)
+{
+    CCASSERT(tileCoordinate.x < _layerSize.width && tileCoordinate.y < _layerSize.height && tileCoordinate.x >=0 && tileCoordinate.y >=0, "TMXLayer: invalid position");
+    CCASSERT(_tiles, "TMXLayer: the tiles map has been released");
+    
+    int idx = static_cast<int>(((int) tileCoordinate.x + (int) tileCoordinate.y * _layerSize.width));
+    
+    // Bits on the far end of the 32-bit global tile ID are used for tile flags
+    int tile = _tiles[idx];
+    auto it = _spriteContainer.find(idx);
+    
+    // converted to sprite.
+    if (tile == 0 && it != _spriteContainer.end())
+    {
+        tile = it->second.second;
+    }
+    
+    // issue1264, flipped tiles can be changed dynamically
+    if (flags)
+    {
+        *flags = (TMXTileFlags)(tile & kTMXFlipedAll);
+    }
+    
+    return (tile & kTMXFlippedMask);
+}
+
+Vec2 FastTMXLayer::getPositionAt(const Vec2& pos)
+{
+    return PointApplyTransform(pos, _tileToNodeTransform);
+}
+
+int FastTMXLayer::getVertexZForPos(const Vec2& pos)
+{
+    int ret = 0;
+    int maxVal = 0;
+    if (_useAutomaticVertexZ)
+    {
+        switch (_layerOrientation)
+        {
+            case FAST_TMX_ORIENTATION_ISO:
+                maxVal = static_cast<int>(_layerSize.width + _layerSize.height);
+                ret = static_cast<int>(-(maxVal - (pos.x + pos.y)));
+                break;
+            case FAST_TMX_ORIENTATION_ORTHO:
+                ret = static_cast<int>(-(_layerSize.height-pos.y));
+                break;
+            case FAST_TMX_ORIENTATION_HEX:
+                CCASSERT(0, "TMX Hexa vertexZ not supported");
+                break;
+            default:
+                CCASSERT(0, "TMX invalid value");
+                break;
+        }
+    }
+    else
+    {
+        ret = _vertexZvalue;
+    }
+    
+    return ret;
+}
+
+void FastTMXLayer::removeTileAt(const Vec2& tileCoordinate)
+{
+    
+    CCASSERT( tileCoordinate.x < _layerSize.width && tileCoordinate.y < _layerSize.height && tileCoordinate.x >=0 && tileCoordinate.y >=0, "TMXLayer: invalid position");
+    
+    int gid = this->getTileGIDAt(tileCoordinate);
+    
+    if( gid ) {
+        
+        int z = (int) tileCoordinate.x + (int)(tileCoordinate.y * _layerSize.width);
+        
+        // remove tile from GID map
+        setFlaggedTileGIDByIndex(z, 0);
+        
+        // remove it from sprites
+        auto it = _spriteContainer.find(z);
+        if (it != _spriteContainer.end())
+        {
+            this->removeChild(it->second.first);
+        }
+    }
+}
+
+void FastTMXLayer::setFlaggedTileGIDByIndex(int index, uint32_t gid)
+{
+    if(gid == _tiles[index]) return;
+    _tiles[index] = gid;
+    _quadsDirty = true;
+    _dirty = true;
+}
+
+void FastTMXLayer::removeChild(Node* node, bool cleanup)
+{
+    int tag = node->getTag();
+    auto it = _spriteContainer.find(tag);
+    if (it != _spriteContainer.end() && it->second.first == node)
+    {
+        _spriteContainer.erase(it);
+    }
+    Node::removeChild(node, cleanup);
+}
+
+// TMXLayer - Properties
+Value FastTMXLayer::getProperty(const std::string& propertyName) const
+{
+    auto propItr = _properties.find(propertyName);
+    if (propItr != _properties.end())
+        return propItr->second;
+    
+    return Value();
+}
+
+void FastTMXLayer::parseInternalProperties()
+{
+    auto vertexz = getProperty("cc_vertexz");
+    if (vertexz.isNull()) return;
+    
+    std::string vertexZStr = vertexz.asString();
+    // If "automatic" is on, then parse the "cc_alpha_func" too
+    if (vertexZStr == "automatic")
+    {
+        _useAutomaticVertexZ = true;
+        auto alphaFuncVal = getProperty("cc_alpha_func");
+        _alphaFuncValue = alphaFuncVal.asFloat();
+    }
+    else
+    {
+        _vertexZvalue = vertexz.asInt();
+    }
+}
+
+//CCTMXLayer2 - obtaining positions, offset
+Vec2 FastTMXLayer::calculateLayerOffset(const Vec2& pos)
+{
+    Vec2 ret;
+    switch (_layerOrientation) 
+    {
+    case FAST_TMX_ORIENTATION_ORTHO:
+        ret.set( pos.x * _mapTileSize.width, -pos.y *_mapTileSize.height);
+        break;
+    case FAST_TMX_ORIENTATION_ISO:
+        ret.set((_mapTileSize.width /2) * (pos.x - pos.y),
+                  (_mapTileSize.height /2 ) * (-pos.x - pos.y));
+        break;
+    case FAST_TMX_ORIENTATION_HEX:
+    default:
+        CCASSERT(pos.isZero(), "offset for this map not implemented yet");
+        break;
+    }
+    return ret;    
+}
+
+// TMXLayer - adding / remove tiles
+void FastTMXLayer::setTileGID(int gid, const Vec2& tileCoordinate)
+{
+    setTileGID(gid, tileCoordinate, (TMXTileFlags)0);
+}
+
+void FastTMXLayer::setTileGID(int gid, const Vec2& tileCoordinate, TMXTileFlags flags)
+{
+    CCASSERT(tileCoordinate.x < _layerSize.width && tileCoordinate.y < _layerSize.height && tileCoordinate.x >=0 && tileCoordinate.y >=0, "TMXLayer: invalid position");
+    CCASSERT(_tiles, "TMXLayer: the tiles map has been released");
+    CCASSERT(gid == 0 || gid >= _tileSet->_firstGid, "TMXLayer: invalid gid" );
+    
+    TMXTileFlags currentFlags;
+    int currentGID = getTileGIDAt(tileCoordinate, &currentFlags);
+    
+    if (currentGID == gid && currentFlags == flags) return;
+    
+    uint32_t gidAndFlags = gid | flags;
+    
+    // setting gid=0 is equal to remove the tile
+    if (gid == 0)
+    {
+        removeTileAt(tileCoordinate);
+    }
+    // empty tile. create a new one
+    else if (currentGID == 0)
+    {
+        int z = (int) tileCoordinate.x + (int)(tileCoordinate.y * _layerSize.width);
+        setFlaggedTileGIDByIndex(z, gidAndFlags);
+    }
+    // modifying an existing tile with a non-empty tile
+    else
+    {
+        int z = (int) tileCoordinate.x + (int)(tileCoordinate.y * _layerSize.width);
+        auto it = _spriteContainer.find(z);
+        if (it != _spriteContainer.end())
+        {
+            Sprite *sprite = it->second.first;
+            Rect rect = _tileSet->getRectForGID(gid);
+            rect = CC_RECT_PIXELS_TO_POINTS(rect);
+            
+            sprite->setTextureRect(rect, false, rect.size);
+            this->reorderChild(sprite, z);
+            if (flags)
+            {
+                setupTileSprite(sprite, sprite->getPosition(), gidAndFlags);
+            }
+            
+            it->second.second = gidAndFlags;
+        }
+        else
+        {
+            setFlaggedTileGIDByIndex(z, gidAndFlags);
+        }
+    }
+}
+
+void FastTMXLayer::setupTileSprite(Sprite* sprite, const Vec2& pos, uint32_t gid)
+{
+    sprite->setPosition(getPositionAt(pos));
+    sprite->setPositionZ((float)getVertexZForPos(pos));
+    sprite->setAnchorPoint(Vec2::ZERO);
+    sprite->setOpacity(this->getOpacity());
+    
+    //issue 1264, flip can be undone as well
+    sprite->setFlippedX(false);
+    sprite->setFlippedY(false);
+    sprite->setRotation(0.0f);
+    
+    // Rotation in tiled is achieved using 3 flipped states, flipping across the horizontal, vertical, and diagonal axes of the tiles.
+    if (gid & kTMXTileDiagonalFlag)
+    {
+        // put the anchor in the middle for ease of rotation.
+        sprite->setAnchorPoint(Vec2(0.5f,0.5f));
+        sprite->setPosition(getPositionAt(pos).x + sprite->getContentSize().height/2,
+                                  getPositionAt(pos).y + sprite->getContentSize().width/2 );
+        
+        uint32_t flag = gid & (kTMXTileHorizontalFlag | kTMXTileVerticalFlag );
+        
+        // handle the 4 diagonally flipped states.
+        if (flag == kTMXTileHorizontalFlag)
+        {
+            sprite->setRotation(90.0f);
+        }
+        else if (flag == kTMXTileVerticalFlag)
+        {
+            sprite->setRotation(270.0f);
+        }
+        else if (flag == (kTMXTileVerticalFlag | kTMXTileHorizontalFlag) )
+        {
+            sprite->setRotation(90.0f);
+            sprite->setFlippedX(true);
+        }
+        else
+        {
+            sprite->setRotation(270.0f);
+            sprite->setFlippedX(true);
+        }
+    }
+    else
+    {
+        if (gid & kTMXTileHorizontalFlag)
+        {
+            sprite->setFlippedX(true);
+        }
+        
+        if (gid & kTMXTileVerticalFlag)
+        {
+            sprite->setFlippedY(true);
+        }
+    }
+}
+
+std::string FastTMXLayer::getDescription() const
+{
+    return StringUtils::format("<FastTMXLayer | tag = %d, size = %d,%d>", _tag, (int)_mapTileSize.width, (int)_mapTileSize.height);
+}
+
+
+NS_CC_END

+ 361 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFastTMXLayer.h

@@ -0,0 +1,361 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#pragma once
+
+#include <unordered_map>
+#include "2d/CCNode.h"
+#include "2d/CCTMXXMLParser.h"
+#include "renderer/CCCustomCommand.h"
+
+NS_CC_BEGIN
+
+class TMXMapInfo;
+class TMXLayerInfo;
+class TMXTilesetInfo;
+class Texture2D;
+class Sprite;
+
+namespace backend
+{
+    class Buffer;
+}
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** @brief FastTMXLayer represents the TMX layer.
+
+ * It is a subclass of SpriteBatchNode. By default the tiles are rendered using a TextureAtlas.
+ * If you modify a tile on runtime, then, that tile will become a Sprite, otherwise no Sprite objects are created.
+ * The benefits of using Sprite objects as tiles are:
+ * - tiles (Sprite) can be rotated/scaled/moved with a nice API.
+
+ * If the layer contains a property named "cc_vertexz" with an integer (in can be positive or negative),
+ * then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth.
+
+ * On the other hand, if the "cc_vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value.
+ * Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawing them. The used alpha func will be:
+
+ * glAlphaFunc( GL_GREATER, value ).
+
+ * "value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer.
+ * The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a different
+ * value, like 0.5.
+ 
+ * For further information, please see the programming guide:
+ * http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:tiled_maps
+ 
+ * @since v3.2
+ * @js NA
+ */
+
+class CC_DLL FastTMXLayer : public Node
+{
+public:
+    /** Possible orientations of the TMX map */
+    static const int FAST_TMX_ORIENTATION_ORTHO;
+    static const int FAST_TMX_ORIENTATION_HEX;
+    static const int FAST_TMX_ORIENTATION_ISO;
+
+    /** Creates a FastTMXLayer with an tileset info, a layer info and a map info.
+     *
+     * @param tilesetInfo An tileset info.
+     * @param layerInfo A layer info.
+     * @param mapInfo A map info.
+     * @return Return an autorelease object.
+     */
+    static FastTMXLayer * create(TMXTilesetInfo *tilesetInfo, TMXLayerInfo *layerInfo, TMXMapInfo *mapInfo);
+    /**
+     * @js ctor
+     */
+    FastTMXLayer();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~FastTMXLayer();
+
+    /** Returns the tile gid at a given tile coordinate. It also returns the tile flags.
+     * 
+     * @param tileCoordinate The tile coordinate.
+     * @param flags A TMXTileFlags.
+     * @return The tile gid at a given tile coordinate. It also returns the tile flags.
+     */
+    int getTileGIDAt(const Vec2& tileCoordinate, TMXTileFlags* flags = nullptr);
+
+    /** Sets the tile gid (gid = tile global id) at a given tile coordinate.
+     * The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor -> Tileset Mgr +1.
+     * If a tile is already placed at that position, then it will be removed.
+     * @param gid The gid value.
+     * @param tileCoordinate The tile coordinate.
+     */
+    void setTileGID(int gid, const Vec2& tileCoordinate);
+
+    /** Sets the tile gid (gid = tile global id) at a given tile coordinate.
+     * The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor -> Tileset Mgr +1.
+     * If a tile is already placed at that position, then it will be removed.
+     * Use withFlags if the tile flags need to be changed as well.
+     *
+     * @param gid A integer value,it will be sets the tile gid.
+     * @param tileCoordinate The tile coordinate.
+     * @param flags A TMXTileFlags.
+     */
+    void setTileGID(int gid, const Vec2& tileCoordinate, TMXTileFlags flags);
+
+    /** Removes a tile at given tile coordinate.
+     *
+     * @param tileCoordinate The tile Coordinate.
+     */
+    void removeTileAt(const Vec2& tileCoordinate);
+
+    /** Returns the position in points of a given tile coordinate.
+     *
+     * @param tileCoordinate The tile Coordinate.
+     * @return The position in points of a given tile coordinate.
+     */
+    Vec2 getPositionAt(const Vec2& tileCoordinate);
+
+    /** Return the value for the specific property name.
+     *
+     * @param propertyName The value for the specific property name.
+     * @return The value for the specific property name.
+     */
+    Value getProperty(const std::string& propertyName) const;
+
+    /** Creates the tiles. */
+    void setupTiles();
+    
+    /** Get the tile layer name.
+     *
+     * @return The tile layer name.
+     */
+    const std::string& getLayerName() { return _layerName; }
+    
+    /** Set the tile layer name.
+     *
+     * @param layerName The new layer name.
+     */
+    void setLayerName(const std::string& layerName) { _layerName = layerName; }
+
+    /** Size of the layer in tiles.
+     *
+     * @return Size of the layer in tiles.
+     */
+    const Size& getLayerSize() const { return _layerSize; }
+    
+    /** Set the size of the layer in tiles. 
+     *
+     * @param size The new size of the layer in tiles.
+     */
+    void setLayerSize(const Size& size) { _layerSize = size; }
+    
+    /** Size of the map's tile (could be different from the tile's size).
+     *
+     * @return Size of the map's tile (could be different from the tile's size).
+     */
+    const Size& getMapTileSize() const { return _mapTileSize; }
+    
+    /** Set the size of the map's tile. 
+     *
+     * @param size The new size of the map's tile.
+     */
+    void setMapTileSize(const Size& size) { _mapTileSize = size; }
+    
+    /** Pointer to the map of tiles.
+     * @js NA
+     * @lua NA
+     * @return The pointer to the map of tiles.
+     */
+    const uint32_t* getTiles() const { return _tiles; };
+    
+    /** Set the pointer to the map of tiles.
+     *
+     * @param tiles The pointer to the map of tiles.
+     */
+    void setTiles(uint32_t* tiles) { _tiles = tiles; _quadsDirty = true;};
+    
+    /** Tileset information for the layer.
+     *
+     * @return Tileset information for the layer.
+     */
+    TMXTilesetInfo* getTileSet() const { return _tileSet; }
+    
+    /** Set the tileset information for the layer. 
+     *
+     * @param info The new tileset information for the layer.
+     */
+    void setTileSet(TMXTilesetInfo* info) {
+        CC_SAFE_RETAIN(info);
+        CC_SAFE_RELEASE(_tileSet);
+        _tileSet = info;
+    }
+    
+    /** Layer orientation, which is the same as the map orientation.
+     *
+     * @return Layer orientation, which is the same as the map orientation.
+     */
+    int getLayerOrientation() const { return _layerOrientation; }
+    
+    /** Set Layer orientation, which is the same as the map orientation. 
+     *
+     * @param orientation Layer orientation, which is the same as the map orientation.
+     */
+    void setLayerOrientation(int orientation) { _layerOrientation = orientation; }
+    
+    /** Properties from the layer. They can be added using Tiled. 
+     *
+     * @return Properties from the layer. They can be added using Tiled.
+     */
+    const ValueMap& getProperties() const { return _properties; }
+    
+    /** Properties from the layer. They can be added using Tiled.
+     *
+     * @return Properties from the layer. They can be added using Tiled.
+     */
+    ValueMap& getProperties() { return _properties; }
+    
+    /** Set the properties to the layer.
+     *
+     * @param properties The properties to the layer.
+     */
+    void setProperties(const ValueMap& properties)
+    {
+        _properties = properties;
+    }
+
+    /** Returns the tile (Sprite) at a given a tile coordinate.
+     * The returned Sprite will be already added to the TMXLayer. Don't add it again.
+     * The Sprite can be treated like any other Sprite: rotated, scaled, translated, opacity, color, etc.
+     * You can remove either by calling:
+     * - layer->removeChild(sprite, cleanup);
+     *
+     * @return Returns the tile (Sprite) at a given a tile coordinate.
+     */
+    Sprite* getTileAt(const Vec2& tileCoordinate);
+    
+    /** Set an sprite to the tile,with the tile coordinate and gid.
+     *
+     * @param sprite A Sprite.
+     * @param pos The tile coordinate.
+     * @param gid The tile gid.
+     */
+    void setupTileSprite(Sprite* sprite, const Vec2& pos, uint32_t gid);
+
+    //
+    // Override
+    //
+    virtual std::string getDescription() const override;
+    virtual void draw(Renderer *renderer, const Mat4& transform, uint32_t flags) override;
+    void removeChild(Node* child, bool cleanup = true) override;
+
+protected:
+    virtual void setOpacity(uint8_t opacity) override;
+
+    bool initWithTilesetInfo(TMXTilesetInfo *tilesetInfo, TMXLayerInfo *layerInfo, TMXMapInfo *mapInfo);
+    void updateTiles(const Rect& culledRect);
+    Vec2 calculateLayerOffset(const Vec2& offset);
+
+    /* The layer recognizes some special properties, like cc_vertexz */
+    void parseInternalProperties();
+    
+    Mat4 tileToNodeTransform();
+    Rect tileBoundsForClipTransform(const Mat4 &tileToClip);
+    
+    int getVertexZForPos(const Vec2& pos);
+    
+    //Flip flags is packed into gid
+    void setFlaggedTileGIDByIndex(int index, uint32_t gid);
+    
+    //
+    void updateTotalQuads();
+    
+    int getTileIndexByPos(int x, int y) const { return x + y * (int) _layerSize.width; }
+    
+    void updateVertexBuffer();
+    void updateIndexBuffer();
+    void updatePrimitives();
+
+    //! name of the layer
+    std::string _layerName;
+
+    /** size of the layer in tiles */
+    Size _layerSize;
+    /** size of the map's tile (could be different from the tile's size) */
+    Size _mapTileSize;
+    /** pointer to the map of tiles */
+    uint32_t* _tiles = nullptr;
+    /** Tileset information for the layer */
+    TMXTilesetInfo* _tileSet = nullptr;
+    /** Layer orientation, which is the same as the map orientation */
+    int _layerOrientation = FAST_TMX_ORIENTATION_ORTHO;
+    /** properties from the layer. They can be added using Tiled */
+    ValueMap _properties;
+
+    Texture2D *_texture = nullptr;
+    
+    /** container for sprite children. map<index, pair<sprite, gid> > */
+    std::map<int, std::pair<Sprite*, int> > _spriteContainer;
+
+    Size _screenGridSize;
+    Rect _screenGridRect;
+    int _screenTileCount = 0;
+    
+    int _vertexZvalue = 0;
+    bool _useAutomaticVertexZ = false;
+    
+    /** tile coordinate to node coordinate transform */
+    Mat4 _tileToNodeTransform;
+    /** data for rendering */
+    bool _quadsDirty = true;
+    std::vector<int> _tileToQuadIndex;
+    std::vector<V3F_C4B_T2F_Quad> _totalQuads;
+#ifdef CC_FAST_TILEMAP_32_BIT_INDICES
+    std::vector<unsigned int> _indices;
+#else
+    std::vector<unsigned short> _indices;
+#endif
+    std::map<int/*vertexZ*/, int/*offset to _indices by quads*/> _indicesVertexZOffsets;
+    std::unordered_map<int/*vertexZ*/, int/*number to quads*/> _indicesVertexZNumber;
+    bool _dirty = true;
+    
+    backend::Buffer* _vertexBuffer = nullptr;
+    backend::Buffer* _indexBuffer = nullptr;
+
+    float _alphaFuncValue = 0.f;
+    std::unordered_map<int, CustomCommand*> _customCommands;
+    
+    backend::UniformLocation _mvpMatrixLocaiton;
+    backend::UniformLocation _textureLocation;
+    backend::UniformLocation _alphaValueLocation;
+};
+
+// end of tilemap_parallax_nodes group
+/// @}
+NS_CC_END

+ 260 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFastTMXTiledMap.cpp

@@ -0,0 +1,260 @@
+/****************************************************************************
+Copyright (c) 2009-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCFastTMXTiledMap.h"
+#include "2d/CCFastTMXLayer.h"
+#include "base/ccUTF8.h"
+
+NS_CC_BEGIN
+
+// implementation FastTMXTiledMap
+
+FastTMXTiledMap * FastTMXTiledMap::create(const std::string& tmxFile)
+{
+    FastTMXTiledMap *ret = new (std::nothrow) FastTMXTiledMap();
+    if (ret->initWithTMXFile(tmxFile))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+FastTMXTiledMap* FastTMXTiledMap::createWithXML(const std::string& tmxString, const std::string& resourcePath)
+{
+    FastTMXTiledMap *ret = new (std::nothrow) FastTMXTiledMap();
+    if (ret->initWithXML(tmxString, resourcePath))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool FastTMXTiledMap::initWithTMXFile(const std::string& tmxFile)
+{
+    CCASSERT(tmxFile.size()>0, "FastTMXTiledMap: tmx file should not be empty");
+    
+    setContentSize(Size::ZERO);
+
+    TMXMapInfo *mapInfo = TMXMapInfo::create(tmxFile);
+
+    if (! mapInfo)
+    {
+        return false;
+    }
+    CCASSERT( !mapInfo->getTilesets().empty(), "FastTMXTiledMap: Map not found. Please check the filename.");
+    buildWithMapInfo(mapInfo);
+
+    return true;
+}
+
+bool FastTMXTiledMap::initWithXML(const std::string& tmxString, const std::string& resourcePath)
+{
+    setContentSize(Size::ZERO);
+
+    TMXMapInfo *mapInfo = TMXMapInfo::createWithXML(tmxString, resourcePath);
+
+    CCASSERT( !mapInfo->getTilesets().empty(), "FastTMXTiledMap: Map not found. Please check the filename.");
+    buildWithMapInfo(mapInfo);
+
+    return true;
+}
+
+FastTMXTiledMap::FastTMXTiledMap()
+    :_mapSize(Size::ZERO)
+    ,_tileSize(Size::ZERO)        
+{
+}
+
+FastTMXTiledMap::~FastTMXTiledMap()
+{
+}
+
+// private
+FastTMXLayer * FastTMXTiledMap::parseLayer(TMXLayerInfo *layerInfo, TMXMapInfo *mapInfo)
+{
+    TMXTilesetInfo *tileset = tilesetForLayer(layerInfo, mapInfo);
+    if (tileset == nullptr)
+        return nullptr;
+    
+    FastTMXLayer *layer = FastTMXLayer::create(tileset, layerInfo, mapInfo);
+
+    // tell the layerinfo to release the ownership of the tiles map.
+    layerInfo->_ownTiles = false;
+    layer->setupTiles();
+
+    return layer;
+}
+
+TMXTilesetInfo * FastTMXTiledMap::tilesetForLayer(TMXLayerInfo *layerInfo, TMXMapInfo *mapInfo)
+{
+    Size size = layerInfo->_layerSize;
+    auto& tilesets = mapInfo->getTilesets();
+
+    for (auto iter = tilesets.crbegin(), iterCrend = tilesets.crend(); iter != iterCrend; ++iter)
+    {
+        TMXTilesetInfo* tilesetInfo = *iter;
+        if (tilesetInfo)
+        {
+            for( int y=0; y < size.height; y++ )
+            {
+                for( int x=0; x < size.width; x++ )
+                {
+                    uint32_t pos = static_cast<uint32_t>(x + size.width * y);
+                    uint32_t gid = layerInfo->_tiles[ pos ];
+                    
+                    // gid are stored in little endian.
+                    // if host is big endian, then swap
+                    //if( o == CFByteOrderBigEndian )
+                    //    gid = CFSwapInt32( gid );
+                    /* We support little endian.*/
+                    
+                    // FIXME: gid == 0 --> empty tile
+                    if( gid != 0 )
+                    {
+                        // Optimization: quick return
+                        // if the layer is invalid (more than 1 tileset per layer) an CCAssert will be thrown later
+                        if( (gid & kTMXFlippedMask)
+                            >= static_cast<uint32_t>(tilesetInfo->_firstGid))
+                        {
+                            return tilesetInfo;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // If all the tiles are 0, return empty tileset
+    CCLOG("cocos2d: Warning: TMX Layer '%s' has no tiles", layerInfo->_name.c_str());
+    return nullptr;
+}
+
+void FastTMXTiledMap::buildWithMapInfo(TMXMapInfo* mapInfo)
+{
+    _mapSize = mapInfo->getMapSize();
+    _tileSize = mapInfo->getTileSize();
+    _mapOrientation = mapInfo->getOrientation();
+
+    _objectGroups = mapInfo->getObjectGroups();
+
+    _properties = mapInfo->getProperties();
+
+    _tileProperties = mapInfo->getTileProperties();
+
+    int idx=0;
+
+    auto& layers = mapInfo->getLayers();
+    for(const auto &layerInfo : layers) {
+        if (layerInfo->_visible)
+        {
+            FastTMXLayer *child = parseLayer(layerInfo, mapInfo);
+            if (child == nullptr) {
+                idx++;
+                continue;
+            }
+            addChild(child, idx, idx);
+            
+            // update content size with the max size
+            const Size& childSize = child->getContentSize();
+            Size currentSize = this->getContentSize();
+            currentSize.width = std::max( currentSize.width, childSize.width );
+            currentSize.height = std::max( currentSize.height, childSize.height );
+            this->setContentSize(currentSize);
+            
+            idx++;
+        }
+    }
+}
+
+// public
+FastTMXLayer * FastTMXTiledMap::getLayer(const std::string& layerName) const
+{
+    CCASSERT(layerName.size() > 0, "Invalid layer name!");
+    
+    for (auto& child : _children)
+    {
+        FastTMXLayer* layer = dynamic_cast<FastTMXLayer*>(child);
+        if(layer)
+        {
+            if(layerName.compare( layer->getLayerName()) == 0)
+            {
+                return layer;
+            }
+        }
+    }
+
+    // layer not found
+    return nullptr;
+}
+
+TMXObjectGroup * FastTMXTiledMap::getObjectGroup(const std::string& groupName) const
+{
+    CCASSERT(groupName.size() > 0, "Invalid group name!");
+
+    if (_objectGroups.size()>0)
+    {
+        for (const auto& objectGroup : _objectGroups)
+        {
+            if (objectGroup && objectGroup->getGroupName() == groupName)
+            {
+                return objectGroup;
+            }
+        }
+    }
+
+    // objectGroup not found
+    return nullptr;
+}
+
+Value FastTMXTiledMap::getProperty(const std::string& propertyName) const
+{
+    auto propsItr = _properties.find(propertyName);
+    if (propsItr != _properties.end())
+        return propsItr->second;
+    
+    return Value();
+}
+
+Value FastTMXTiledMap::getPropertiesForGID(int GID) const
+{
+    auto propsItr = _tileProperties.find(GID);
+    if (propsItr != _tileProperties.end())
+        return propsItr->second;
+    
+    return Value();
+}
+
+std::string FastTMXTiledMap::getDescription() const
+{
+    return StringUtils::format("<FastTMXTiledMap | Tag = %d, Layers = %d", _tag, static_cast<int>(_children.size()));
+}
+
+NS_CC_END

+ 248 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFastTMXTiledMap.h

@@ -0,0 +1,248 @@
+/****************************************************************************
+Copyright (c) 2009-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#pragma once
+
+#include "2d/CCNode.h"
+#include "2d/CCTMXObjectGroup.h"
+
+NS_CC_BEGIN
+
+class TMXLayerInfo;
+class TMXTilesetInfo;
+class TMXMapInfo;
+class FastTMXLayer;
+/**
+ * @addtogroup _2d
+ * @{
+ */
+    
+/** @brief FastTMXTiledMap knows how to parse and render a TMX map.
+
+ * It adds support for the TMX tiled map format used by http://www.mapeditor.org.
+ * It supports isometric, hexagonal and orthogonal tiles.
+ * It also supports object groups, objects, and properties.
+
+ * Features:
+ * - Each tile will be treated as an Sprite.
+ * - The sprites are created on demand. They will be created only when you call "layer->tileAt(position)".
+ * - Each tile can be rotated / moved / scaled / tinted / "opaqued", since each tile is a Sprite.
+ * - Tiles can be added/removed in runtime.
+ * - The z-order of the tiles can be modified in runtime.
+ * - Each tile has an anchorPoint of (0,0).
+ * - The anchorPoint of the TMXTileMap is (0,0).
+ * - The TMX layers will be added as a child.
+ * - The TMX layers will be aliased by default.
+ * - The tileset image will be loaded using the TextureCache.
+ * - Each tile will have a unique tag.
+ * - Each tile will have a unique z value. top-left: z=1, bottom-right: z=max z.
+ * - Each object group will be treated as an MutableArray.
+ * - Object class which will contain all the properties in a dictionary.
+ * - Properties can be assigned to the Map, Layer, Object Group, and Object.
+
+ * Limitations:
+ * - It only supports one tileset per layer.
+ * - Embedded images are not supported.
+ * - It only supports the XML format (the JSON format is not supported).
+
+ * Technical description:
+ * Each layer is created using an FastTMXLayer (subclass of SpriteBatchNode). If you have 5 layers, then 5 FastTMXLayer will be created,
+ * unless the layer visibility is off. In that case, the layer won't be created at all.
+ * You can obtain the layers (FastTMXLayer objects) at runtime by:
+ * - map->getChildByTag(tag_number);  // 0=1st layer, 1=2nd layer, 2=3rd layer, etc...
+ * - map->getLayer(name_of_the_layer);
+
+ * Each object group is created using a TMXObjectGroup which is a subclass of MutableArray.
+ * You can obtain the object groups at runtime by:
+ * - map->getObjectGroup(name_of_the_object_group);
+
+ * Each object is a TMXObject.
+
+ * Each property is stored as a key-value pair in an MutableDictionary.
+ * You can obtain the properties at runtime by:
+
+ * map->getProperty(name_of_the_property);
+ * layer->getProperty(name_of_the_property);
+ * objectGroup->getProperty(name_of_the_property);
+ * object->getProperty(name_of_the_property);
+
+ * @since v3.2
+ * @js NA
+ */
+class CC_DLL FastTMXTiledMap : public Node
+{
+public:
+    /** Creates a TMX Tiled Map with a TMX file.
+     *
+     * @return An autorelease object.
+     */
+    static FastTMXTiledMap* create(const std::string& tmxFile);
+
+    /** Initializes a TMX Tiled Map with a TMX formatted XML string and a path to TMX resources. 
+     *
+     * @param tmxString A TMX formatted XML string.
+     * @param resourcePath A path to TMX resources.
+     * @return An autorelease object.
+     */
+    static FastTMXTiledMap* createWithXML(const std::string& tmxString, const std::string& resourcePath);
+
+    /** Return the FastTMXLayer for the specific layer. 
+     * 
+     * @return Return the FastTMXLayer for the specific layer.
+     */
+    FastTMXLayer* getLayer(const std::string& layerName) const;
+
+    /** Return the TMXObjectGroup for the specific group. 
+     * 
+     * @return Return the TMXObjectGroup for the specific group.
+     */
+    TMXObjectGroup* getObjectGroup(const std::string& groupName) const;
+
+    /** Return the value for the specific property name.
+     *
+     * @return Return the value for the specific property name.
+     */
+    Value getProperty(const std::string& propertyName) const;
+
+    /** Return properties dictionary for tile GID.
+     *
+     * @return Return properties dictionary for tile GID.
+     */
+    Value getPropertiesForGID(int GID) const;
+
+    /** The map's size property measured in tiles. 
+     *
+     * @return The map's size property measured in tiles.
+     */
+    const Size& getMapSize() const { return _mapSize; }
+    
+    /** Set the map's size property measured in tiles.
+     *
+     * @param mapSize The map's size property measured in tiles.
+     */
+    void setMapSize(const Size& mapSize) { _mapSize = mapSize; }
+
+    /** The tiles's size property measured in pixels.
+     *
+     * @return The tiles's size property measured in pixels.
+     */
+    const Size& getTileSize() const { return _tileSize; }
+    
+    /** Set the tiles's size property measured in pixels. 
+     *
+     * @param tileSize The tiles's size property measured in pixels.
+     */
+    void setTileSize(const Size& tileSize) { _tileSize = tileSize; }
+
+    /** Get map orientation. 
+     *
+     * @return The map orientation.
+     */
+    int getMapOrientation() const { return _mapOrientation; }
+    
+    /** Set map orientation. 
+     *
+     * @param mapOrientation The map orientation.
+     */
+    void setMapOrientation(int mapOrientation) { _mapOrientation = mapOrientation; }
+
+    /** Get object groups. 
+     *
+     * @return Object groups.
+     */
+    const Vector<TMXObjectGroup*>& getObjectGroups() const { return _objectGroups; }
+    Vector<TMXObjectGroup*>& getObjectGroups() { return _objectGroups; }
+    
+    /** Set object groups. 
+     *
+     * @param groups An object groups.
+     */
+    void setObjectGroups(const Vector<TMXObjectGroup*>& groups) {
+        _objectGroups = groups;
+    }
+    
+    /** Get properties.
+     *
+     * @return Properties.
+     */
+    const ValueMap& getProperties() const { return _properties; }
+    
+    /** Set properties. 
+     *
+     * @param properties An ValueMap Properties.
+     */
+    void setProperties(const ValueMap& properties) {
+        _properties = properties;
+    }
+
+    virtual std::string getDescription() const override;
+
+protected:
+    /**
+     * @js ctor
+     */
+    FastTMXTiledMap();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~FastTMXTiledMap();
+
+    /** initializes a TMX Tiled Map with a TMX file */
+    bool initWithTMXFile(const std::string& tmxFile);
+
+    /** initializes a TMX Tiled Map with a TMX formatted XML string and a path to TMX resources */
+    bool initWithXML(const std::string& tmxString, const std::string& resourcePath);
+    
+    FastTMXLayer * parseLayer(TMXLayerInfo *layerInfo, TMXMapInfo *mapInfo);
+    TMXTilesetInfo * tilesetForLayer(TMXLayerInfo *layerInfo, TMXMapInfo *mapInfo);
+    void buildWithMapInfo(TMXMapInfo* mapInfo);
+
+    /** the map's size property measured in tiles */
+    Size _mapSize;
+    /** the tiles's size property measured in pixels */
+    Size _tileSize;
+    /** map orientation */
+    int _mapOrientation;
+    /** object groups */
+    Vector<TMXObjectGroup*> _objectGroups;
+    /** properties */
+    ValueMap _properties;
+    
+    //! tile properties
+    ValueMapIntKey _tileProperties;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(FastTMXTiledMap);
+
+};
+
+// end of tilemap_parallax_nodes group
+/** @} */
+    
+
+NS_CC_END

+ 24 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFont.cpp

@@ -0,0 +1,24 @@
+/****************************************************************************
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+

+ 51 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFont.h

@@ -0,0 +1,51 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#ifndef _CCFont_h_
+#define _CCFont_h_
+
+/// @cond DO_NOT_SHOW
+
+#include <string>
+#include "base/ccTypes.h"
+#include "base/ccUTF8.h"
+
+NS_CC_BEGIN
+
+class FontAtlas;
+
+class CC_DLL Font : public Ref
+{
+public:
+    virtual FontAtlas* createFontAtlas() = 0;
+    virtual int* getHorizontalKerningForTextUTF32(const std::u32string& text, int &outNumLetters) const = 0;
+    virtual int getFontMaxHeight() const { return 0; }
+};
+
+NS_CC_END
+
+/// @endcond
+#endif

+ 548 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontAtlas.cpp

@@ -0,0 +1,548 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#include "2d/CCFontAtlas.h"
+#if CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID
+#include <iconv.h>
+#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
+#include "platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h"
+#endif
+#include "2d/CCFontFreeType.h"
+#include "base/ccUTF8.h"
+#include "base/CCDirector.h"
+#include "base/CCEventListenerCustom.h"
+#include "base/CCEventDispatcher.h"
+#include "base/CCEventType.h"
+
+NS_CC_BEGIN
+
+const int FontAtlas::CacheTextureWidth = 512;
+const int FontAtlas::CacheTextureHeight = 512;
+const char* FontAtlas::CMD_PURGE_FONTATLAS = "__cc_PURGE_FONTATLAS";
+const char* FontAtlas::CMD_RESET_FONTATLAS = "__cc_RESET_FONTATLAS";
+
+FontAtlas::FontAtlas(Font &theFont) 
+: _font(&theFont)
+{
+    _font->retain();
+
+    _fontFreeType = dynamic_cast<FontFreeType*>(_font);
+    if (_fontFreeType)
+    {
+        _lineHeight = (float)_font->getFontMaxHeight();
+        _fontAscender = _fontFreeType->getFontAscender();
+        _letterEdgeExtend = 2;
+
+        if (_fontFreeType->isDistanceFieldEnabled())
+        {
+            _letterPadding += 2 * FontFreeType::DistanceMapSpread;    
+        }
+
+        auto outlineSize = _fontFreeType->getOutlineSize();
+        if (outlineSize > 0)
+        {
+            _lineHeight += 2 * outlineSize;
+        }
+
+        
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+        auto eventDispatcher = Director::getInstance()->getEventDispatcher();
+
+        _rendererRecreatedListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, CC_CALLBACK_1(FontAtlas::listenRendererRecreated, this));
+        eventDispatcher->addEventListenerWithFixedPriority(_rendererRecreatedListener, 1);
+#endif
+    }
+}
+
+void FontAtlas::reinit()
+{
+    if (_currentPageData)
+    {
+        delete []_currentPageData;
+        _currentPageData = nullptr;
+    }
+    
+    CC_SAFE_DELETE_ARRAY(_currentPageDataRGBA);
+    
+    auto texture = new (std::nothrow) Texture2D;
+    
+    _currentPageDataSize = CacheTextureWidth * CacheTextureHeight;
+    
+    auto outlineSize = _fontFreeType->getOutlineSize();
+    if(outlineSize > 0)
+    {
+        _currentPageDataSize *= 2;
+        
+        _currentPageDataSizeRGBA = _currentPageDataSize * 2;
+        _currentPageDataRGBA = new (std::nothrow) unsigned char[_currentPageDataSizeRGBA];
+        memset(_currentPageDataRGBA, 0, _currentPageDataSizeRGBA);
+    }
+    
+    _currentPageData = new (std::nothrow) unsigned char[_currentPageDataSize];
+    memset(_currentPageData, 0, _currentPageDataSize);
+    
+    initTextureWithZeros(texture);
+
+    addTexture(texture,0);
+    texture->release();
+}
+
+FontAtlas::~FontAtlas()
+{
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+    if (_fontFreeType && _rendererRecreatedListener)
+    {
+        auto eventDispatcher = Director::getInstance()->getEventDispatcher();
+        eventDispatcher->removeEventListener(_rendererRecreatedListener);
+        _rendererRecreatedListener = nullptr;
+    }
+#endif
+
+    _font->release();
+    releaseTextures();
+
+    delete []_currentPageData;
+
+#if CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID
+    if (_iconv)
+    {
+        iconv_close(_iconv);
+        _iconv = nullptr;
+    }
+#endif
+}
+
+void FontAtlas::initTextureWithZeros(Texture2D *texture)
+{
+    char *zeros = nullptr;    
+    backend::PixelFormat pixelFormat;
+    float outlineSize = _fontFreeType->getOutlineSize();
+    size_t zeroBytes = 0;
+    if (outlineSize > 0)
+    {    
+        //metal do no support AI88 format
+        pixelFormat = backend::PixelFormat::RGBA8888;
+        zeroBytes = CacheTextureWidth * CacheTextureWidth * 4;
+    }
+    else
+    {
+        pixelFormat = backend::PixelFormat::A8;
+        zeroBytes = CacheTextureWidth * CacheTextureWidth;
+    }
+    zeros = new char[zeroBytes]();
+    //std::fill(zeros, zeros + cnt, 0);
+    texture->initWithData(zeros, zeroBytes, pixelFormat, CacheTextureWidth, CacheTextureHeight, Size(CacheTextureWidth, CacheTextureHeight));
+    delete[] zeros;
+}
+
+void FontAtlas::reset()
+{
+    releaseTextures();
+    
+    _currLineHeight = 0;
+    _currentPage = 0;
+    _currentPageOrigX = 0;
+    _currentPageOrigY = 0;
+    _letterDefinitions.clear();
+    
+    reinit();
+}
+
+void FontAtlas::releaseTextures()
+{
+    for( auto &item: _atlasTextures)
+    {
+        item.second->release();
+    }
+    _atlasTextures.clear();
+}
+
+void FontAtlas::purgeTexturesAtlas()
+{
+    if (_fontFreeType)
+    {
+        reset();
+        auto eventDispatcher = Director::getInstance()->getEventDispatcher();
+        eventDispatcher->dispatchCustomEvent(CMD_PURGE_FONTATLAS,this);
+        eventDispatcher->dispatchCustomEvent(CMD_RESET_FONTATLAS,this);
+    }
+}
+
+void FontAtlas::listenRendererRecreated(EventCustom * /*event*/)
+{
+    purgeTexturesAtlas();
+}
+
+void FontAtlas::addLetterDefinition(char32_t utf32Char, const FontLetterDefinition &letterDefinition)
+{
+    _letterDefinitions[utf32Char] = letterDefinition;
+}
+
+void FontAtlas::scaleFontLetterDefinition(float scaleFactor)
+{
+    for (auto&& fontDefinition : _letterDefinitions) {
+        auto& letterDefinition = fontDefinition.second;
+        letterDefinition.width *= scaleFactor;
+        letterDefinition.height *= scaleFactor;
+        letterDefinition.offsetX *= scaleFactor;
+        letterDefinition.offsetY *= scaleFactor;
+        letterDefinition.xAdvance = (int)(letterDefinition.xAdvance * scaleFactor);
+    }
+}
+
+bool FontAtlas::getLetterDefinitionForChar(char32_t utf32Char, FontLetterDefinition &letterDefinition)
+{
+    auto outIterator = _letterDefinitions.find(utf32Char);
+
+    if (outIterator != _letterDefinitions.end())
+    {
+        letterDefinition = (*outIterator).second;
+        return letterDefinition.validDefinition;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+void FontAtlas::conversionU32TOGB2312(const std::u32string& u32Text, std::unordered_map<unsigned int, unsigned int>& charCodeMap)
+{
+    size_t strLen = u32Text.length();
+    auto gb2312StrSize = strLen * 2;
+    auto gb2312Text = new (std::nothrow) char[gb2312StrSize];
+    memset(gb2312Text, 0, gb2312StrSize);
+
+    switch (_fontFreeType->getEncoding())
+    {
+    case FT_ENCODING_GB2312:
+    {
+#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
+        std::u16string u16Text;
+        cocos2d::StringUtils::UTF32ToUTF16(u32Text, u16Text);
+        WideCharToMultiByte(936, NULL, (LPCWCH)u16Text.c_str(), strLen, (LPSTR)gb2312Text, gb2312StrSize, NULL, NULL);
+#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
+        conversionEncodingJNI((char*)u32Text.c_str(), gb2312StrSize, "UTF-32LE", gb2312Text, "GB2312");
+#else
+        if (_iconv == nullptr)
+        {
+            _iconv = iconv_open("GBK//TRANSLIT", "UTF-32LE");
+        }
+
+        if (_iconv == (iconv_t)-1)
+        {
+            CCLOG("conversion from utf32 to gb2312 not available");
+        }
+        else
+        {
+            char* pin = (char*)u32Text.c_str();
+            char* pout = gb2312Text;
+            size_t inLen = strLen * 2;
+            size_t outLen = gb2312StrSize;
+
+            iconv(_iconv, (char**)&pin, &inLen, &pout, &outLen);
+        }
+#endif
+    }
+    break;
+    default:
+        CCLOG("Unsupported encoding:%d", _fontFreeType->getEncoding());
+        break;
+    }
+
+    unsigned short gb2312Code = 0;
+    unsigned char* dst = (unsigned char*)&gb2312Code;
+    char32_t u32Code;
+    for (size_t index = 0, gbIndex = 0; index < strLen; ++index)
+    {
+        u32Code = u32Text[index];
+        if (u32Code < 256)
+        {
+            charCodeMap[u32Code] = u32Code;
+            gbIndex += 1;
+        }
+        else
+        {
+            dst[0] = gb2312Text[gbIndex + 1];
+            dst[1] = gb2312Text[gbIndex];
+            charCodeMap[u32Code] = gb2312Code;
+
+            gbIndex += 2;
+        }
+    }
+
+    delete[] gb2312Text;
+}
+
+void FontAtlas::findNewCharacters(const std::u32string& u32Text, std::unordered_map<unsigned int, unsigned int>& charCodeMap)
+{
+    std::u32string newChars;
+    FT_Encoding charEncoding = _fontFreeType->getEncoding();
+
+    //find new characters
+    if (_letterDefinitions.empty())
+    {
+        // fixed #16169: new android project crash in android 5.0.2 device (Nexus 7) when use 3.12.
+        // While using clang compiler with gnustl_static on android, the copy assignment operator of `std::u32string`
+        // will affect the memory validity, it means after `newChars` is destroyed, the memory of `u32Text` holds
+        // will be a dead region. `u32text` represents the variable in `Label::_utf32Text`, when somewhere
+        // allocates memory by `malloc, realloc, new, new[]`, the generated memory address may be the same
+        // as `Label::_utf32Text` holds. If doing a `memset` or other memory operations, the orignal `Label::_utf32Text`
+        // will be in an unknown state. Meanwhile, a bunch lots of logic which depends on `Label::_utf32Text`
+        // will be broken.
+        
+        // newChars = u32Text;
+        
+        // Using `append` method is a workaround for this issue. So please be carefuly while using the assignment operator
+        // of `std::u32string`.
+        newChars.append(u32Text);
+    }
+    else
+    {
+        auto length = u32Text.length();
+        newChars.reserve(length);
+        for (size_t i = 0; i < length; ++i)
+        {
+            auto outIterator = _letterDefinitions.find(u32Text[i]);
+            if (outIterator == _letterDefinitions.end())
+            {
+                newChars.push_back(u32Text[i]);
+            }
+        }
+    }
+
+    if (!newChars.empty())
+    {
+        switch (charEncoding)
+        {
+        case FT_ENCODING_UNICODE:
+        {
+            for (auto u32Code : newChars)
+            {
+                charCodeMap[u32Code] = u32Code;
+            }
+            break;
+        }
+        case FT_ENCODING_GB2312:
+        {
+            conversionU32TOGB2312(newChars, charCodeMap);
+            break;
+        }
+        default:
+            CCLOG("FontAtlas::findNewCharacters: Unsupported encoding:%d", charEncoding);
+            break;
+        }
+    }
+}
+
+bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
+{
+    if (_fontFreeType == nullptr)
+    {
+        return false;
+    }
+
+    if (!_currentPageData)
+        reinit(); 
+    
+    std::unordered_map<unsigned int, unsigned int> codeMapOfNewChar;
+    findNewCharacters(utf32Text, codeMapOfNewChar);
+    if (codeMapOfNewChar.empty())
+    {
+        return false;
+    }
+
+    int adjustForDistanceMap = _letterPadding / 2;
+    int adjustForExtend = _letterEdgeExtend / 2;
+    long bitmapWidth;
+    long bitmapHeight;
+    int glyphHeight;
+    Rect tempRect;
+    FontLetterDefinition tempDef;
+
+    auto scaleFactor = CC_CONTENT_SCALE_FACTOR();
+    auto  pixelFormat = _fontFreeType->getOutlineSize() > 0 ? backend::PixelFormat::AI88 : backend::PixelFormat::A8;
+
+    int startY = (int)_currentPageOrigY;
+
+    for (auto&& it : codeMapOfNewChar)
+    {
+        auto bitmap = _fontFreeType->getGlyphBitmap(it.second, bitmapWidth, bitmapHeight, tempRect, tempDef.xAdvance);
+        if (bitmap && bitmapWidth > 0 && bitmapHeight > 0)
+        {
+            tempDef.validDefinition = true;
+            tempDef.width = tempRect.size.width + _letterPadding + _letterEdgeExtend;
+            tempDef.height = tempRect.size.height + _letterPadding + _letterEdgeExtend;
+            tempDef.offsetX = tempRect.origin.x - adjustForDistanceMap - adjustForExtend;
+            tempDef.offsetY = _fontAscender + tempRect.origin.y - adjustForDistanceMap - adjustForExtend;
+
+            if (_currentPageOrigX + tempDef.width > CacheTextureWidth)
+            {
+                _currentPageOrigY += _currLineHeight;
+                _currLineHeight = 0;
+                _currentPageOrigX = 0;
+                if (_currentPageOrigY + _lineHeight + _letterPadding + _letterEdgeExtend >= CacheTextureHeight)
+                {
+                    updateTextureContent(pixelFormat, startY);
+
+                    startY = 0;
+
+                    _currentPageOrigY = 0;
+                    memset(_currentPageData, 0, _currentPageDataSize);
+                    _currentPage++;
+                    auto tex = new (std::nothrow) Texture2D;
+                    
+                    initTextureWithZeros(tex);
+
+                    if (_antialiasEnabled)
+                    {
+                        tex->setAntiAliasTexParameters();
+                    }
+                    else
+                    {
+                        tex->setAliasTexParameters();
+                    }
+                    addTexture(tex, _currentPage);
+                    
+                    tex->release();
+                }
+            }
+            glyphHeight = static_cast<int>(bitmapHeight) + _letterPadding + _letterEdgeExtend;
+            if (glyphHeight > _currLineHeight)
+            {
+                _currLineHeight = glyphHeight;
+            }
+            _fontFreeType->renderCharAt(_currentPageData, (int)_currentPageOrigX + adjustForExtend, (int)_currentPageOrigY + adjustForExtend, bitmap, bitmapWidth, bitmapHeight);
+
+            tempDef.U = _currentPageOrigX;
+            tempDef.V = _currentPageOrigY;
+            tempDef.textureID = _currentPage;
+            _currentPageOrigX += tempDef.width + 1;
+            // take from pixels to points
+            tempDef.width = tempDef.width / scaleFactor;
+            tempDef.height = tempDef.height / scaleFactor;
+            tempDef.U = tempDef.U / scaleFactor;
+            tempDef.V = tempDef.V / scaleFactor;
+        }
+        else{
+            if(bitmap)
+                delete[] bitmap;
+            if (tempDef.xAdvance)
+                tempDef.validDefinition = true;
+            else
+                tempDef.validDefinition = false;
+
+            tempDef.width = 0;
+            tempDef.height = 0;
+            tempDef.U = 0;
+            tempDef.V = 0;
+            tempDef.offsetX = 0;
+            tempDef.offsetY = 0;
+            tempDef.textureID = 0;
+            _currentPageOrigX += 1;
+        }
+
+        _letterDefinitions[it.first] = tempDef;
+    }
+
+    updateTextureContent(pixelFormat, startY);
+    return true;
+}
+
+void FontAtlas::updateTextureContent(backend::PixelFormat format, int startY)
+{
+    unsigned char *data = nullptr;
+    auto outlineSize = _fontFreeType->getOutlineSize();
+    if (outlineSize > 0 && format == backend::PixelFormat::AI88)
+    {
+        int nLen = CacheTextureWidth * ((int)_currentPageOrigY - startY + _currLineHeight);
+        data = _currentPageData + CacheTextureWidth * (int)startY * 2;
+        memset(_currentPageDataRGBA, 0, 4 * nLen);
+        for (auto i = 0; i < nLen; i++)
+        {
+            _currentPageDataRGBA[i*4] = data[i*2];
+            _currentPageDataRGBA[i*4+3] = data[i*2+1];
+        }
+        _atlasTextures[_currentPage]->updateWithData(_currentPageDataRGBA, 0, startY, CacheTextureWidth, (int)_currentPageOrigY - startY + _currLineHeight);
+    }
+    else
+    {
+        data = _currentPageData + CacheTextureWidth * (int)startY;
+       _atlasTextures[_currentPage]->updateWithData(data, 0, startY, CacheTextureWidth, (int)_currentPageOrigY - startY + _currLineHeight);
+    }
+}
+
+void FontAtlas::addTexture(Texture2D *texture, int slot)
+{
+    texture->retain();
+    _atlasTextures[slot] = texture;
+}
+
+Texture2D* FontAtlas::getTexture(int slot)
+{
+    return _atlasTextures[slot];
+}
+
+void FontAtlas::setLineHeight(float newHeight)
+{
+    _lineHeight = newHeight;
+}
+
+std::string FontAtlas::getFontName() const
+{
+    std::string fontName = _fontFreeType ? _fontFreeType->getFontName() : "";
+    if(fontName.empty()) return fontName;
+    auto idx = fontName.rfind('/');
+    if (idx != std::string::npos) { return fontName.substr(idx + 1); }
+    idx = fontName.rfind('\\');
+    if (idx != std::string::npos) { return fontName.substr(idx + 1); }
+    return fontName;
+}
+
+void FontAtlas::setAliasTexParameters()
+{
+    if (_antialiasEnabled)
+    {
+        _antialiasEnabled = false;
+        for (const auto & tex : _atlasTextures)
+        {
+            tex.second->setAliasTexParameters();
+        }
+    }
+}
+
+void FontAtlas::setAntiAliasTexParameters()
+{
+    if (! _antialiasEnabled)
+    {
+        _antialiasEnabled = true;
+        for (const auto & tex : _atlasTextures)
+        {
+            tex.second->setAntiAliasTexParameters();
+        }
+    }
+}
+
+NS_CC_END

+ 165 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontAtlas.h

@@ -0,0 +1,165 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#ifndef _CCFontAtlas_h_
+#define _CCFontAtlas_h_
+
+/// @cond DO_NOT_SHOW
+
+#include <string>
+#include <unordered_map>
+
+#include "platform/CCPlatformMacros.h"
+#include "base/CCRef.h"
+#include "platform/CCStdC.h" // ssize_t on windows
+#include "renderer/CCTexture2D.h"
+
+NS_CC_BEGIN
+
+class Font;
+class EventCustom;
+class EventListenerCustom;
+class FontFreeType;
+
+struct FontLetterDefinition
+{
+    float U;
+    float V;
+    float width;
+    float height;
+    float offsetX;
+    float offsetY;
+    int textureID;
+    bool validDefinition;
+    int xAdvance;
+};
+
+class CC_DLL FontAtlas : public Ref
+{
+public:
+    static const int CacheTextureWidth;
+    static const int CacheTextureHeight;
+    static const char* CMD_PURGE_FONTATLAS;
+    static const char* CMD_RESET_FONTATLAS;
+    /**
+     * @js ctor
+     */
+    FontAtlas(Font &theFont);
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~FontAtlas();
+    
+    void addLetterDefinition(char32_t utf32Char, const FontLetterDefinition &letterDefinition);
+    bool getLetterDefinitionForChar(char32_t utf32Char, FontLetterDefinition &letterDefinition);
+    
+    bool prepareLetterDefinitions(const std::u32string& utf16String);
+
+    const std::unordered_map<ssize_t, Texture2D*>& getTextures() const { return _atlasTextures; }
+    void  addTexture(Texture2D *texture, int slot);
+    float getLineHeight() const { return _lineHeight; }
+    void  setLineHeight(float newHeight);
+    
+    std::string getFontName() const;
+
+    Texture2D* getTexture(int slot);
+    const Font* getFont() const { return _font; }
+
+    /** listen the event that renderer was recreated on Android/WP8
+     It only has effect on Android and WP8.
+     */
+    void listenRendererRecreated(EventCustom *event);
+    
+    /** Removes textures atlas.
+     It will purge the textures atlas and if multiple texture exist in the FontAtlas.
+     */
+    void purgeTexturesAtlas();
+
+    /** sets font texture parameters:
+     - GL_TEXTURE_MIN_FILTER = GL_LINEAR
+     - GL_TEXTURE_MAG_FILTER = GL_LINEAR
+     */
+     void setAntiAliasTexParameters();
+
+     /** sets font texture parameters:
+     - GL_TEXTURE_MIN_FILTER = GL_NEAREST
+     - GL_TEXTURE_MAG_FILTER = GL_NEAREST
+     */
+     void setAliasTexParameters();
+
+protected:
+    void reset();
+    
+    void reinit();
+    
+    void releaseTextures();
+
+    void findNewCharacters(const std::u32string& u32Text, std::unordered_map<unsigned int, unsigned int>& charCodeMap);
+
+    void conversionU32TOGB2312(const std::u32string& u32Text, std::unordered_map<unsigned int, unsigned int>& charCodeMap);
+
+    void initTextureWithZeros(Texture2D *texture);
+
+    /**
+     * Scale each font letter by scaleFactor.
+     *
+     * @param scaleFactor A float scale factor for scaling font letter info.
+     */
+    void scaleFontLetterDefinition(float scaleFactor);
+    
+    void updateTextureContent(backend::PixelFormat format, int startY);
+
+    std::unordered_map<ssize_t, Texture2D*> _atlasTextures;
+    std::unordered_map<char32_t, FontLetterDefinition> _letterDefinitions;
+    float _lineHeight = 0.f;
+    Font* _font = nullptr;
+    FontFreeType* _fontFreeType = nullptr;
+    void* _iconv = nullptr;
+
+    // Dynamic GlyphCollection related stuff
+    int _currentPage = 0;
+    unsigned char *_currentPageData = nullptr;
+    unsigned char *_currentPageDataRGBA = nullptr;
+    int _currentPageDataSize = 0;
+    int _currentPageDataSizeRGBA = 0;
+    float _currentPageOrigX = 0;
+    float _currentPageOrigY = 0;
+    int _letterPadding = 0;
+    int _letterEdgeExtend = 0;
+
+    int _fontAscender = 0;
+    EventListenerCustom* _rendererRecreatedListener = nullptr;
+    bool _antialiasEnabled = true;
+    int _currLineHeight = 0;
+
+    friend class Label;
+};
+
+NS_CC_END
+
+/// @endcond
+#endif /* defined(__cocos2d_libs__CCFontAtlas__) */

+ 264 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontAtlasCache.cpp

@@ -0,0 +1,264 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+#include "2d/CCFontAtlasCache.h"
+
+#include "base/CCDirector.h"
+#include "2d/CCFontFNT.h"
+#include "2d/CCFontFreeType.h"
+#include "2d/CCFontAtlas.h"
+#include "2d/CCFontCharMap.h"
+#include "2d/CCLabel.h"
+#include "platform/CCFileUtils.h"
+
+NS_CC_BEGIN
+
+std::unordered_map<std::string, FontAtlas *> FontAtlasCache::_atlasMap;
+#define ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE 255
+
+void FontAtlasCache::purgeCachedData()
+{
+    auto atlasMapCopy = _atlasMap;
+    for (auto&& atlas : atlasMapCopy)
+    {
+        auto refCount = atlas.second->getReferenceCount();
+        atlas.second->release();
+        if (refCount != 1)
+            atlas.second->purgeTexturesAtlas();
+    }
+    _atlasMap.clear();
+}
+
+FontAtlas* FontAtlasCache::getFontAtlasTTF(const _ttfConfig* config)
+{
+    auto realFontFilename = FileUtils::getInstance()->getNewFilename(config->fontFilePath);  // resolves real file path, to prevent storing multiple atlases for the same file.
+    bool useDistanceField = config->distanceFieldEnabled;
+    if(config->outlineSize > 0)
+    {
+        useDistanceField = false;
+    }
+
+    std::string key;
+    char keyPrefix[ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE];
+    snprintf(keyPrefix, ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE, useDistanceField ? "df %.2f %d " : "%.2f %d ", config->fontSize, config->outlineSize);
+    std::string atlasName(keyPrefix);
+    atlasName += realFontFilename;
+
+    auto it = _atlasMap.find(atlasName);
+
+    if ( it == _atlasMap.end() )
+    {
+        auto font = FontFreeType::create(realFontFilename, config->fontSize, config->glyphs,
+            config->customGlyphs, useDistanceField, (float)config->outlineSize);
+        if (font)
+        {
+            auto tempAtlas = font->createFontAtlas();
+            if (tempAtlas)
+            {
+                _atlasMap[atlasName] = tempAtlas;
+                return _atlasMap[atlasName];
+            }
+        }
+    }
+    else
+        return it->second;
+
+    return nullptr;
+}
+
+FontAtlas* FontAtlasCache::getFontAtlasFNT(const std::string& fontFileName, const Vec2& imageOffset /* = Vec2::ZERO */)
+{
+    auto realFontFilename = FileUtils::getInstance()->getNewFilename(fontFileName);  // resolves real file path, to prevent storing multiple atlases for the same file.
+    char keyPrefix[ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE];
+    snprintf(keyPrefix, ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE, "%.2f %.2f ", imageOffset.x, imageOffset.y);
+    std::string atlasName(keyPrefix);
+    atlasName += realFontFilename;
+    
+    auto it = _atlasMap.find(atlasName);
+    if ( it == _atlasMap.end() )
+    {
+        auto font = FontFNT::create(realFontFilename, imageOffset);
+
+        if(font)
+        {
+            auto tempAtlas = font->createFontAtlas();
+            if (tempAtlas)
+            {
+                _atlasMap[atlasName] = tempAtlas;
+                return _atlasMap[atlasName];
+            }
+        }
+    }
+    else
+        return it->second;
+    
+    return nullptr;
+}
+
+FontAtlas* FontAtlasCache::getFontAtlasCharMap(const std::string& plistFile)
+{
+    std::string atlasName = plistFile;
+    
+    auto it = _atlasMap.find(atlasName);
+    if ( it == _atlasMap.end() )
+    {
+        auto font = FontCharMap::create(plistFile);
+
+        if(font)
+        {
+            auto tempAtlas = font->createFontAtlas();
+            if (tempAtlas)
+            {
+                _atlasMap[atlasName] = tempAtlas;
+                return _atlasMap[atlasName];
+            }
+        }
+    }
+    else
+        return it->second;
+
+    return nullptr;
+}
+
+FontAtlas* FontAtlasCache::getFontAtlasCharMap(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap)
+{
+    char key[ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE];
+    sprintf(key,"name:%p_%d_%d_%d",texture->getBackendTexture(),itemWidth,itemHeight,startCharMap);
+    std::string atlasName = key;
+
+    auto it = _atlasMap.find(atlasName);
+    if ( it == _atlasMap.end() )
+    {
+        auto font = FontCharMap::create(texture,itemWidth,itemHeight,startCharMap);
+
+        if(font)
+        {
+            auto tempAtlas = font->createFontAtlas();
+            if (tempAtlas)
+            {
+                _atlasMap[atlasName] = tempAtlas;
+                return _atlasMap[atlasName];
+            }
+        }
+    }
+    else
+        return it->second;
+
+    return nullptr;
+}
+
+FontAtlas* FontAtlasCache::getFontAtlasCharMap(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap)
+{
+    char keyPrefix[ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE];
+    snprintf(keyPrefix, ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE, "%d %d %d ", itemWidth, itemHeight, startCharMap);
+    std::string atlasName(keyPrefix);
+    atlasName += charMapFile;
+
+    auto it = _atlasMap.find(atlasName);
+    if ( it == _atlasMap.end() )
+    {
+        auto font = FontCharMap::create(charMapFile,itemWidth,itemHeight,startCharMap);
+
+        if(font)
+        {
+            auto tempAtlas = font->createFontAtlas();
+            if (tempAtlas)
+            {
+                _atlasMap[atlasName] = tempAtlas;
+                return _atlasMap[atlasName];
+            }
+        }
+    }
+    else
+        return it->second;
+
+    return nullptr;
+}
+
+bool FontAtlasCache::releaseFontAtlas(FontAtlas *atlas)
+{
+    if (nullptr != atlas)
+    {
+        for( auto &item: _atlasMap )
+        {
+            if ( item.second == atlas )
+            {
+                if (atlas->getReferenceCount() == 1)
+                {
+                  _atlasMap.erase(item.first);
+                }
+                
+                atlas->release();
+                
+                return true;
+            }
+        }
+    }
+    
+    return false;
+}
+
+void FontAtlasCache::reloadFontAtlasFNT(const std::string& fontFileName, const Vec2& imageOffset/* = Vec2::ZERO*/)
+{
+    char keyPrefix[ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE];
+    snprintf(keyPrefix, ATLAS_MAP_KEY_PREFIX_BUFFER_SIZE, "%.2f %.2f ", imageOffset.x, imageOffset.y);
+    std::string atlasName(keyPrefix);
+    atlasName += fontFileName;
+    
+    auto it = _atlasMap.find(atlasName);
+    if (it != _atlasMap.end())
+    {
+        CC_SAFE_RELEASE_NULL(it->second);
+        _atlasMap.erase(it);
+    }
+    FontFNT::reloadBMFontResource(fontFileName);
+    auto font = FontFNT::create(fontFileName, imageOffset);
+    if (font)
+    {
+        auto tempAtlas = font->createFontAtlas();
+        if (tempAtlas)
+        {
+            _atlasMap[atlasName] = tempAtlas;
+        }
+    }
+
+}
+
+void FontAtlasCache::unloadFontAtlasTTF(const std::string& fontFileName)
+{
+    auto item = _atlasMap.begin();
+    while (item != _atlasMap.end())
+    {
+        if (item->first.find(fontFileName) != std::string::npos)
+        {
+            CC_SAFE_RELEASE_NULL(item->second);
+            item = _atlasMap.erase(item);
+        }
+        else
+            item++;
+    }
+}
+
+NS_CC_END

+ 77 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontAtlasCache.h

@@ -0,0 +1,77 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#ifndef _CCFontAtlasCache_h_
+#define _CCFontAtlasCache_h_
+
+/// @cond DO_NOT_SHOW
+
+#include <unordered_map>
+#include "base/ccTypes.h"
+
+NS_CC_BEGIN
+
+class FontAtlas;
+class Texture2D;
+struct _ttfConfig;
+
+class CC_DLL FontAtlasCache
+{  
+public:
+    static FontAtlas* getFontAtlasTTF(const _ttfConfig* config);
+    static FontAtlas* getFontAtlasFNT(const std::string& fontFileName, const Vec2& imageOffset = Vec2::ZERO);
+
+    static FontAtlas* getFontAtlasCharMap(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap);
+    static FontAtlas* getFontAtlasCharMap(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap);
+    static FontAtlas* getFontAtlasCharMap(const std::string& plistFile);
+    
+    static bool releaseFontAtlas(FontAtlas *atlas);
+
+    /** Removes cached data.
+     It will purge the textures atlas and if multiple texture exist in one FontAtlas.
+     */
+    static void purgeCachedData();
+
+    /** Release current FNT texture and reload it.
+     CAUTION : All component use this font texture should be reset font name, though the file name is same!
+               otherwise, it will cause program crash!
+    */
+    static void reloadFontAtlasFNT(const std::string& fontFileName, const Vec2& imageOffset = Vec2::ZERO);
+
+    /** Unload all texture atlas texture create by special file name.
+     CAUTION : All component use this font texture should be reset font name, though the file name is same!
+               otherwise, it will cause program crash!
+    */
+    static void unloadFontAtlasTTF(const std::string& fontFileName);
+
+private:
+    static std::unordered_map<std::string, FontAtlas *> _atlasMap;
+};
+
+NS_CC_END
+
+/// @endcond
+#endif

+ 148 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontCharMap.cpp

@@ -0,0 +1,148 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#include "2d/CCFontCharMap.h"
+#include "2d/CCFontAtlas.h"
+#include "platform/CCFileUtils.h"
+#include "base/CCDirector.h"
+#include "renderer/CCTextureCache.h"
+
+NS_CC_BEGIN
+
+FontCharMap * FontCharMap::create(const std::string& plistFile)
+{
+    std::string pathStr = FileUtils::getInstance()->fullPathForFilename(plistFile);
+    std::string relPathStr = pathStr.substr(0, pathStr.find_last_of('/'))+"/";
+
+    ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(pathStr);
+
+    CCASSERT(dict["version"].asInt() == 1, "Unsupported version. Upgrade cocos2d version");
+
+    std::string textureFilename = relPathStr + dict["textureFilename"].asString();
+
+    unsigned int width = dict["itemWidth"].asInt();
+    unsigned int height = dict["itemHeight"].asInt();
+    unsigned int startChar = dict["firstChar"].asInt();
+
+    Texture2D *tempTexture = Director::getInstance()->getTextureCache()->addImage(textureFilename);
+    if (!tempTexture)
+    {
+        return nullptr;
+    }
+    
+    FontCharMap *tempFont =  new FontCharMap(tempTexture,width,height,startChar);
+    
+    if (!tempFont)
+    {
+        return nullptr;
+    }
+    tempFont->autorelease();
+    return tempFont;
+}
+
+FontCharMap* FontCharMap::create(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap)
+{
+    Texture2D *tempTexture = Director::getInstance()->getTextureCache()->addImage(charMapFile);
+
+    if (!tempTexture)
+    {
+        return nullptr;
+    }
+
+    FontCharMap *tempFont =  new FontCharMap(tempTexture,itemWidth,itemHeight,startCharMap);
+
+    if (!tempFont)
+    {
+        return nullptr;
+    }
+    tempFont->autorelease();
+    return tempFont;
+}
+
+FontCharMap* FontCharMap::create(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap)
+{
+    FontCharMap *tempFont =  new FontCharMap(texture,itemWidth,itemHeight,startCharMap);
+
+    if (!tempFont)
+    {
+        return nullptr;
+    }
+    tempFont->autorelease();
+    return tempFont;
+}
+
+FontCharMap::~FontCharMap()
+{
+
+}
+
+int* FontCharMap::getHorizontalKerningForTextUTF32(const std::u32string& /*text*/, int & /*outNumLetters*/) const
+{
+    return nullptr;
+}
+
+FontAtlas * FontCharMap::createFontAtlas()
+{
+    FontAtlas *tempAtlas = new (std::nothrow) FontAtlas(*this);
+    if (!tempAtlas)
+        return nullptr;
+    
+    Size s = _texture->getContentSizeInPixels();
+    int itemsPerColumn = (int)(s.height / _itemHeight);
+    int itemsPerRow = (int)(s.width / _itemWidth);
+
+    tempAtlas->setLineHeight((float)_itemHeight);
+
+    auto contentScaleFactor = CC_CONTENT_SCALE_FACTOR();
+
+    FontLetterDefinition tempDefinition;
+    tempDefinition.textureID = 0;
+    tempDefinition.offsetX  = 0.0f;
+    tempDefinition.offsetY  = 0.0f;
+    tempDefinition.validDefinition = true;
+    tempDefinition.width = _itemWidth / contentScaleFactor;
+    tempDefinition.height = _itemHeight / contentScaleFactor;
+    tempDefinition.xAdvance = _itemWidth;
+
+    int charId = _mapStartChar;
+    for (int row = 0; row < itemsPerColumn; ++row)
+    {
+        for (int col = 0; col < itemsPerRow; ++col)
+        {
+            tempDefinition.U = _itemWidth * col / contentScaleFactor;
+            tempDefinition.V = _itemHeight * row / contentScaleFactor;
+
+            tempAtlas->addLetterDefinition(charId, tempDefinition);
+            charId++;
+        }
+    }
+    
+    tempAtlas->addTexture(_texture,0);
+
+    return tempAtlas;
+}
+
+NS_CC_END

+ 72 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontCharMap.h

@@ -0,0 +1,72 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#ifndef _CCFontCharMap_h_
+#define _CCFontCharMap_h_
+
+/// @cond DO_NOT_SHOW
+
+#include "2d/CCFont.h"
+
+NS_CC_BEGIN
+
+class Texture2D;
+class FontCharMap : public Font
+{  
+public:
+    static FontCharMap * create(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap);
+    static FontCharMap * create(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap);
+    static FontCharMap * create(const std::string& plistFile);
+    
+    virtual int* getHorizontalKerningForTextUTF32(const std::u32string& text, int &outNumLetters) const override;
+    virtual FontAtlas *createFontAtlas() override;
+    
+protected:    
+    FontCharMap(Texture2D* texture,int itemWidth, int itemHeight, int startCharMap)
+        :_texture(texture)
+        ,_mapStartChar(startCharMap)
+        ,_itemWidth(itemWidth)
+        ,_itemHeight(itemHeight)
+    {}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~FontCharMap();
+    
+private:
+    Texture2D* _texture;
+    int _mapStartChar;
+    int _itemWidth;
+    int _itemHeight;
+
+};
+
+/// @endcond
+
+NS_CC_END
+
+#endif /* defined(_CCFontCharMap_h_) */

+ 795 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontFNT.cpp

@@ -0,0 +1,795 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#include "2d/CCFontFNT.h"
+#include "2d/CCFontAtlas.h"
+#include "platform/CCFileUtils.h"
+#include "base/CCConfiguration.h"
+#include "base/CCDirector.h"
+#include "base/CCMap.h"
+#include "base/ccUTF8.h"
+#include "renderer/CCTextureCache.h"
+
+#include <cmath>
+#include <set>
+#include <unordered_map>
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup GUI
+ * @{
+ * @addtogroup label
+ * @{
+ */
+
+enum {
+    kLabelAutomaticWidth = -1,
+};
+
+struct _FontDefHashElement;
+
+/**
+@struct BMFontDef
+BMFont definition
+*/
+typedef struct _BMFontDef {
+    //! ID of the character
+    unsigned int charID;
+    //! origin and size of the font
+    Rect rect;
+    //! The X amount the image should be offset when drawing the image (in pixels)
+    short xOffset;
+    //! The Y amount the image should be offset when drawing the image (in pixels)
+    short yOffset;
+    //! The amount to move the current position after drawing the character (in pixels)
+    short xAdvance;
+} BMFontDef;
+
+/** @struct BMFontPadding
+BMFont padding
+@since v0.8.2
+*/
+typedef struct _BMFontPadding {
+    /// padding left
+    int left;
+    /// padding top
+    int top;
+    /// padding right
+    int right;
+    /// padding bottom
+    int bottom;
+} BMFontPadding;
+
+/** @brief BMFontConfiguration has parsed configuration of the .fnt file
+@since v0.8
+*/
+class CC_DLL BMFontConfiguration : public Ref
+{
+    // FIXME: Creating a public interface so that the bitmapFontArray[] is accessible
+public://@public
+    // BMFont definitions
+    std::unordered_map<int /* key */, BMFontDef /* fontDef */> _fontDefDictionary;
+
+    //! FNTConfig: Common Height Should be signed (issue #1343)
+    int _commonHeight;
+    //! Padding
+    BMFontPadding    _padding;
+    //! atlas name
+    std::string _atlasName;
+    //! values for kerning
+    std::unordered_map<uint64_t /* key */, int /* amount */> _kerningDictionary;
+    
+    // Character Set defines the letters that actually exist in the font
+    std::set<unsigned int> *_characterSet;
+    //! Font Size
+    int _fontSize;
+public:
+    /**
+     * @js ctor
+     */
+    BMFontConfiguration();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~BMFontConfiguration();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    std::string description() const;
+
+    /** allocates a BMFontConfiguration with a FNT file */
+    static BMFontConfiguration * create(const std::string& FNTfile);
+
+    /** initializes a BitmapFontConfiguration with a FNT file */
+    bool initWithFNTfile(const std::string& FNTfile);
+    
+    const std::string& getAtlasName() { return _atlasName; }
+    void setAtlasName(const std::string& atlasName) { _atlasName = atlasName; }
+    
+    std::set<unsigned int>* getCharacterSet() const;
+private:
+    std::set<unsigned int>* parseConfigFile(const std::string& controlFile);
+    std::set<unsigned int>* parseBinaryConfigFile(unsigned char* pData, unsigned long size, const std::string& controlFile);
+    unsigned int parseCharacterDefinition(const char* line);
+    void parseInfoArguments(const char* line);
+    void parseCommonArguments(const char* line);
+    void parseImageFileName(const char* line, const std::string& fntFile);
+    void parseKerningEntry(const char* line);
+    void purgeKerningDictionary();
+    void purgeFontDefDictionary();
+};
+
+//
+//FNTConfig Cache - free functions
+//
+static Map<std::string, BMFontConfiguration*>* s_configurations = nullptr;
+
+BMFontConfiguration* FNTConfigLoadFile(const std::string& fntFile)
+{
+    BMFontConfiguration* ret = nullptr;
+
+    if (s_configurations == nullptr)
+    {
+        s_configurations = new (std::nothrow) Map<std::string, BMFontConfiguration*>();
+    }
+
+    ret = s_configurations->at(fntFile);
+    if( ret == nullptr )
+    {
+        ret = BMFontConfiguration::create(fntFile);
+        if (ret)
+        {
+            s_configurations->insert(fntFile, ret);
+        }        
+    }
+
+    return ret;
+}
+
+//
+//BitmapFontConfiguration
+//
+
+BMFontConfiguration * BMFontConfiguration::create(const std::string& FNTfile)
+{
+    BMFontConfiguration * ret = new (std::nothrow) BMFontConfiguration();
+    if (ret->initWithFNTfile(FNTfile))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool BMFontConfiguration::initWithFNTfile(const std::string& FNTfile)
+{
+    _characterSet = this->parseConfigFile(FNTfile);
+
+    if (! _characterSet)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+std::set<unsigned int>* BMFontConfiguration::getCharacterSet() const
+{
+    return _characterSet;
+}
+
+BMFontConfiguration::BMFontConfiguration()
+: _commonHeight(0)
+, _characterSet(nullptr)
+, _fontSize(0)
+{
+
+}
+
+BMFontConfiguration::~BMFontConfiguration()
+{
+    CCLOGINFO( "deallocing BMFontConfiguration: %p", this );
+    this->purgeFontDefDictionary();
+    this->purgeKerningDictionary();
+    _atlasName.clear();
+    CC_SAFE_DELETE(_characterSet);
+}
+
+std::string BMFontConfiguration::description() const
+{
+    return StringUtils::format(
+        "<BMFontConfiguration = " CC_FORMAT_PRINTF_SIZE_T " | Glphys:%d Kernings:%d | Image = %s>",
+        (size_t)this,
+        static_cast<int>(_fontDefDictionary.size()),
+        static_cast<int>(_kerningDictionary.size()),
+        _atlasName.c_str()
+    );
+}
+
+void BMFontConfiguration::purgeKerningDictionary()
+{
+    _kerningDictionary.clear();
+}
+
+void BMFontConfiguration::purgeFontDefDictionary()
+{
+    _fontDefDictionary.clear();
+}
+
+std::set<unsigned int>* BMFontConfiguration::parseConfigFile(const std::string& controlFile)
+{
+    std::string data = FileUtils::getInstance()->getStringFromFile(controlFile);
+    if (data.empty())
+    {
+        return nullptr;
+    }
+    if (data.size() >= (sizeof("BMP") - 1) && memcmp("BMF", data.c_str(), sizeof("BMP") - 1) == 0) {
+        // Handle fnt file of binary format
+        std::set<unsigned int>* ret = parseBinaryConfigFile((unsigned char*)&data.front(), data.size(), controlFile);
+        return ret;
+    }
+    if (data[0] == 0)
+    {
+        CCLOG("cocos2d: Error parsing FNTfile %s", controlFile.c_str());
+        return nullptr;
+    }
+    auto contents = data.c_str();
+    
+    std::set<unsigned int> *validCharsString = new (std::nothrow) std::set<unsigned int>();
+    
+    auto contentsLen = strlen(contents);
+    char line[512] = {0};
+    
+    auto next = strchr(contents, '\n');
+    auto base = contents;
+    size_t lineLength = 0;
+    size_t parseCount = 0;
+    while (next)
+    {
+        lineLength = ((int)(next - base));
+        memcpy(line, contents + parseCount, lineLength);
+        line[lineLength] = 0;
+
+        parseCount += lineLength + 1;
+        if (parseCount < contentsLen)
+        {
+            base = next + 1;
+            next = strchr(base, '\n');
+        } 
+        else
+        {
+            next = nullptr;
+        }
+
+        if (memcmp(line, "info face", 9) == 0)
+        {
+            // FIXME: info parsing is incomplete
+            // Not needed for the Hiero editors, but needed for the AngelCode editor
+            //            [self parseInfoArguments:line];
+            this->parseInfoArguments(line);
+        }
+        // Check to see if the start of the line is something we are interested in
+        else if (memcmp(line, "common lineHeight", 17) == 0)
+        {
+            this->parseCommonArguments(line);
+        }
+        else if (memcmp(line, "page id", 7) == 0)
+        {
+            this->parseImageFileName(line, controlFile);
+        }
+        else if (memcmp(line, "chars c", 7) == 0)
+        {
+            // Ignore this line
+        }
+        else if (memcmp(line, "char", 4) == 0)
+        {
+            // Parse the current line and create a new CharDef
+            unsigned int charID = this->parseCharacterDefinition(line);
+            validCharsString->insert(charID);
+        }
+        else if (memcmp(line, "kerning first", 13) == 0)
+        {
+            this->parseKerningEntry(line);
+        }
+    }
+    
+    return validCharsString;
+}
+
+std::set<unsigned int>* BMFontConfiguration::parseBinaryConfigFile(unsigned char* pData, unsigned long size, const std::string& controlFile)
+{
+    /* based on http://www.angelcode.com/products/bmfont/doc/file_format.html file format */
+
+    std::set<unsigned int> *validCharsString = new (std::nothrow) std::set<unsigned int>();
+
+    unsigned long remains = size;
+
+    CCASSERT(pData[3] == 3, "Only version 3 is supported");
+
+    pData += 4; remains -= 4;
+
+    while (remains > 0)
+    {
+        unsigned char blockId = pData[0]; pData += 1; remains -= 1;
+        uint32_t blockSize = 0; memcpy(&blockSize, pData, 4);
+
+        pData += 4; remains -= 4;
+
+        if (blockId == 1)
+        {
+            /*
+             fontSize       2   int      0
+             bitField       1   bits     2  bit 0: smooth, bit 1: unicode, bit 2: italic, bit 3: bold, bit 4: fixedHeight, bits 5-7: reserved
+             charSet        1   uint     3
+             stretchH       2   uint     4
+             aa             1   uint     6
+             paddingUp      1   uint     7
+             paddingRight   1   uint     8
+             paddingDown    1   uint     9
+             paddingLeft    1   uint     10
+             spacingHoriz   1   uint     11
+             spacingVert    1   uint     12
+             outline        1   uint     13 added with version 2
+             fontName       n+1 string   14 null terminated string with length n
+             */
+
+            memcpy(&_fontSize, pData, 2);
+            _padding.top = (unsigned char)pData[7];
+            _padding.right = (unsigned char)pData[8];
+            _padding.bottom = (unsigned char)pData[9];
+            _padding.left = (unsigned char)pData[10];
+        }
+		else if (blockId == 2)
+		{
+            /*
+             lineHeight 2   uint    0
+             base       2   uint    2
+             scaleW     2   uint    4
+             scaleH     2   uint    6
+             pages      2   uint    8
+             bitField   1   bits    10  bits 0-6: reserved, bit 7: packed
+             alphaChnl  1   uint    11
+             redChnl    1   uint    12
+             greenChnl  1   uint    13
+             blueChnl   1   uint    14
+             */
+
+            uint16_t lineHeight = 0; memcpy(&lineHeight, pData, 2);
+            _commonHeight = lineHeight;
+
+            uint16_t scaleW = 0; memcpy(&scaleW, pData + 4, 2);
+            uint16_t scaleH = 0; memcpy(&scaleH, pData + 6, 2);
+
+            CCASSERT(scaleW <= Configuration::getInstance()->getMaxTextureSize() && scaleH <= Configuration::getInstance()->getMaxTextureSize(), "CCLabelBMFont: page can't be larger than supported");
+
+            uint16_t pages = 0; memcpy(&pages, pData + 8, 2);
+            CCASSERT(pages == 1, "CCBitfontAtlas: only supports 1 page");
+        }
+        else if (blockId == 3)
+        {
+            /*
+             pageNames 	p*(n+1) 	strings 	0 	p null terminated strings, each with length n
+             */
+
+            const char *value = (const char *)pData;
+            CCASSERT(strlen(value) < blockSize, "Block size should be less then string");
+
+            _atlasName = FileUtils::getInstance()->fullPathFromRelativeFile(value, controlFile);
+        }
+		else if (blockId == 4)
+		{
+            /*
+             id         4   uint    0+c*20  These fields are repeated until all characters have been described
+             x          2   uint    4+c*20
+             y          2   uint    6+c*20
+             width      2   uint    8+c*20
+             height     2   uint    10+c*20
+             xoffset    2   int     12+c*20
+             yoffset    2   int     14+c*20
+             xadvance   2   int     16+c*20
+             page       1   uint    18+c*20
+             chnl       1   uint    19+c*20
+             */
+
+            unsigned long count = blockSize / 20;
+
+            for (unsigned long i = 0; i < count; i++)
+            {
+                uint32_t charId = 0; memcpy(&charId, pData + (i * 20), 4);
+                
+                BMFontDef& fontDef = _fontDefDictionary[charId];
+                fontDef.charID = charId;
+
+                uint16_t charX = 0; memcpy(&charX, pData + (i * 20) + 4, 2);
+                fontDef.rect.origin.x = charX;
+
+                uint16_t charY = 0; memcpy(&charY, pData + (i * 20) + 6, 2);
+                fontDef.rect.origin.y = charY;
+
+                uint16_t charWidth = 0; memcpy(&charWidth, pData + (i * 20) + 8, 2);
+                fontDef.rect.size.width = charWidth;
+
+                uint16_t charHeight = 0; memcpy(&charHeight, pData + (i * 20) + 10, 2);
+                fontDef.rect.size.height = charHeight;
+
+                int16_t xoffset = 0; memcpy(&xoffset, pData + (i * 20) + 12, 2);
+                fontDef.xOffset = xoffset;
+
+                int16_t yoffset = 0; memcpy(&yoffset, pData + (i * 20) + 14, 2);
+                fontDef.yOffset = yoffset;
+
+                int16_t xadvance = 0; memcpy(&xadvance, pData + (i * 20) + 16, 2);
+                fontDef.xAdvance = xadvance;
+
+                validCharsString->insert(fontDef.charID);
+            }
+        }
+        else if (blockId == 5) {
+            /*
+             first  4   uint    0+c*10 	These fields are repeated until all kerning pairs have been described
+             second 4   uint    4+c*10
+             amount 2   int     8+c*10
+             */
+
+            unsigned long count = blockSize / 20;
+
+            for (unsigned long i = 0; i < count; i++)
+            {
+                uint32_t first = 0; memcpy(&first, pData + (i * 10), 4);
+                uint32_t second = 0; memcpy(&second, pData + (i * 10) + 4, 4);
+                int16_t amount = 0; memcpy(&amount, pData + (i * 10) + 8, 2);
+
+                uint64_t key = ((uint64_t)first<<32) | ((uint64_t)second&0xffffffffll);
+                _kerningDictionary[key] = amount;
+            }
+        }
+
+        pData += blockSize; remains -= blockSize;
+    }
+
+    return validCharsString;
+}
+
+void BMFontConfiguration::parseImageFileName(const char* line, const std::string& fntFile)
+{
+    //////////////////////////////////////////////////////////////////////////
+    // line to parse:
+    // page id=0 file="bitmapFontTest.png"
+    //////////////////////////////////////////////////////////////////////////
+
+    // page ID. Sanity check
+    int pageId;
+    sscanf(line, "page id=%d", &pageId);
+    CCASSERT(pageId == 0, "LabelBMFont file could not be found");
+    // file 
+    char fileName[255];
+    sscanf(strchr(line,'"') + 1, "%[^\"]", fileName);
+    _atlasName = FileUtils::getInstance()->fullPathFromRelativeFile(fileName, fntFile);
+}
+
+void BMFontConfiguration::parseInfoArguments(const char* line)
+{
+    //////////////////////////////////////////////////////////////////////////
+    // possible lines to parse:
+    // info face="Script" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,4,3,2 spacing=0,0 outline=0
+    // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
+    //////////////////////////////////////////////////////////////////////////
+    sscanf(strstr(line, "size=") + 5, "%d", &_fontSize);
+    // padding
+    sscanf(strstr(line,"padding=") + 8, "%d,%d,%d,%d", &_padding.top, &_padding.right, &_padding.bottom, &_padding.left);
+    //CCLOG("cocos2d: padding: %d,%d,%d,%d", _padding.left, _padding.top, _padding.right, _padding.bottom);
+}
+
+void BMFontConfiguration::parseCommonArguments(const char* line)
+{
+    //////////////////////////////////////////////////////////////////////////
+    // line to parse:
+    // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0
+    //////////////////////////////////////////////////////////////////////////
+  
+    // Height
+    auto tmp = strstr(line, "lineHeight=") + 11;
+    sscanf(tmp, "%d", &_commonHeight);
+    
+#if COCOS2D_DEBUG > 0
+    // scaleW. sanity check
+    int value;
+    tmp = strstr(tmp, "scaleW=") + 7;
+    sscanf(tmp, "%d", &value);
+
+    int maxTextureSize = Configuration::getInstance()->getMaxTextureSize();
+    CCASSERT(value <= maxTextureSize, "CCLabelBMFont: page can't be larger than supported");
+
+    // scaleH. sanity check
+    tmp = strstr(tmp, "scaleH=") + 7;
+    sscanf(tmp, "%d", &value);
+    CCASSERT(value <= maxTextureSize, "CCLabelBMFont: page can't be larger than supported");
+
+    // pages. sanity check
+    tmp = strstr(tmp, "pages=") + 6;
+    sscanf(tmp, "%d", &value);
+    CCASSERT(value == 1, "CCBitfontAtlas: only supports 1 page");
+#endif
+    // packed (ignore) What does this mean ??
+}
+
+unsigned int BMFontConfiguration::parseCharacterDefinition(const char* line)
+{
+    unsigned int charID = 0;
+    
+    //////////////////////////////////////////////////////////////////////////
+    // line to parse:
+    // char id=32   x=0     y=0     width=0     height=0     xoffset=0     yoffset=44    xadvance=14     page=0  chnl=0 
+    //////////////////////////////////////////////////////////////////////////
+
+    // Character ID
+    auto tmp = strstr(line, "id=") + 3;
+    sscanf(tmp, "%u", &charID);
+    
+    BMFontDef& characterDefinition = _fontDefDictionary[charID];
+    characterDefinition.charID = charID;
+
+    // Character x
+    tmp = strstr(tmp, "x=") + 2;
+    sscanf(tmp, "%f", &characterDefinition.rect.origin.x);
+    // Character y
+    tmp = strstr(tmp, "y=") + 2;
+    sscanf(tmp, "%f", &characterDefinition.rect.origin.y);
+    // Character width
+    tmp = strstr(tmp, "width=") + 6;
+    sscanf(tmp, "%f", &characterDefinition.rect.size.width);
+    // Character height
+    tmp = strstr(tmp, "height=") + 7;
+    sscanf(tmp, "%f", &characterDefinition.rect.size.height);
+    // Character xoffset
+    tmp = strstr(tmp, "xoffset=") + 8;
+    sscanf(tmp, "%hd", &characterDefinition.xOffset);
+    // Character yoffset
+    tmp = strstr(tmp, "yoffset=") + 8;
+    sscanf(tmp, "%hd", &characterDefinition.yOffset);
+    // Character xadvance
+    tmp = strstr(tmp, "xadvance=") + 9;
+    sscanf(tmp, "%hd", &characterDefinition.xAdvance);
+    
+    return charID;
+}
+
+void BMFontConfiguration::parseKerningEntry(const char* line)
+{        
+    //////////////////////////////////////////////////////////////////////////
+    // line to parse:
+    // kerning first=121  second=44  amount=-7
+    //////////////////////////////////////////////////////////////////////////
+
+    int first, second, amount;
+    auto tmp = strstr(line, "first=") + 6;
+    sscanf(tmp, "%d", &first);
+
+    tmp = strstr(tmp, "second=") + 7;
+    sscanf(tmp, "%d", &second);
+
+    tmp = strstr(tmp, "amount=") + 7;
+    sscanf(tmp, "%d", &amount);
+
+    uint64_t key = ((uint64_t)first<<32) | ((uint64_t)second&0xffffffffll);
+    
+    _kerningDictionary[key] = amount;
+}
+
+FontFNT * FontFNT::create(const std::string& fntFilePath, const Vec2& imageOffset /* = Vec2::ZERO */)
+{
+    BMFontConfiguration *newConf = FNTConfigLoadFile(fntFilePath);
+    if (!newConf)
+        return nullptr;
+    
+    // add the texture
+    Texture2D *tempTexture = Director::getInstance()->getTextureCache()->addImage(newConf->getAtlasName());
+    if (!tempTexture)
+    {
+        return nullptr;
+    }
+    
+    FontFNT *tempFont =  new FontFNT(newConf,imageOffset);
+    tempFont->setFontSize((float)newConf->_fontSize);
+    if (!tempFont)
+    {
+        return nullptr;
+    }
+    tempFont->autorelease();
+    return tempFont;
+}
+
+FontFNT::FontFNT(BMFontConfiguration *theContfig, const Vec2& imageOffset /* = Vec2::ZERO */)
+:_configuration(theContfig)
+,_imageOffset(CC_POINT_PIXELS_TO_POINTS(imageOffset))
+{
+    _configuration->retain();
+}
+
+FontFNT::~FontFNT()
+{
+    _configuration->release();
+}
+
+void FontFNT::purgeCachedData()
+{
+    if (s_configurations)
+    {
+        s_configurations->clear();
+        CC_SAFE_DELETE(s_configurations);
+    }
+}
+
+int * FontFNT::getHorizontalKerningForTextUTF32(const std::u32string& text, int &outNumLetters) const
+{
+    outNumLetters = static_cast<int>(text.length());
+    
+    if (!outNumLetters)
+        return nullptr;
+    
+    int *sizes = new (std::nothrow) int[outNumLetters];
+    if (!sizes)
+        return nullptr;
+    
+    for (int c = 0; c < outNumLetters; ++c)
+    {
+        if (c < (outNumLetters-1))
+            sizes[c] = getHorizontalKerningForChars(text[c], text[c+1]);
+        else
+            sizes[c] = 0;
+    }
+    
+    return sizes;
+}
+
+int  FontFNT::getHorizontalKerningForChars(char32_t firstChar, char32_t secondChar) const
+{
+    int ret = 0;
+    uint64_t key = ((uint64_t)firstChar << 32) | ((uint64_t)secondChar & 0xffffffffll);
+    
+    auto iter = _configuration->_kerningDictionary.find(key);
+    
+    if (iter != _configuration->_kerningDictionary.end())
+    {
+        ret = iter->second;
+    }
+    
+    return ret;
+}
+
+void FontFNT::setFontSize(float fontSize)
+{
+    _fontSize = fontSize;
+}
+
+int FontFNT::getOriginalFontSize()const
+{
+    return _configuration->_fontSize;
+}
+
+FontAtlas * FontFNT::createFontAtlas()
+{
+    // check that everything is fine with the BMFontCofniguration
+    if (_configuration->_fontDefDictionary.empty())
+        return nullptr;
+    
+    size_t numGlyphs = _configuration->_characterSet->size();
+    if (numGlyphs == 0)
+        return nullptr;
+    
+    if (_configuration->_commonHeight == 0)
+        return nullptr;
+    
+    FontAtlas *tempAtlas = new (std::nothrow) FontAtlas(*this);
+    if (tempAtlas == nullptr)
+        return nullptr;
+    
+    // common height
+    int originalFontSize = _configuration->_fontSize;
+    float originalLineHeight = (float)_configuration->_commonHeight;
+    float factor = 0.0f;
+    if (std::abs(_fontSize - originalFontSize) < FLT_EPSILON) {
+        factor = 1.0f;
+    }else {
+        factor = _fontSize / originalFontSize;
+    }
+    
+    tempAtlas->setLineHeight(originalLineHeight * factor);
+    
+    for (auto&& e : _configuration->_fontDefDictionary)
+    {
+        BMFontDef& fontDef = e.second;
+        
+        FontLetterDefinition tempDefinition;
+
+        Rect tempRect;
+        
+        tempRect = fontDef.rect;
+        tempRect = CC_RECT_PIXELS_TO_POINTS(tempRect);
+        
+        tempDefinition.offsetX  = fontDef.xOffset;
+        tempDefinition.offsetY  = fontDef.yOffset;
+        
+        tempDefinition.U        = tempRect.origin.x + _imageOffset.x;
+        tempDefinition.V        = tempRect.origin.y + _imageOffset.y;
+        
+        tempDefinition.width    = tempRect.size.width;
+        tempDefinition.height   = tempRect.size.height;
+        
+        //carloX: only one texture supported FOR NOW
+        tempDefinition.textureID = 0;
+        
+        tempDefinition.validDefinition = true;
+        tempDefinition.xAdvance = fontDef.xAdvance;
+        // add the new definition
+        if (65535 < fontDef.charID) {
+            CCLOGWARN("Warning: 65535 < fontDef.charID (%u), ignored", fontDef.charID);
+        } else {
+            tempAtlas->addLetterDefinition(fontDef.charID,tempDefinition);
+        }
+    }
+    
+    // add the texture (only one texture for now)
+    
+    Texture2D *tempTexture = Director::getInstance()->getTextureCache()->addImage(_configuration->getAtlasName());
+    if (!tempTexture) {
+        CC_SAFE_RELEASE(tempAtlas);
+        return nullptr;
+    }
+    
+    // add the texture
+    tempAtlas->addTexture(tempTexture, 0);
+    
+    // done
+    return tempAtlas;
+}
+
+void FontFNT::reloadBMFontResource(const std::string& fntFilePath)
+{
+    if (s_configurations == nullptr)
+    {
+        s_configurations = new (std::nothrow) Map<std::string, BMFontConfiguration*>();
+    }
+
+    BMFontConfiguration *ret = s_configurations->at(fntFilePath);
+    if (ret != nullptr)
+    {
+        s_configurations->erase(fntFilePath);
+    }
+    ret = BMFontConfiguration::create(fntFilePath);
+    if (ret)
+    {
+        s_configurations->insert(fntFilePath, ret);
+        Director::getInstance()->getTextureCache()->reloadTexture(ret->getAtlasName());
+
+    }
+}
+
+NS_CC_END

+ 78 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontFNT.h

@@ -0,0 +1,78 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#ifndef _CCFontFNT_h_
+#define _CCFontFNT_h_
+
+/// @cond DO_NOT_SHOW
+
+#include "2d/CCFont.h"
+
+NS_CC_BEGIN
+
+class BMFontConfiguration;
+
+class CC_DLL FontFNT : public Font
+{
+    
+public:
+    
+    static FontFNT * create(const std::string& fntFilePath, const Vec2& imageOffset = Vec2::ZERO);
+    /** Purges the cached data.
+    Removes from memory the cached configurations and the atlas name dictionary.
+    */
+    static void purgeCachedData();
+    virtual int* getHorizontalKerningForTextUTF32(const std::u32string& text, int &outNumLetters) const override;
+    virtual FontAtlas *createFontAtlas() override;
+    void setFontSize(float fontSize);
+    int getOriginalFontSize()const;
+
+    static void reloadBMFontResource(const std::string& fntFilePath);
+
+protected:
+    
+    FontFNT(BMFontConfiguration *theContfig, const Vec2& imageOffset = Vec2::ZERO);
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~FontFNT();
+    
+private:
+    
+    int  getHorizontalKerningForChars(char32_t firstChar, char32_t secondChar) const;
+    
+    BMFontConfiguration * _configuration;
+    Vec2                   _imageOffset;
+    //User defined font size
+    float  _fontSize;
+};
+
+/// @endcond
+
+NS_CC_END
+
+#endif /* defined(__cocos2d_libs__CCFontFNT__) */

+ 669 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontFreeType.cpp

@@ -0,0 +1,669 @@
+/****************************************************************************
+Copyright (c) 2013      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCFontFreeType.h"
+#include FT_BBOX_H
+#include "edtaa3func.h"
+#include "2d/CCFontAtlas.h"
+#include "base/CCDirector.h"
+#include "base/ccUTF8.h"
+#include "platform/CCFileUtils.h"
+
+NS_CC_BEGIN
+
+
+FT_Library FontFreeType::_FTlibrary;
+bool       FontFreeType::_FTInitialized = false;
+const int  FontFreeType::DistanceMapSpread = 3;
+
+const char* FontFreeType::_glyphASCII = "\"!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ ";
+const char* FontFreeType::_glyphNEHE = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ";
+
+typedef struct _DataRef
+{
+    Data data;
+    unsigned int referenceCount;
+}DataRef;
+
+static std::unordered_map<std::string, DataRef> s_cacheFontData;
+
+FontFreeType * FontFreeType::create(const std::string &fontName, float fontSize, GlyphCollection glyphs, const char *customGlyphs,bool distanceFieldEnabled /* = false */,float outline /* = 0 */)
+{
+    FontFreeType *tempFont =  new (std::nothrow) FontFreeType(distanceFieldEnabled,outline);
+
+    if (!tempFont)
+        return nullptr;
+    
+    tempFont->setGlyphCollection(glyphs, customGlyphs);
+    
+    if (!tempFont->createFontObject(fontName, fontSize))
+    {
+        delete tempFont;
+        return nullptr;
+    }
+    tempFont->autorelease();
+    return tempFont;
+}
+
+bool FontFreeType::initFreeType()
+{
+    if (_FTInitialized == false)
+    {
+        // begin freetype
+        if (FT_Init_FreeType( &_FTlibrary ))
+            return false;
+        
+        _FTInitialized = true;
+    }
+    
+    return  _FTInitialized;
+}
+
+void FontFreeType::shutdownFreeType()
+{
+    if (_FTInitialized == true)
+    {
+        FT_Done_FreeType(_FTlibrary);
+        s_cacheFontData.clear();
+        _FTInitialized = false;
+    }
+}
+
+FT_Library FontFreeType::getFTLibrary()
+{
+    initFreeType();
+    return _FTlibrary;
+}
+
+FontFreeType::FontFreeType(bool distanceFieldEnabled /* = false */, float outline /* = 0 */)
+: _fontRef(nullptr)
+, _stroker(nullptr)
+, _encoding(FT_ENCODING_UNICODE)
+, _distanceFieldEnabled(distanceFieldEnabled)
+, _outlineSize(0.0f)
+, _lineHeight(0)
+, _fontAtlas(nullptr)
+, _usedGlyphs(GlyphCollection::ASCII)
+{
+    if (outline > 0.0f)
+    {
+        _outlineSize = outline * CC_CONTENT_SCALE_FACTOR();
+        FT_Stroker_New(FontFreeType::getFTLibrary(), &_stroker);
+        FT_Stroker_Set(_stroker,
+            (int)(_outlineSize * 64),
+            FT_STROKER_LINECAP_ROUND,
+            FT_STROKER_LINEJOIN_ROUND,
+            0);
+    }
+}
+
+bool FontFreeType::createFontObject(const std::string &fontName, float fontSize)
+{
+    FT_Face face;
+    // save font name locally
+    _fontName = fontName;
+
+    auto it = s_cacheFontData.find(fontName);
+    if (it != s_cacheFontData.end())
+    {
+        (*it).second.referenceCount += 1;
+    }
+    else
+    {
+        s_cacheFontData[fontName].referenceCount = 1;
+        s_cacheFontData[fontName].data = FileUtils::getInstance()->getDataFromFile(fontName);    
+
+        if (s_cacheFontData[fontName].data.isNull())
+        {
+            return false;
+        }
+    }
+
+    if (FT_New_Memory_Face(getFTLibrary(), s_cacheFontData[fontName].data.getBytes(), s_cacheFontData[fontName].data.getSize(), 0, &face ))
+        return false;
+
+    if (FT_Select_Charmap(face, FT_ENCODING_UNICODE))
+    {
+        int foundIndex = -1;
+        for (int charmapIndex = 0; charmapIndex < face->num_charmaps; charmapIndex++)
+        {
+            if (face->charmaps[charmapIndex]->encoding != FT_ENCODING_NONE)
+            {
+                foundIndex = charmapIndex;
+                break;
+            }
+        }
+
+        if (foundIndex == -1)
+        {
+            return false;
+        }
+
+        _encoding = face->charmaps[foundIndex]->encoding;
+        if (FT_Select_Charmap(face, _encoding))
+        {
+            return false;
+        }
+    }
+
+    // set the requested font size
+    int dpi = 72;
+    int fontSizePoints = (int)(64.f * fontSize * CC_CONTENT_SCALE_FACTOR());
+    if (FT_Set_Char_Size(face, fontSizePoints, fontSizePoints, dpi, dpi))
+        return false;
+    
+    // store the face globally
+    _fontRef = face;
+    _lineHeight = static_cast<int>((_fontRef->size->metrics.ascender - _fontRef->size->metrics.descender) >> 6);
+    
+    // done and good
+    return true;
+}
+
+FontFreeType::~FontFreeType()
+{
+    if (_FTInitialized)
+    {
+        if (_stroker)
+        {
+            FT_Stroker_Done(_stroker);
+        }
+        if (_fontRef)
+        {
+            FT_Done_Face(_fontRef);
+        }
+    }
+
+    auto iter = s_cacheFontData.find(_fontName);
+    if (iter != s_cacheFontData.end())
+    {
+        iter->second.referenceCount -= 1;
+        if (iter->second.referenceCount == 0)
+        {
+            s_cacheFontData.erase(iter);
+        }
+    }
+}
+
+FontAtlas * FontFreeType::createFontAtlas()
+{
+    if (_fontAtlas == nullptr)
+    {
+        _fontAtlas = new (std::nothrow) FontAtlas(*this);
+        if (_fontAtlas && _usedGlyphs != GlyphCollection::DYNAMIC)
+        {
+            std::u32string utf32;
+            if (StringUtils::UTF8ToUTF32(getGlyphCollection(), utf32))
+            {
+                _fontAtlas->prepareLetterDefinitions(utf32);
+            }
+        }
+//        this->autorelease();
+    }
+    
+    return _fontAtlas;
+}
+
+int * FontFreeType::getHorizontalKerningForTextUTF32(const std::u32string& text, int &outNumLetters) const
+{
+    if (!_fontRef)
+        return nullptr;
+    
+    outNumLetters = static_cast<int>(text.length());
+
+    if (!outNumLetters)
+        return nullptr;
+    
+    int *sizes = new (std::nothrow) int[outNumLetters];
+    if (!sizes)
+        return nullptr;
+    memset(sizes,0,outNumLetters * sizeof(int));
+
+    bool hasKerning = FT_HAS_KERNING( _fontRef ) != 0;
+    if (hasKerning)
+    {
+        for (int c = 1; c < outNumLetters; ++c)
+        {
+            sizes[c] = getHorizontalKerningForChars(text[c-1], text[c]);
+        }
+    }
+    
+    return sizes;
+}
+
+int  FontFreeType::getHorizontalKerningForChars(uint64_t firstChar, uint64_t secondChar) const
+{
+    // get the ID to the char we need
+    int glyphIndex1 = FT_Get_Char_Index(_fontRef, static_cast<FT_ULong>(firstChar));
+    
+    if (!glyphIndex1)
+        return 0;
+    
+    // get the ID to the char we need
+    int glyphIndex2 = FT_Get_Char_Index(_fontRef, static_cast<FT_ULong>(secondChar));
+    
+    if (!glyphIndex2)
+        return 0;
+    
+    FT_Vector kerning;
+    
+    if (FT_Get_Kerning( _fontRef, glyphIndex1, glyphIndex2,  FT_KERNING_DEFAULT,  &kerning))
+        return 0;
+    
+    return (static_cast<int>(kerning.x >> 6));
+}
+
+int FontFreeType::getFontAscender() const
+{
+    return (static_cast<int>(_fontRef->size->metrics.ascender >> 6));
+}
+
+const char* FontFreeType::getFontFamily() const
+{
+    if (!_fontRef)
+        return nullptr;
+
+    return _fontRef->family_name;
+}
+
+unsigned char* FontFreeType::getGlyphBitmap(uint64_t theChar, long &outWidth, long &outHeight, Rect &outRect,int &xAdvance)
+{
+    bool invalidChar = true;
+    unsigned char* ret = nullptr;
+
+    do
+    {
+        if (_fontRef == nullptr)
+            break;
+
+        if (_distanceFieldEnabled)
+        {
+            if (FT_Load_Char(_fontRef, static_cast<FT_ULong>(theChar), FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT))
+                break;
+        }
+        else
+        {
+            if (FT_Load_Char(_fontRef, static_cast<FT_ULong>(theChar), FT_LOAD_RENDER | FT_LOAD_NO_AUTOHINT))
+                break;
+        }
+
+        auto& metrics = _fontRef->glyph->metrics;
+        outRect.origin.x = static_cast<float>(metrics.horiBearingX >> 6);
+        outRect.origin.y = static_cast<float>(-(metrics.horiBearingY >> 6));
+        outRect.size.width = static_cast<float>((metrics.width >> 6));
+        outRect.size.height = static_cast<float>((metrics.height >> 6));
+
+        xAdvance = (static_cast<int>(_fontRef->glyph->metrics.horiAdvance >> 6));
+
+        outWidth  = _fontRef->glyph->bitmap.width;
+        outHeight = _fontRef->glyph->bitmap.rows;
+        ret = _fontRef->glyph->bitmap.buffer;
+
+        if (_outlineSize > 0 && outWidth > 0 && outHeight > 0)
+        {
+            auto copyBitmap = new (std::nothrow) unsigned char[outWidth * outHeight];
+            memcpy(copyBitmap,ret,outWidth * outHeight * sizeof(unsigned char));
+
+            FT_BBox bbox;
+            auto outlineBitmap = getGlyphBitmapWithOutline(theChar,bbox);
+            if(outlineBitmap == nullptr)
+            {
+                ret = nullptr;
+                delete [] copyBitmap;
+                break;
+            }
+
+            int glyphMinX = (int)outRect.origin.x;
+            int glyphMaxX = (int)(outRect.origin.x + outWidth);
+            int glyphMinY = (int)(-outHeight - outRect.origin.y);
+            int glyphMaxY = (int)-outRect.origin.y;
+
+            auto outlineMinX = bbox.xMin >> 6;
+            auto outlineMaxX = bbox.xMax >> 6;
+            auto outlineMinY = bbox.yMin >> 6;
+            auto outlineMaxY = bbox.yMax >> 6;
+            auto outlineWidth = outlineMaxX - outlineMinX;
+            auto outlineHeight = outlineMaxY - outlineMinY;
+
+            auto blendImageMinX = MIN(outlineMinX, glyphMinX);
+            auto blendImageMaxY = MAX(outlineMaxY, glyphMaxY);
+            auto blendWidth = MAX(outlineMaxX, glyphMaxX) - blendImageMinX;
+            auto blendHeight = blendImageMaxY - MIN(outlineMinY, glyphMinY);
+
+            outRect.origin.x = (float)blendImageMinX;
+            outRect.origin.y = -blendImageMaxY + _outlineSize;
+
+            unsigned char *blendImage = nullptr;
+            if (blendWidth > 0 && blendHeight > 0)
+            {
+                FT_Pos index, index2;
+                auto imageSize = blendWidth * blendHeight * 2;
+                blendImage = new (std::nothrow) unsigned char[imageSize];
+                memset(blendImage, 0, imageSize);
+
+                auto px = outlineMinX - blendImageMinX;
+                auto py = blendImageMaxY - outlineMaxY;
+                for (int x = 0; x < outlineWidth; ++x)
+                {
+                    for (int y = 0; y < outlineHeight; ++y)
+                    {
+                        index = px + x + ((py + y) * blendWidth);
+                        index2 = x + (y * outlineWidth);
+                        blendImage[2 * index] = outlineBitmap[index2];
+                    }
+                }
+
+                px = glyphMinX - blendImageMinX;
+                py = blendImageMaxY - glyphMaxY;
+                for (int x = 0; x < outWidth; ++x)
+                {
+                    for (int y = 0; y < outHeight; ++y)
+                    {
+                        index = px + x + ((y + py) * blendWidth);
+                        index2 = x + (y * outWidth);
+                        blendImage[2 * index + 1] = copyBitmap[index2];
+                    }
+                }
+            }
+
+            outRect.size.width  = (float)blendWidth;
+            outRect.size.height = (float)blendHeight;
+            outWidth  = blendWidth;
+            outHeight = blendHeight;
+
+            delete [] outlineBitmap;
+            delete [] copyBitmap;
+            ret = blendImage;
+        }
+
+        invalidChar = false;
+    } while (0);
+
+    if (invalidChar)
+    {
+        outRect.size.width  = 0;
+        outRect.size.height = 0;
+        xAdvance = 0;
+
+        return nullptr;
+    }
+    else
+    {
+       return ret;
+    }
+}
+
+unsigned char * FontFreeType::getGlyphBitmapWithOutline(uint64_t theChar, FT_BBox &bbox)
+{   
+    unsigned char* ret = nullptr;
+    if (FT_Load_Char(_fontRef, static_cast<FT_ULong>(theChar), FT_LOAD_NO_BITMAP) == 0)
+    {
+        if (_fontRef->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
+        {
+            FT_Glyph glyph;
+            if (FT_Get_Glyph(_fontRef->glyph, &glyph) == 0)
+            {
+                FT_Glyph_StrokeBorder(&glyph, _stroker, 0, 1);
+                if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
+                {
+                    FT_Outline *outline = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
+                    FT_Glyph_Get_CBox(glyph,FT_GLYPH_BBOX_GRIDFIT,&bbox);
+                    long width = (bbox.xMax - bbox.xMin)>>6;
+                    long rows = (bbox.yMax - bbox.yMin)>>6;
+
+                    FT_Bitmap bmp;
+                    bmp.buffer = new (std::nothrow) unsigned char[width * rows];
+                    memset(bmp.buffer, 0, width * rows);
+                    bmp.width = (int)width;
+                    bmp.rows = (int)rows;
+                    bmp.pitch = (int)width;
+                    bmp.pixel_mode = FT_PIXEL_MODE_GRAY;
+                    bmp.num_grays = 256;
+
+                    FT_Raster_Params params;
+                    memset(&params, 0, sizeof (params));
+                    params.source = outline;
+                    params.target = &bmp;
+                    params.flags = FT_RASTER_FLAG_AA;
+                    FT_Outline_Translate(outline,-bbox.xMin,-bbox.yMin);
+                    FT_Outline_Render(_FTlibrary, outline, &params);
+
+                    ret = bmp.buffer;
+                }
+                FT_Done_Glyph(glyph);
+            }
+        }
+    }
+
+    return ret;
+}
+
+unsigned char * makeDistanceMap( unsigned char *img, long width, long height)
+{
+    long pixelAmount = (width + 2 * FontFreeType::DistanceMapSpread) * (height + 2 * FontFreeType::DistanceMapSpread);
+
+    short * xdist = (short *)  malloc( pixelAmount * sizeof(short) );
+    short * ydist = (short *)  malloc( pixelAmount * sizeof(short) );
+    double * gx   = (double *) calloc( pixelAmount, sizeof(double) );
+    double * gy      = (double *) calloc( pixelAmount, sizeof(double) );
+    double * data    = (double *) calloc( pixelAmount, sizeof(double) );
+    double * outside = (double *) calloc( pixelAmount, sizeof(double) );
+    double * inside  = (double *) calloc( pixelAmount, sizeof(double) );
+    long i,j;
+
+    // Convert img into double (data) rescale image levels between 0 and 1
+    long outWidth = width + 2 * FontFreeType::DistanceMapSpread;
+    for (i = 0; i < width; ++i)
+    {
+        for (j = 0; j < height; ++j)
+        {
+            data[j * outWidth + FontFreeType::DistanceMapSpread + i] = img[j * width + i] / 255.0;
+        }
+    }
+
+    width += 2 * FontFreeType::DistanceMapSpread;
+    height += 2 * FontFreeType::DistanceMapSpread;
+
+    // Transform background (outside contour, in areas of 0's)   
+    computegradient( data, (int)width, (int)height, gx, gy);
+    edtaa3(data, gx, gy, (int)width, (int)height, xdist, ydist, outside);
+    for( i=0; i< pixelAmount; i++)
+        if( outside[i] < 0.0 )
+            outside[i] = 0.0;
+
+    // Transform foreground (inside contour, in areas of 1's)   
+    for( i=0; i< pixelAmount; i++)
+        data[i] = 1 - data[i];
+    computegradient( data, (int)width, (int)height, gx, gy);
+    edtaa3(data, gx, gy, (int)width, (int)height, xdist, ydist, inside);
+    for( i=0; i< pixelAmount; i++)
+        if( inside[i] < 0.0 )
+            inside[i] = 0.0;
+
+    // The bipolar distance field is now outside-inside
+    double dist;
+    /* Single channel 8-bit output (bad precision and range, but simple) */    
+    unsigned char *out = (unsigned char *) malloc( pixelAmount * sizeof(unsigned char) );
+    for( i=0; i < pixelAmount; i++)
+    {
+        dist = outside[i] - inside[i];
+        dist = 128.0 - dist*16;
+        if( dist < 0 ) dist = 0;
+        if( dist > 255 ) dist = 255;
+        out[i] = (unsigned char) dist;
+    }
+    /* Dual channel 16-bit output (more complicated, but good precision and range) */
+    /*unsigned char *out = (unsigned char *) malloc( pixelAmount * 3 * sizeof(unsigned char) ); 
+    for( i=0; i< pixelAmount; i++)
+    {
+        dist = outside[i] - inside[i];
+        dist = 128.0 - dist*16;
+        if( dist < 0.0 ) dist = 0.0;
+        if( dist >= 256.0 ) dist = 255.999;
+        // R channel is a copy of the original grayscale image
+        out[3*i] = img[i];
+        // G channel is fraction
+        out[3*i + 1] = (unsigned char) ( 256 - (dist - floor(dist)* 256.0 ));
+        // B channel is truncated integer part
+        out[3*i + 2] = (unsigned char)dist; 
+    }*/
+    
+    free( xdist );
+    free( ydist );
+    free( gx );
+    free( gy );
+    free( data );
+    free( outside );
+    free( inside );
+
+    return out;
+}
+
+void FontFreeType::renderCharAt(unsigned char *dest,int posX, int posY, unsigned char* bitmap,long bitmapWidth,long bitmapHeight)
+{
+    int iX = posX;
+    int iY = posY;
+
+    if (_distanceFieldEnabled)
+    {
+        auto distanceMap = makeDistanceMap(bitmap,bitmapWidth,bitmapHeight);
+
+        bitmapWidth += 2 * DistanceMapSpread;
+        bitmapHeight += 2 * DistanceMapSpread;
+
+        for (long y = 0; y < bitmapHeight; ++y)
+        {
+            long bitmap_y = y * bitmapWidth;
+
+            for (long x = 0; x < bitmapWidth; ++x)
+            {    
+                /* Dual channel 16-bit output (more complicated, but good precision and range) */
+                /*int index = (iX + ( iY * destSize )) * 3;                
+                int index2 = (bitmap_y + x)*3;
+                dest[index] = out[index2];
+                dest[index + 1] = out[index2 + 1];
+                dest[index + 2] = out[index2 + 2];*/
+
+                //Single channel 8-bit output 
+                dest[iX + ( iY * FontAtlas::CacheTextureWidth )] = distanceMap[bitmap_y + x];
+
+                iX += 1;
+            }
+
+            iX  = posX;
+            iY += 1;
+        }
+        free(distanceMap);
+    }
+    else if(_outlineSize > 0)
+    {
+        unsigned char tempChar;
+        for (long y = 0; y < bitmapHeight; ++y)
+        {
+            long bitmap_y = y * bitmapWidth;
+
+            for (int x = 0; x < bitmapWidth; ++x)
+            {
+                tempChar = bitmap[(bitmap_y + x) * 2];
+                dest[(iX + ( iY * FontAtlas::CacheTextureWidth ) ) * 2] = tempChar;
+                tempChar = bitmap[(bitmap_y + x) * 2 + 1];
+                dest[(iX + ( iY * FontAtlas::CacheTextureWidth ) ) * 2 + 1] = tempChar;
+
+                iX += 1;
+            }
+
+            iX  = posX;
+            iY += 1;
+        }
+        delete [] bitmap;
+    }
+    else
+    {
+        for (long y = 0; y < bitmapHeight; ++y)
+        {
+            long bitmap_y = y * bitmapWidth;
+
+            for (int x = 0; x < bitmapWidth; ++x)
+            {
+                unsigned char cTemp = bitmap[bitmap_y + x];
+
+                // the final pixel
+                dest[(iX + ( iY * FontAtlas::CacheTextureWidth ) )] = cTemp;
+
+                iX += 1;
+            }
+
+            iX  = posX;
+            iY += 1;
+        }
+    } 
+}
+
+void FontFreeType::setGlyphCollection(GlyphCollection glyphs, const char* customGlyphs /* = nullptr */)
+{
+    _usedGlyphs = glyphs;
+    if (glyphs == GlyphCollection::CUSTOM)
+    {
+        _customGlyphs = customGlyphs;
+    }
+}
+
+const char* FontFreeType::getGlyphCollection() const
+{
+    const char* glyphCollection = nullptr;
+    switch (_usedGlyphs)
+    {
+    case cocos2d::GlyphCollection::DYNAMIC:
+        break;
+    case cocos2d::GlyphCollection::NEHE:
+        glyphCollection = _glyphNEHE;
+        break;
+    case cocos2d::GlyphCollection::ASCII:
+        glyphCollection = _glyphASCII;
+        break;
+    case cocos2d::GlyphCollection::CUSTOM:
+        glyphCollection = _customGlyphs.c_str();
+        break;
+    default:
+        break;
+    }
+
+    return glyphCollection;
+}
+
+void FontFreeType::releaseFont(const std::string &fontName)
+{
+    auto item = s_cacheFontData.begin();
+    while (s_cacheFontData.end() != item)
+    {
+        if (item->first.find(fontName) != std::string::npos)
+            item = s_cacheFontData.erase(item);
+        else
+            item++;
+    }
+}
+
+NS_CC_END

+ 111 - 0
3rdparty/cocos2dx/include/cocos/2d/CCFontFreeType.h

@@ -0,0 +1,111 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#ifndef _FontFreetype_h_
+#define _FontFreetype_h_
+
+/// @cond DO_NOT_SHOW
+
+#include "2d/CCFont.h"
+
+#include <string>
+#include <ft2build.h>
+
+#include FT_FREETYPE_H
+#include FT_STROKER_H
+
+NS_CC_BEGIN
+
+class CC_DLL FontFreeType : public Font
+{
+public:
+    static const int DistanceMapSpread;
+
+    static FontFreeType* create(const std::string &fontName, float fontSize, GlyphCollection glyphs,
+        const char *customGlyphs,bool distanceFieldEnabled = false, float outline = 0);
+
+    static void shutdownFreeType();
+
+    bool isDistanceFieldEnabled() const { return _distanceFieldEnabled;}
+
+    float getOutlineSize() const { return _outlineSize; }
+
+    void renderCharAt(unsigned char *dest,int posX, int posY, unsigned char* bitmap,long bitmapWidth,long bitmapHeight); 
+
+    FT_Encoding getEncoding() const { return _encoding; }
+
+    int* getHorizontalKerningForTextUTF32(const std::u32string& text, int &outNumLetters) const override;
+    
+    unsigned char* getGlyphBitmap(uint64_t theChar, long &outWidth, long &outHeight, Rect &outRect,int &xAdvance);
+    
+    int getFontAscender() const;
+    const char* getFontFamily() const;
+    std::string getFontName() const { return _fontName; }
+
+    virtual FontAtlas* createFontAtlas() override;
+    virtual int getFontMaxHeight() const override { return _lineHeight; }
+
+    static void releaseFont(const std::string &fontName);
+
+private:
+    static const char* _glyphASCII;
+    static const char* _glyphNEHE;
+    static FT_Library _FTlibrary;
+    static bool _FTInitialized;
+
+    FontFreeType(bool distanceFieldEnabled = false, float outline = 0);
+    virtual ~FontFreeType();
+
+    bool createFontObject(const std::string &fontName, float fontSize);
+
+    bool initFreeType();
+    FT_Library getFTLibrary();
+    
+    int getHorizontalKerningForChars(uint64_t firstChar, uint64_t secondChar) const;
+    unsigned char* getGlyphBitmapWithOutline(uint64_t code, FT_BBox &bbox);
+
+    void setGlyphCollection(GlyphCollection glyphs, const char* customGlyphs = nullptr);
+    const char* getGlyphCollection() const;
+    
+    FT_Face _fontRef;
+    FT_Stroker _stroker;
+    FT_Encoding _encoding;
+
+    std::string _fontName;
+    bool _distanceFieldEnabled;
+    float _outlineSize;
+    int _lineHeight;
+    FontAtlas* _fontAtlas;
+
+    GlyphCollection _usedGlyphs;
+    std::string _customGlyphs;
+};
+
+/// @endcond
+
+NS_CC_END
+
+#endif

+ 821 - 0
3rdparty/cocos2dx/include/cocos/2d/CCGrid.cpp

@@ -0,0 +1,821 @@
+/****************************************************************************
+Copyright (c) 2009      On-Core
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCGrid.h"
+
+#include "base/ccMacros.h"
+#include "base/ccUtils.h"
+#include "2d/CCNode.h"
+#include "renderer/CCRenderer.h"
+#include "renderer/CCTexture2D.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/ProgramState.h"
+#include "renderer/backend/Device.h"
+#include "2d/CCCamera.h"
+
+
+NS_CC_BEGIN
+// implementation of GridBase
+
+bool GridBase::initWithSize(const Size& gridSize)
+{
+    return initWithSize(gridSize, Rect::ZERO);
+}
+
+bool GridBase::initWithSize(const cocos2d::Size &gridSize, const cocos2d::Rect &rect)
+{
+    Director *director = Director::getInstance();
+    Size s = director->getWinSizeInPixels();
+    
+    auto POTWide = ccNextPOT((unsigned int)s.width);
+    auto POTHigh = ccNextPOT((unsigned int)s.height);
+    
+    Texture2D *texture = new (std::nothrow) Texture2D();
+    if (! texture)
+    {
+        CCLOG("cocos2d: Grid: error creating texture");
+        return false;
+    }
+
+    backend::TextureDescriptor descriptor;
+    descriptor.width = POTWide;
+    descriptor.height = POTHigh;
+    descriptor.textureUsage = backend::TextureUsage::RENDER_TARGET;
+    descriptor.textureFormat = backend::PixelFormat::RGBA8888;
+    auto backendTexture = backend::Device::getInstance()->newTexture(descriptor);
+    texture->initWithBackendTexture(backendTexture);
+    backendTexture->release();
+    
+    initWithSize(gridSize, texture, false, rect);
+    
+    texture->release();
+    
+    return true;
+}
+
+bool GridBase::initWithSize(const Size& gridSize, Texture2D *texture, bool flipped)
+{
+    return initWithSize(gridSize, texture, flipped, Rect::ZERO);
+}
+
+bool GridBase::initWithSize(const Size& gridSize, Texture2D *texture, bool flipped, const Rect& rect)
+{
+    bool ret = true;
+    
+    _active = false;
+    _reuseGrid = 0;
+    _gridSize = gridSize;
+    
+    _texture = texture;
+    CC_SAFE_RETAIN(_texture);
+    _isTextureFlipped = flipped;
+
+#ifdef CC_USE_METAL
+    _isTextureFlipped = !flipped;
+#endif
+    
+    if (rect.equals(Rect::ZERO)) {
+        auto size = _texture->getContentSize();
+        _gridRect.setRect(0, 0, size.width, size.height);
+    }
+    else{
+        _gridRect = rect;
+    }
+    _step.x = _gridRect.size.width/_gridSize.width;
+    _step.y = _gridRect.size.height/_gridSize.height;
+
+    auto& pipelineDescriptor = _drawCommand.getPipelineDescriptor();
+    CC_SAFE_RELEASE(_programState);
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE);
+    _programState = new (std::nothrow) backend::ProgramState(program);
+    pipelineDescriptor.programState = _programState;
+    _mvpMatrixLocation = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix");
+    _textureLocation = pipelineDescriptor.programState->getUniformLocation("u_texture");
+    
+#define VERTEX_POSITION_SIZE 3
+#define VERTEX_TEXCOORD_SIZE 2
+    uint32_t texcoordOffset = (VERTEX_POSITION_SIZE)*sizeof(float);
+    uint32_t totalSize = (VERTEX_POSITION_SIZE+VERTEX_TEXCOORD_SIZE)*sizeof(float);
+    auto vertexLayout = _programState->getVertexLayout();
+    const auto& attributeInfo = _programState->getProgram()->getActiveAttributes();
+    auto iter = attributeInfo.find("a_position");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT3, 0, false);
+    }
+    iter = attributeInfo.find("a_texCoord");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_texCoord", iter->second.location, backend::VertexFormat::FLOAT2, texcoordOffset, false);
+    }
+    vertexLayout->setLayout(totalSize);
+
+    calculateVertexPoints();
+    updateBlendState();
+    return ret;
+}
+
+void GridBase::updateBlendState()
+{
+    if (! _texture || ! _texture->hasPremultipliedAlpha())
+    {
+        _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
+    }
+    else
+    {
+        _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+    }
+}
+
+GridBase::~GridBase()
+{
+    CCLOGINFO("deallocing GridBase: %p", this);
+
+    //TODO: ? why 2.0 comments this line:        setActive(false);
+    CC_SAFE_RELEASE(_texture);
+    
+    CC_SAFE_RELEASE(_programState);
+}
+
+// properties
+void GridBase::setActive(bool active)
+{
+    _active = active;
+    if (! active)
+    {
+        Director *pDirector = Director::getInstance();
+        Director::Projection proj = pDirector->getProjection();
+        pDirector->setProjection(proj);
+    }
+}
+
+void GridBase::setTextureFlipped(bool flipped)
+{
+    if (_isTextureFlipped != flipped)
+    {
+        _isTextureFlipped = flipped;
+        calculateVertexPoints();
+    }
+}
+
+void GridBase::set2DProjection()
+{
+    Director *director = Director::getInstance();
+    Size    size = director->getWinSizeInPixels();
+
+    director->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+
+    Mat4 orthoMatrix;
+    Mat4::createOrthographicOffCenter(0, size.width, 0, size.height, -1, 1, &orthoMatrix);
+    director->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, orthoMatrix);
+
+    director->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+}
+
+void GridBase::setGridRect(const cocos2d::Rect &rect)
+{
+    _gridRect = rect;
+}
+
+void GridBase::beforeDraw()
+{
+    // save projection
+    Director *director = Director::getInstance();
+
+    auto renderer = director->getRenderer();
+
+    renderer->addCommand(&_groupCommand);
+    renderer->pushGroup(_groupCommand.getRenderQueueID());
+
+    _beforeDrawCommand.func = [=]() -> void {
+        _directorProjection = director->getProjection();
+        set2DProjection();
+        Size    size = director->getWinSizeInPixels();
+        renderer->setViewPort(0, 0, (unsigned int)size.width, (unsigned int)size.height);
+
+        RenderTargetFlag flags = RenderTargetFlag::COLOR;
+        _oldColorAttachment = renderer->getColorAttachment();
+        _oldDepthAttachment = renderer->getDepthAttachment();
+        _oldStencilAttachment = renderer->getStencilAttachment();
+        _oldRenderTargetFlag = renderer->getRenderTargetFlag();
+
+        renderer->setRenderTarget(flags, _texture, nullptr, nullptr);
+    };
+    renderer->addCommand(&_beforeDrawCommand);
+    renderer->clear(ClearFlag::COLOR, _clearColor, 1, 0, 0.0);
+}
+
+void GridBase::afterDraw(cocos2d::Node * /*target*/)
+{
+    // restore projection
+    Director *director = Director::getInstance();
+    auto renderer = director->getRenderer();
+
+    _afterDrawCommand.func = [=]() -> void {
+        director->setProjection(_directorProjection);
+        const auto& vp = Camera::getDefaultViewport();
+        renderer->setViewPort(vp.x, vp.y, vp.w, vp.h);
+        renderer->setRenderTarget(_oldRenderTargetFlag, _oldColorAttachment, _oldDepthAttachment, _oldStencilAttachment);
+    };
+    renderer->addCommand(&_afterDrawCommand);
+
+    renderer->popGroup();
+
+//    if (target->getCamera()->isDirty())
+//    {
+//        Vec2 offset = target->getAnchorPointInPoints();
+//
+//        //
+//        // FIXME: Camera should be applied in the AnchorPoint
+//        //
+//        kmGLTranslatef(offset.x, offset.y, 0);
+//        target->getCamera()->locate();
+//        kmGLTranslatef(-offset.x, -offset.y, 0);
+//    }
+
+    // restore projection for default FBO .fixed bug #543 #544
+    //TODO:         Director::getInstance()->setProjection(Director::getInstance()->getProjection());
+    //TODO:         Director::getInstance()->applyOrientation();
+    _beforeBlitCommand.func = [=]() -> void {
+        beforeBlit();
+    };
+    renderer->addCommand(&_beforeBlitCommand);
+
+    blit();
+
+    _afterBlitCommand.func = [=]() -> void {
+        afterBlit();
+    };
+    renderer->addCommand(&_afterBlitCommand);
+}
+
+// implementation of Grid3D
+
+Grid3D* Grid3D::create(const Size& gridSize)
+{
+    Grid3D *ret= new (std::nothrow) Grid3D();
+
+    if (ret)
+    {
+        if (ret->initWithSize(gridSize))
+        {
+            ret->autorelease();
+        }
+        else
+        {
+            delete ret;
+            ret = nullptr;
+        }
+    }
+
+    return ret;
+}
+
+Grid3D* Grid3D::create(const Size& gridSize, const Rect& rect)
+{
+    Grid3D *ret= new (std::nothrow) Grid3D();
+    
+    if (ret)
+    {
+        if (ret->initWithSize(gridSize, rect))
+        {
+            ret->autorelease();
+        }
+        else
+        {
+            delete ret;
+            ret = nullptr;
+        }
+    }
+    
+    return ret;
+}
+
+Grid3D* Grid3D::create(const Size& gridSize, Texture2D *texture, bool flipped)
+{
+    Grid3D *ret= new (std::nothrow) Grid3D();
+    
+    if (ret)
+    {
+        if (ret->initWithSize(gridSize, texture, flipped))
+        {
+            ret->autorelease();
+        }
+        else
+        {
+            delete ret;
+            ret = nullptr;
+        }
+    }
+    
+    return ret;
+}
+
+Grid3D* Grid3D::create(const Size& gridSize, Texture2D *texture, bool flipped, const Rect& rect)
+{
+    Grid3D *ret= new (std::nothrow) Grid3D();
+    
+    if (ret)
+    {
+        if (ret->initWithSize(gridSize, texture, flipped, rect))
+        {
+            ret->autorelease();
+        }
+        else
+        {
+            delete ret;
+            ret = nullptr;
+        }
+    }
+    
+    return ret;
+}
+
+
+Grid3D::Grid3D()
+{
+}
+
+Grid3D::~Grid3D()
+{
+    CC_SAFE_FREE(_texCoordinates);
+    CC_SAFE_FREE(_vertices);
+    CC_SAFE_FREE(_indices);
+    CC_SAFE_FREE(_originalVertices);
+    CC_SAFE_FREE(_vertexBuffer);
+}
+
+void Grid3D::beforeBlit()
+{
+    auto renderer = Director::getInstance()->getRenderer();
+
+    if(_needDepthTestForBlit)
+    {
+        _oldDepthTest = renderer->getDepthTest();
+        renderer->setDepthTest(true);
+
+        _oldDepthWrite = renderer->getDepthWrite();
+        renderer->setDepthWrite(true);
+    }
+}
+
+void Grid3D::afterBlit()
+{
+    auto renderer = Director::getInstance()->getRenderer();
+    if(_needDepthTestForBlit)
+    {
+       renderer->setDepthTest(_oldDepthTest);
+       renderer->setDepthWrite(_oldDepthWrite);
+    }
+}
+
+void Grid3D::blit()
+{
+    updateVertexBuffer();
+    _drawCommand.init(0, _blendFunc);
+    Director::getInstance()->getRenderer()->addCommand(&_drawCommand);
+    cocos2d::Mat4 projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    auto programState = _drawCommand.getPipelineDescriptor().programState;
+    programState->setUniform(_mvpMatrixLocation, projectionMat.m, sizeof(projectionMat.m));
+    programState->setTexture(_textureLocation, 0, _texture->getBackendTexture());
+}
+
+void Grid3D::calculateVertexPoints()
+{
+    float width = (float)_texture->getPixelsWide();
+    float height = (float)_texture->getPixelsHigh();
+    float imageH = _texture->getContentSizeInPixels().height;
+
+    int x = 0, y = 0, i = 0;
+    CC_SAFE_FREE(_vertices);
+    CC_SAFE_FREE(_originalVertices);
+    CC_SAFE_FREE(_texCoordinates);
+    CC_SAFE_FREE(_vertexBuffer);
+    CC_SAFE_FREE(_indices);
+
+    size_t numOfPoints = static_cast<size_t>((_gridSize.width+1) * (_gridSize.height+1));
+
+    _vertices = malloc(numOfPoints * sizeof(Vec3));
+    _originalVertices = malloc(numOfPoints * sizeof(Vec3));
+    _texCoordinates = malloc(numOfPoints * sizeof(Vec2));
+    _vertexBuffer = malloc(numOfPoints * (sizeof(Vec3) + sizeof(Vec2)));
+    _indices = (unsigned short*)malloc(static_cast<size_t>(_gridSize.width * _gridSize.height * sizeof(unsigned short) * 6));
+
+    float *vertArray = (float*)_vertices;
+    float *texArray = (float*)_texCoordinates;
+    unsigned short *idxArray = _indices;
+
+    for (x = 0; x < _gridSize.width; ++x)
+    {
+        for (y = 0; y < _gridSize.height; ++y)
+        {
+            int idx = (int)(y * _gridSize.width) + x;
+
+            float x1 = x * _step.x + _gridRect.origin.x;
+            float x2 = x1 + _step.x;
+            float y1 = y * _step.y + _gridRect.origin.y;
+            float y2= y1 + _step.y;
+
+            unsigned short a = (unsigned short)(x * (_gridSize.height + 1) + y);
+            unsigned short b = (unsigned short)((x + 1) * (_gridSize.height + 1) + y);
+            unsigned short c = (unsigned short)((x + 1) * (_gridSize.height + 1) + (y + 1));
+            unsigned short d = (unsigned short)(x * (_gridSize.height + 1) + (y + 1));
+
+            unsigned short tempidx[6] = {a, b, d, b, c, d};
+
+            memcpy(&idxArray[6*idx], tempidx, 6*sizeof(unsigned short));
+
+            int l1[4] = {a*3, b*3, c*3, d*3};
+
+            Vec3 e(x1, y1, 0);
+            Vec3 f(x2, y1, 0);
+            Vec3 g(x2, y2, 0);
+            Vec3 h(x1, y2, 0);
+
+            Vec3 l2[4] = {e, f, g, h};
+
+            int tex1[4] = {a*2, b*2, c*2, d*2};
+            Vec2 Tex2F[4] = {Vec2(x1, y1), Vec2(x2, y1), Vec2(x2, y2), Vec2(x1, y2)};
+
+            for (i = 0; i < 4; ++i)
+            {
+                vertArray[l1[i]] = l2[i].x;
+                vertArray[l1[i] + 1] = l2[i].y;
+                vertArray[l1[i] + 2] = l2[i].z;
+
+                texArray[tex1[i]] = Tex2F[i].x / width;
+                if (_isTextureFlipped)
+                {
+                    texArray[tex1[i] + 1] = (imageH - Tex2F[i].y) / height;
+                }
+                else
+                {
+                    texArray[tex1[i] + 1] = Tex2F[i].y / height;
+                }
+            }
+        }
+    }
+
+    updateVertexAndTexCoordinate();
+
+    memcpy(_originalVertices, _vertices, static_cast<size_t>((_gridSize.width+1) * (_gridSize.height+1) * sizeof(Vec3)));
+}
+
+Vec3 Grid3D::getVertex(const Vec2& pos) const
+{
+    CCASSERT( pos.x == (unsigned int)pos.x && pos.y == (unsigned int) pos.y , "Numbers must be integers");
+    
+    int index = (int)(pos.x * (_gridSize.height+1) + pos.y) * 3;
+    float *vertArray = (float*)_vertices;
+
+    Vec3 vert(vertArray[index], vertArray[index+1], vertArray[index+2]);
+
+    return vert;
+}
+
+Vec3 Grid3D::getOriginalVertex(const Vec2& pos) const
+{
+    CCASSERT( pos.x == (unsigned int)pos.x && pos.y == (unsigned int) pos.y , "Numbers must be integers");
+    
+    int index = (int)(pos.x * (_gridSize.height+1) + pos.y) * 3;
+    float *vertArray = (float*)_originalVertices;
+
+    Vec3 vert(vertArray[index], vertArray[index+1], vertArray[index+2]);
+
+    return vert;
+}
+
+void Grid3D::setVertex(const Vec2& pos, const Vec3& vertex)
+{
+    CCASSERT( pos.x == (unsigned int)pos.x && pos.y == (unsigned int) pos.y , "Numbers must be integers");
+    int index = (int)(pos.x * (_gridSize.height + 1) + pos.y) * 3;
+    float *vertArray = (float*)_vertices;
+    vertArray[index] = vertex.x;
+    vertArray[index+1] = vertex.y;
+    vertArray[index+2] = vertex.z;
+}
+
+void Grid3D::reuse()
+{
+    if (_reuseGrid > 0)
+    {
+        memcpy(_originalVertices, _vertices, static_cast<size_t>((_gridSize.width+1) * (_gridSize.height+1) * sizeof(Vec3)));
+        --_reuseGrid;
+    }
+}
+
+void Grid3D::updateVertexBuffer()
+{
+    size_t numOfPoints = static_cast<size_t>((_gridSize.width+1) * (_gridSize.height+1));
+    auto tempVecPointer = (Vec3*)_vertices;
+    for (size_t i = 0; i < numOfPoints; ++i)
+    {
+        auto offset = i * (sizeof(Vec3) + sizeof(Vec2));
+        memcpy((char*)_vertexBuffer + offset, &tempVecPointer[i], sizeof(Vec3));
+    }
+    _drawCommand.updateVertexBuffer(_vertexBuffer, (unsigned int)(numOfPoints * sizeof(Vec3) + numOfPoints * sizeof(Vec2)) );
+
+    _drawCommand.updateIndexBuffer(_indices, static_cast<unsigned int>(_gridSize.width * _gridSize.height * 6 * sizeof(unsigned short)));
+}
+
+void Grid3D::updateVertexAndTexCoordinate()
+{
+    unsigned int numOfPoints = static_cast<unsigned int>((_gridSize.width+1) * (_gridSize.height+1));
+    auto tempVecPointer = (Vec3*)_vertices;
+    auto tempTexPointer = (Vec2*)_texCoordinates;
+    for (unsigned int i = 0; i < numOfPoints; ++i)
+    {
+        auto offset = i * (sizeof(Vec3) + sizeof(Vec2));
+        memcpy((char*)_vertexBuffer + offset, &tempVecPointer[i], sizeof(Vec3));
+        memcpy((char*)_vertexBuffer + offset + sizeof(Vec3), &tempTexPointer[i], sizeof(Vec2));
+    }
+    _drawCommand.createVertexBuffer((unsigned int)(sizeof(Vec3) + sizeof(Vec2)), numOfPoints, CustomCommand::BufferUsage::DYNAMIC);
+    _drawCommand.updateVertexBuffer(_vertexBuffer, numOfPoints * sizeof(Vec3) + numOfPoints * sizeof(Vec2));
+
+    unsigned int capacity = (unsigned int)(_gridSize.width * _gridSize.height) * 6;
+    _drawCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, capacity, CustomCommand::BufferUsage::DYNAMIC);
+    _drawCommand.updateIndexBuffer(_indices, capacity * sizeof(unsigned short));
+}
+
+// implementation of TiledGrid3D
+
+TiledGrid3D::~TiledGrid3D()
+{
+    CC_SAFE_FREE(_texCoordinates);
+    CC_SAFE_FREE(_vertices);
+    CC_SAFE_FREE(_originalVertices);
+    CC_SAFE_FREE(_indices);
+}
+
+TiledGrid3D* TiledGrid3D::create(const Size& gridSize)
+{
+    TiledGrid3D *ret= new (std::nothrow) TiledGrid3D();
+
+    if (ret)
+    {
+        if (ret->initWithSize(gridSize))
+        {
+            ret->autorelease();
+        }
+        else
+        {
+            delete ret;
+            ret = nullptr;
+        }
+    }
+
+    return ret;
+}
+
+TiledGrid3D* TiledGrid3D::create(const Size& gridSize, const Rect& rect)
+{
+    TiledGrid3D *ret= new (std::nothrow) TiledGrid3D();
+    
+    if (ret)
+    {
+        if (ret->initWithSize(gridSize, rect))
+        {
+            ret->autorelease();
+        }
+        else
+        {
+            delete ret;
+            ret = nullptr;
+        }
+    }
+    
+    return ret;
+}
+
+TiledGrid3D* TiledGrid3D::create(const Size& gridSize, Texture2D *texture, bool flipped, const Rect& rect)
+{
+    TiledGrid3D *ret= new (std::nothrow) TiledGrid3D();
+    
+    if (ret)
+    {
+        if (ret->initWithSize(gridSize, texture, flipped, rect))
+        {
+            ret->autorelease();
+        }
+        else
+        {
+            delete ret;
+            ret = nullptr;
+        }
+    }
+    
+    return ret;
+}
+
+TiledGrid3D* TiledGrid3D::create(const Size& gridSize, Texture2D *texture, bool flipped)
+{
+    TiledGrid3D *ret= new (std::nothrow) TiledGrid3D();
+    
+    if (ret)
+    {
+        if (ret->initWithSize(gridSize, texture, flipped))
+        {
+            ret->autorelease();
+        }
+        else
+        {
+            delete ret;
+            ret = nullptr;
+        }
+    }
+    
+    return ret;
+}
+
+void TiledGrid3D::blit()
+{
+    updateVertexBuffer();
+    Director::getInstance()->getRenderer()->addCommand(&_drawCommand);
+    cocos2d::Mat4 projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    auto programState = _drawCommand.getPipelineDescriptor().programState;
+    programState->setUniform(_mvpMatrixLocation, projectionMat.m, sizeof(projectionMat.m));
+    programState->setTexture(_textureLocation, 0, _texture->getBackendTexture());
+}
+
+void TiledGrid3D::calculateVertexPoints()
+{
+    float width = (float)_texture->getPixelsWide();
+    float height = (float)_texture->getPixelsHigh();
+    float imageH = _texture->getContentSizeInPixels().height;
+    
+    int numQuads = (int)(_gridSize.width * _gridSize.height);
+    CC_SAFE_FREE(_vertices);
+    CC_SAFE_FREE(_originalVertices);
+    CC_SAFE_FREE(_texCoordinates);
+    CC_SAFE_FREE(_indices);
+    CC_SAFE_FREE(_vertexBuffer);
+
+    _vertices = malloc(numQuads*4*sizeof(Vec3));
+    _originalVertices = malloc(numQuads*4*sizeof(Vec3));
+    _texCoordinates = malloc(numQuads*4*sizeof(Vec2));
+    _indices = (unsigned short*)malloc(numQuads*6*sizeof(unsigned short));
+    _vertexBuffer = malloc(numQuads * 4 * (sizeof(Vec3) + sizeof(Vec2) ) );
+
+    float *vertArray = (float*)_vertices;
+    float *texArray = (float*)_texCoordinates;
+    unsigned short *idxArray = _indices;
+    
+
+    for (int x = 0; x < _gridSize.width; ++x)
+    {
+        for (int y = 0; y < _gridSize.height; ++y)
+        {
+            float x1 = x * _step.x + _gridRect.origin.x;
+            float x2 = x1 + _step.x;
+            float y1 = y * _step.y + _gridRect.origin.y;
+            float y2 = y1 + _step.y;
+            
+            *vertArray++ = x1;
+            *vertArray++ = y1;
+            *vertArray++ = 0;
+            *vertArray++ = x2;
+            *vertArray++ = y1;
+            *vertArray++ = 0;
+            *vertArray++ = x1;
+            *vertArray++ = y2;
+            *vertArray++ = 0;
+            *vertArray++ = x2;
+            *vertArray++ = y2;
+            *vertArray++ = 0;
+            
+            float newY1 = y1;
+            float newY2 = y2;
+            
+            if (_isTextureFlipped) 
+            {
+                newY1 = imageH - y1;
+                newY2 = imageH - y2;
+            }
+
+            *texArray++ = x1 / width;
+            *texArray++ = newY1 / height;
+            *texArray++ = x2 / width;
+            *texArray++ = newY1 / height;
+            *texArray++ = x1 / width;
+            *texArray++ = newY2 / height;
+            *texArray++ = x2 / width;
+            *texArray++ = newY2 / height;
+        }
+    }
+    
+    for (int x = 0; x < numQuads; x++)
+    {
+        idxArray[x*6+0] = (unsigned short)(x * 4 + 0);
+        idxArray[x*6+1] = (unsigned short)(x * 4 + 1);
+        idxArray[x*6+2] = (unsigned short)(x * 4 + 2);
+        
+        idxArray[x*6+3] = (unsigned short)(x * 4 + 1);
+        idxArray[x*6+4] = (unsigned short)(x * 4 + 2);
+        idxArray[x*6+5] = (unsigned short)(x * 4 + 3);
+    }
+
+    updateVertexAndTexCoordinate();
+    memcpy(_originalVertices, _vertices, numQuads * 12 * sizeof(float));
+}
+
+void TiledGrid3D::setTile(const Vec2& pos, const Quad3& coords)
+{
+    CCASSERT( pos.x == (unsigned int)pos.x && pos.y == (unsigned int) pos.y , "Numbers must be integers");
+    int idx = (int)(_gridSize.height * pos.x + pos.y) * 4 * 3;
+    float *vertArray = (float*)_vertices;
+    memcpy(&vertArray[idx], &coords, sizeof(Quad3));
+}
+
+Quad3 TiledGrid3D::getOriginalTile(const Vec2& pos) const
+{
+    CCASSERT( pos.x == (unsigned int)pos.x && pos.y == (unsigned int) pos.y , "Numbers must be integers");
+    int idx = (int)(_gridSize.height * pos.x + pos.y) * 4 * 3;
+    float *vertArray = (float*)_originalVertices;
+
+    Quad3 ret;
+    memcpy(&ret, &vertArray[idx], sizeof(Quad3));
+
+    return ret;
+}
+
+Quad3 TiledGrid3D::getTile(const Vec2& pos) const
+{
+    CCASSERT( pos.x == (unsigned int)pos.x && pos.y == (unsigned int) pos.y , "Numbers must be integers");
+    int idx = (int)(_gridSize.height * pos.x + pos.y) * 4 * 3;
+    float *vertArray = (float*)_vertices;
+
+    Quad3 ret;
+    memcpy(&ret, &vertArray[idx], sizeof(Quad3));
+
+    return ret;
+}
+
+void TiledGrid3D::reuse()
+{
+    if (_reuseGrid > 0)
+    {
+        int numQuads = (int)(_gridSize.width * _gridSize.height);
+
+        memcpy(_originalVertices, _vertices, numQuads * 12 * sizeof(float));
+        --_reuseGrid;
+    }
+}
+
+void TiledGrid3D::updateVertexBuffer()
+{
+    size_t gradSize = static_cast<size_t>(_gridSize.width * _gridSize.height);
+    size_t numOfPoints = gradSize * 4;
+    auto tempVecPointer = (Vec3*)_vertices;
+    for (size_t i = 0; i < numOfPoints; ++i)
+    {
+        auto offset = i * (sizeof(Vec3) + sizeof(Vec2));
+        memcpy((char*)_vertexBuffer + offset, &tempVecPointer[i], sizeof(Vec3));
+    }
+    _drawCommand.updateVertexBuffer(_vertexBuffer, (unsigned int)(numOfPoints * sizeof(Vec3) + numOfPoints * sizeof(Vec2)) );
+
+    _drawCommand.updateIndexBuffer(_indices, gradSize * 6 * sizeof(unsigned short));
+}
+
+void TiledGrid3D::updateVertexAndTexCoordinate()
+{
+    size_t gradSize = static_cast<size_t>(_gridSize.width * _gridSize.height);
+    auto numOfPoints = gradSize * 4;
+    auto tempVecPointer = (Vec3*)_vertices;
+    auto tempTexPointer = (Vec2*)_texCoordinates;
+    for (size_t i = 0; i < numOfPoints; ++i)
+    {
+        auto offset = i * (sizeof(Vec3) + sizeof(Vec2));
+        memcpy((char*)_vertexBuffer + offset, &tempVecPointer[i], sizeof(Vec3));
+        memcpy((char*)_vertexBuffer + offset + sizeof(Vec3), &tempTexPointer[i], sizeof(Vec2));
+    }
+    _drawCommand.createVertexBuffer((sizeof(Vec3) + sizeof(Vec2) ), numOfPoints, CustomCommand::BufferUsage::DYNAMIC);
+    _drawCommand.updateVertexBuffer(_vertexBuffer, numOfPoints * sizeof(Vec3) + numOfPoints * sizeof(Vec2));
+
+    _drawCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, gradSize * 6, CustomCommand::BufferUsage::DYNAMIC);
+    _drawCommand.updateIndexBuffer(_indices, gradSize * 6 * sizeof(unsigned short));
+}
+
+NS_CC_END

+ 307 - 0
3rdparty/cocos2dx/include/cocos/2d/CCGrid.h

@@ -0,0 +1,307 @@
+/****************************************************************************
+Copyright (c) 2009      On-Core
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#pragma once
+
+#include "base/CCRef.h"
+#include "base/ccTypes.h"
+#include "base/CCDirector.h"
+#include "renderer/CCGroupCommand.h"
+#include "renderer/CCCustomCommand.h"
+#include "renderer/CCCallbackCommand.h"
+
+NS_CC_BEGIN
+
+class Texture2D;
+// class Grabber;
+// class GLProgram;
+class Node;
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** Base class for Other grid.
+*/
+class CC_DLL GridBase : public Ref
+{
+public:
+    /**
+    Destructor.
+     * @js NA
+     * @lua NA
+     */
+    virtual ~GridBase();
+
+    /**@{
+     Interface for custom action when before or after draw.
+     @js NA
+     */
+    virtual void beforeBlit() {}
+    virtual void afterBlit() {}
+    /**@}*/
+
+    /**Interface used to blit the texture with grid to screen.*/
+    virtual void blit() = 0;
+    /**Interface, Reuse the grid vertices.*/
+    virtual void reuse() = 0;
+    /**Interface, Calculate the vertices used for the blit.*/
+    virtual void calculateVertexPoints() = 0;
+
+    /**@{
+     Init the Grid base.
+     @param gridSize the size of the grid.
+     @param texture The texture used for grab.
+     @param flipped whether or not the grab texture should be flip by Y or not.
+     @param rect The effective grid rect.
+    */
+    bool initWithSize(const Size& gridSize);
+    bool initWithSize(const Size& gridSize, const Rect& rect);
+    bool initWithSize(const Size& gridSize, Texture2D *texture, bool flipped);
+    bool initWithSize(const Size& gridSize, Texture2D *texture, bool flipped, const Rect& rect);
+
+    /**@}*/
+    /** @{
+    Getter and setter of the active state of the grid.
+    */
+    bool isActive() const { return _active; }
+    void setActive(bool active);
+    /**@}*/
+
+    /** Get number of times that the grid will be reused. */
+    int getReuseGrid() const { return _reuseGrid; }
+    /** Set number of times that the grid will be reused. */
+    void setReuseGrid(int reuseGrid) { _reuseGrid = reuseGrid; }
+
+    /** Size of the grid. */
+    const Size& getGridSize() const { return _gridSize; }
+    /**Set the size of the grid.*/
+    void setGridSize(const Size& gridSize) { _gridSize = gridSize; }
+
+    /** Pixels between the grids. */
+    const Vec2& getStep() const { return _step; }
+    /**Get the pixels between the grids.*/
+    void setStep(const Vec2& step) { _step = step; }
+
+    /** is texture flipped. */
+    bool isTextureFlipped() const { return _isTextureFlipped; }
+    /**Set the texture flipped or not.*/
+    void setTextureFlipped(bool flipped);
+    
+    /**@{
+     Init and reset the status when render effects by using the grid.
+     */
+    void beforeDraw();
+    void afterDraw(Node *target);
+    /**@}*/
+    
+    /**Change projection to 2D for grabbing.*/
+    void set2DProjection();
+    
+    /**
+     * @brief Set the effect grid rect.
+     * @param rect The effect grid rect.
+     */
+    void setGridRect(const Rect& rect);
+    /**
+     * @brief Get the effect grid rect.
+     * @return Return the effect grid rect.
+     */
+    const Rect& getGridRect() const { return _gridRect; }
+
+protected:
+    void updateBlendState();
+    
+    bool _active = false;
+    int  _reuseGrid = 0;
+    Size _gridSize;
+    Texture2D *_texture = nullptr;
+    Vec2 _step;
+    bool _isTextureFlipped = false;
+    Director::Projection _directorProjection = Director::Projection::_2D;
+    Rect _gridRect;
+
+    Color4F _clearColor = {0, 0, 0, 0};
+
+    GroupCommand _groupCommand;
+    CustomCommand _drawCommand;
+    CallbackCommand _beforeDrawCommand;
+    CallbackCommand _afterDrawCommand;
+    CallbackCommand _beforeBlitCommand;
+    CallbackCommand _afterBlitCommand;
+    RenderTargetFlag _oldRenderTargetFlag;
+    Texture2D* _oldColorAttachment = nullptr;
+    Texture2D* _oldDepthAttachment = nullptr;
+    Texture2D* _oldStencilAttachment = nullptr;
+    
+    backend::UniformLocation _mvpMatrixLocation;
+    backend::UniformLocation _textureLocation;
+    backend::ProgramState* _programState = nullptr;
+    
+    BlendFunc  _blendFunc;
+};
+
+/**
+ Grid3D is a 3D grid implementation. Each vertex has 3 dimensions: x,y,z
+ */
+class CC_DLL Grid3D : public GridBase
+{
+public:
+    /** create one Grid. */
+    static Grid3D* create(const Size& gridSize);
+    /** create one Grid. */
+    static Grid3D* create(const Size& gridSize, const Rect& rect);
+    /** create one Grid. */
+    static Grid3D* create(const Size& gridSize, Texture2D *texture, bool flipped);
+    /** create one Grid. */
+    static Grid3D* create(const Size& gridSize, Texture2D *texture, bool flipped, const Rect& rect);
+    /**
+     Constructor.
+     * @js ctor
+     */
+    Grid3D();
+    /**
+     Destructor.
+     * @js NA
+     * @lua NA
+     */
+    ~Grid3D();
+
+    /** Returns the vertex at a given position.
+     * @js NA
+     * @lua NA
+     */
+    Vec3 getVertex(const Vec2& pos) const;
+
+    /** Returns the original (non-transformed) vertex at a given position.
+     * @js NA
+     * @lua NA
+     */
+    Vec3 getOriginalVertex(const Vec2& pos) const;
+
+    /** Sets a new vertex at a given position.
+     * @lua NA
+     */
+    void setVertex(const Vec2& pos, const Vec3& vertex);
+    /**@{
+     Implementations for interfaces in base class.
+     */
+    virtual void beforeBlit() override;
+    virtual void afterBlit() override;
+    virtual void blit() override;
+    virtual void reuse() override;
+    virtual void calculateVertexPoints() override;
+    /**@}*/
+    
+    /**@{
+     Getter and Setter for depth test state when blit.
+     @js NA
+     */
+    void setNeedDepthTestForBlit(bool neededDepthTest) { _needDepthTestForBlit = neededDepthTest; }
+    bool getNeedDepthTestForBlit() const { return _needDepthTestForBlit; }
+    /**@}*/
+protected:
+    void updateVertexBuffer();
+    void updateVertexAndTexCoordinate();
+
+    void* _vertexBuffer = nullptr;
+    void* _texCoordinates = nullptr;
+    void* _vertices = nullptr;
+    void* _originalVertices = nullptr;
+    unsigned short *_indices = nullptr;
+    bool _needDepthTestForBlit = false;
+    bool _oldDepthTest = false;
+    bool _oldDepthWrite = false;
+};
+
+/**
+ TiledGrid3D is a 3D grid implementation. It differs from Grid3D in that
+ the tiles can be separated from the grid.
+*/
+class CC_DLL TiledGrid3D : public GridBase
+{
+public:
+    /** Create one Grid. */
+    static TiledGrid3D* create(const Size& gridSize);
+    /** Create one Grid. */
+    static TiledGrid3D* create(const Size& gridSize, const Rect& rect);
+    /** Create one Grid. */
+    static TiledGrid3D* create(const Size& gridSize, Texture2D *texture, bool flipped);
+    /** Create one Grid. */
+    static TiledGrid3D* create(const Size& gridSize, Texture2D *texture, bool flipped, const Rect& rect);
+
+    /**@{
+     Implementations for interfaces in base class.
+     */
+    virtual void blit() override;
+    virtual void reuse() override;
+    virtual void calculateVertexPoints() override;
+    /**@}*/
+
+    /** Returns the tile at the given position.
+     * @js NA
+     * @lua NA
+     */
+    Quad3 getTile(const Vec2& pos) const;
+    /** Returns the original tile (untransformed) at the given position.
+     * @js NA
+     * @lua NA
+     */
+    Quad3 getOriginalTile(const Vec2& pos) const;
+
+    /** Sets a new tile.
+     * @lua NA
+     */
+    void setTile(const Vec2& pos, const Quad3& coords);
+
+protected:
+    /**
+     Constructor.
+     * @js ctor
+     */
+    TiledGrid3D() = default;
+    /**
+     Destructor.
+     * @js NA
+     * @lua NA
+     */
+    ~TiledGrid3D();
+
+    void updateVertexBuffer();
+    void updateVertexAndTexCoordinate();
+
+    void* _texCoordinates = nullptr;
+    void* _vertices = nullptr;
+    void* _originalVertices = nullptr;
+    unsigned short* _indices = nullptr;
+    void* _vertexBuffer = nullptr;
+};
+
+// end of effects group
+/// @}
+
+NS_CC_END

+ 2418 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLabel.cpp

@@ -0,0 +1,2418 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#include "2d/CCLabel.h"
+#include <algorithm>
+#include <stddef.h> // offsetof
+#include "base/ccTypes.h"
+#include "2d/CCFont.h"
+#include "2d/CCFontAtlasCache.h"
+#include "2d/CCFontAtlas.h"
+#include "2d/CCSprite.h"
+#include "2d/CCSpriteBatchNode.h"
+#include "2d/CCDrawNode.h"
+#include "2d/CCCamera.h"
+#include "base/ccUTF8.h"
+#include "base/ccMacros.h"
+#include "platform/CCFileUtils.h"
+#include "renderer/CCRenderer.h"
+#include "renderer/CCRenderCommand.h"
+#include "base/CCDirector.h"
+#include "base/CCEventListenerCustom.h"
+#include "base/CCEventDispatcher.h"
+#include "base/CCEventCustom.h"
+#include "base/ccUtils.h"
+#include "2d/CCFontFNT.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/ProgramState.h"
+
+NS_CC_BEGIN
+
+
+namespace {
+    void updateBlend(backend::BlendDescriptor &blendDescriptor, BlendFunc blendFunc)
+    {
+        blendDescriptor.blendEnabled = true;
+        if (blendFunc == BlendFunc::ALPHA_NON_PREMULTIPLIED)
+        {
+            blendDescriptor.sourceRGBBlendFactor = backend::BlendFactor::SRC_ALPHA;
+            blendDescriptor.destinationRGBBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
+            blendDescriptor.sourceAlphaBlendFactor = backend::BlendFactor::SRC_ALPHA;
+            blendDescriptor.destinationAlphaBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
+        }
+        else
+        {
+            blendDescriptor.sourceRGBBlendFactor = backend::BlendFactor::ONE;
+            blendDescriptor.destinationRGBBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
+            blendDescriptor.sourceAlphaBlendFactor = backend::BlendFactor::ONE;
+            blendDescriptor.destinationAlphaBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
+        }
+    }
+}
+
+/**
+ * LabelLetter used to update the quad in texture atlas without SpriteBatchNode.
+ */
+class LabelLetter : public Sprite
+{
+public:
+    LabelLetter()
+    {
+        _textureAtlas = nullptr;
+        _letterVisible = true;
+    }
+
+    static LabelLetter* createWithTexture(Texture2D *texture, const Rect& rect, bool rotated = false)
+    {
+        auto letter = new (std::nothrow) LabelLetter();
+        if (letter && letter->initWithTexture(texture, rect, rotated))
+        {
+            letter->Sprite::setVisible(false);
+            letter->autorelease();
+            return letter;
+        }
+        CC_SAFE_DELETE(letter);
+        return nullptr;
+    }
+    
+    CREATE_FUNC(LabelLetter);
+
+    virtual void updateTransform() override
+    {
+        if (isDirty())
+        {
+            _transformToBatch = getNodeToParentTransform();
+            Size &size = _rect.size;
+
+            float x1 = _offsetPosition.x;
+            float y1 = _offsetPosition.y;
+            float x2 = x1 + size.width;
+            float y2 = y1 + size.height;
+
+            // issue #17022: don't flip, again, the letter since they are flipped in sprite's code
+            //if (_flippedX) std::swap(x1, x2);
+            //if (_flippedY) std::swap(y1, y2);
+
+            float x = _transformToBatch.m[12];
+            float y = _transformToBatch.m[13];
+
+            float cr = _transformToBatch.m[0];
+            float sr = _transformToBatch.m[1];
+            float cr2 = _transformToBatch.m[5];
+            float sr2 = -_transformToBatch.m[4];
+            float ax = x1 * cr - y1 * sr2 + x;
+            float ay = x1 * sr + y1 * cr2 + y;
+
+            float bx = x2 * cr - y1 * sr2 + x;
+            float by = x2 * sr + y1 * cr2 + y;
+            float cx = x2 * cr - y2 * sr2 + x;
+            float cy = x2 * sr + y2 * cr2 + y;
+            float dx = x1 * cr - y2 * sr2 + x;
+            float dy = x1 * sr + y2 * cr2 + y;
+
+            _quad.bl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(ax), SPRITE_RENDER_IN_SUBPIXEL(ay), _positionZ);
+            _quad.br.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(bx), SPRITE_RENDER_IN_SUBPIXEL(by), _positionZ);
+            _quad.tl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(dx), SPRITE_RENDER_IN_SUBPIXEL(dy), _positionZ);
+            _quad.tr.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(cx), SPRITE_RENDER_IN_SUBPIXEL(cy), _positionZ);
+
+            if (_textureAtlas)
+            {
+                _textureAtlas->updateQuad(&_quad, _atlasIndex);
+            }
+
+            _recursiveDirty = false;
+            setDirty(false);
+        }
+
+        Node::updateTransform();
+    }
+
+    virtual void updateColor() override
+    {
+        if (_textureAtlas == nullptr)
+        {
+            return;
+        }
+
+        auto displayedOpacity = _displayedOpacity;
+        if(!_letterVisible)
+        {
+            displayedOpacity = 0.0f;
+        }
+        Color4B color4(_displayedColor.r, _displayedColor.g, _displayedColor.b, displayedOpacity);
+        // special opacity for premultiplied textures
+        if (_opacityModifyRGB)
+        {
+            color4.r *= displayedOpacity / 255.0f;
+            color4.g *= displayedOpacity / 255.0f;
+            color4.b *= displayedOpacity / 255.0f;
+        }
+        _quad.bl.colors = color4;
+        _quad.br.colors = color4;
+        _quad.tl.colors = color4;
+        _quad.tr.colors = color4;
+
+        _textureAtlas->updateQuad(&_quad, _atlasIndex);
+    }
+
+    void setVisible(bool visible) override
+    {
+        _letterVisible = visible;
+        updateColor();
+    }
+
+    bool isVisible() const override
+    {
+        return _letterVisible;
+    }
+    
+    //LabelLetter doesn't need to draw directly.
+    void draw(Renderer* /*renderer*/, const Mat4 & /*transform*/, uint32_t /*flags*/) override
+    {
+    }
+    
+private:
+    bool _letterVisible;
+};
+
+Label::BatchCommand::BatchCommand()
+{
+    textCommand.setDrawType(CustomCommand::DrawType::ELEMENT);
+    textCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
+    shadowCommand.setDrawType(CustomCommand::DrawType::ELEMENT);
+    shadowCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
+    outLineCommand.setDrawType(CustomCommand::DrawType::ELEMENT);
+    outLineCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
+}
+
+Label::BatchCommand::~BatchCommand()
+{
+    CC_SAFE_RELEASE_NULL(textCommand.getPipelineDescriptor().programState);
+    CC_SAFE_RELEASE_NULL(shadowCommand.getPipelineDescriptor().programState);
+    CC_SAFE_RELEASE_NULL(outLineCommand.getPipelineDescriptor().programState);
+}
+
+std::array<CustomCommand*, 3> Label::BatchCommand::getCommandArray()
+{
+    return std::array<CustomCommand*, 3>{&textCommand, &shadowCommand, &outLineCommand};
+}
+
+Label* Label::create()
+{
+    auto ret = new (std::nothrow) Label;
+
+    if (ret)
+    {
+        ret->autorelease();
+    }
+
+    return ret;
+}
+
+Label* Label::createWithSystemFont(const std::string& text, const std::string& font, float fontSize, const Size& dimensions /* = Size::ZERO */, TextHAlignment hAlignment /* = TextHAlignment::LEFT */, TextVAlignment vAlignment /* = TextVAlignment::TOP */)
+{
+    auto ret = new (std::nothrow) Label(hAlignment,vAlignment);
+
+    if (ret)
+    {
+        ret->setSystemFontName(font);
+        ret->setSystemFontSize(fontSize);
+        ret->setDimensions(dimensions.width, dimensions.height);
+        ret->setString(text);
+
+        ret->autorelease();
+
+        return ret;
+    }
+
+    return nullptr;
+}
+
+Label* Label::createWithTTF(const std::string& text, const std::string& fontFile, float fontSize, const Size& dimensions /* = Size::ZERO */, TextHAlignment hAlignment /* = TextHAlignment::LEFT */, TextVAlignment vAlignment /* = TextVAlignment::TOP */)
+{
+    auto ret = new (std::nothrow) Label(hAlignment,vAlignment);
+
+    if (ret && ret->initWithTTF(text, fontFile, fontSize, dimensions, hAlignment, vAlignment))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+Label* Label::createWithTTF(const TTFConfig& ttfConfig, const std::string& text, TextHAlignment hAlignment /* = TextHAlignment::CENTER */, int maxLineWidth /* = 0 */)
+{
+    auto ret = new (std::nothrow) Label(hAlignment);
+
+    if (ret && ret->initWithTTF(ttfConfig, text, hAlignment, maxLineWidth))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+Label* Label::createWithBMFont(const std::string& bmfontFilePath, const std::string& text,const TextHAlignment& hAlignment /* = TextHAlignment::LEFT */, int maxLineWidth /* = 0 */, const Vec2& imageOffset /* = Vec2::ZERO */)
+{
+    auto ret = new (std::nothrow) Label(hAlignment);
+
+    if (ret && ret->setBMFontFilePath(bmfontFilePath,imageOffset))
+    {
+        ret->setMaxLineWidth(maxLineWidth);
+        ret->setString(text);
+        ret->autorelease();
+
+        return ret;
+    }
+    
+    delete ret;
+    return nullptr;
+}
+
+Label* Label::createWithCharMap(const std::string& plistFile)
+{
+    auto ret = new (std::nothrow) Label();
+
+    if (ret && ret->setCharMap(plistFile))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    delete ret;
+    return nullptr;
+}
+
+Label* Label::createWithCharMap(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap)
+{
+    auto ret = new (std::nothrow) Label();
+
+    if (ret && ret->setCharMap(texture,itemWidth,itemHeight,startCharMap))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    delete ret;
+    return nullptr;
+}
+
+Label* Label::createWithCharMap(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap)
+{
+    auto ret = new (std::nothrow) Label();
+
+    if (ret && ret->setCharMap(charMapFile,itemWidth,itemHeight,startCharMap))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    delete ret;
+    return nullptr;
+}
+
+bool Label::setCharMap(const std::string& plistFile)
+{
+    auto newAtlas = FontAtlasCache::getFontAtlasCharMap(plistFile);
+
+    if (!newAtlas)
+    {
+        reset();
+        return false;
+    }
+
+    _currentLabelType = LabelType::CHARMAP;
+    setFontAtlas(newAtlas);
+
+    return true;
+}
+
+
+bool Label::initWithTTF(const std::string& text,
+                        const std::string& fontFilePath, float fontSize,
+                        const Size& dimensions,
+                        TextHAlignment /*hAlignment*/, TextVAlignment /*vAlignment*/)
+{
+    if (FileUtils::getInstance()->isFileExist(fontFilePath))
+    {
+        TTFConfig ttfConfig(fontFilePath, fontSize, GlyphCollection::DYNAMIC);
+        if (setTTFConfig(ttfConfig))
+        {
+            setDimensions(dimensions.width, dimensions.height);
+            setString(text);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool Label::initWithTTF(const TTFConfig& ttfConfig, const std::string& text, TextHAlignment /*hAlignment*/, int maxLineWidth)
+{
+    if (FileUtils::getInstance()->isFileExist(ttfConfig.fontFilePath) && setTTFConfig(ttfConfig))
+    {
+        setMaxLineWidth(maxLineWidth);
+        setString(text);
+        return true;
+    }
+    return false;
+}
+
+bool Label::setCharMap(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap)
+{
+    auto newAtlas = FontAtlasCache::getFontAtlasCharMap(texture,itemWidth,itemHeight,startCharMap);
+
+    if (!newAtlas)
+    {
+        reset();
+        return false;
+    }
+
+    _currentLabelType = LabelType::CHARMAP;
+    setFontAtlas(newAtlas);
+
+    return true;
+}
+
+bool Label::setCharMap(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap)
+{
+    auto newAtlas = FontAtlasCache::getFontAtlasCharMap(charMapFile,itemWidth,itemHeight,startCharMap);
+
+    if (!newAtlas)
+    {
+        reset();
+        return false;
+    }
+
+    _currentLabelType = LabelType::CHARMAP;
+    setFontAtlas(newAtlas);
+
+    return true;
+}
+
+Label::Label(TextHAlignment hAlignment /* = TextHAlignment::LEFT */, 
+             TextVAlignment vAlignment /* = TextVAlignment::TOP */)
+: _textSprite(nullptr)
+, _shadowNode(nullptr)
+, _fontAtlas(nullptr)
+, _reusedLetter(nullptr)
+, _horizontalKernings(nullptr)
+, _boldEnabled(false)
+, _underlineNode(nullptr)
+, _strikethroughEnabled(false)
+{
+    setAnchorPoint(Vec2::ANCHOR_MIDDLE);
+    reset();
+    _hAlignment = hAlignment;
+    _vAlignment = vAlignment;
+
+#if CC_LABEL_DEBUG_DRAW
+    _debugDrawNode = DrawNode::create();
+    addChild(_debugDrawNode);
+#endif
+
+    _purgeTextureListener = EventListenerCustom::create(FontAtlas::CMD_PURGE_FONTATLAS, [this](EventCustom* event){
+        if (_fontAtlas && _currentLabelType == LabelType::TTF && event->getUserData() == _fontAtlas)
+        {
+            for (auto&& it : _letters)
+            {
+                it.second->setTexture(nullptr);
+            }
+            _batchNodes.clear();
+            _batchCommands.clear();
+
+            if (_fontAtlas)
+            {
+                FontAtlasCache::releaseFontAtlas(_fontAtlas);
+            }
+        }
+    });
+    _eventDispatcher->addEventListenerWithFixedPriority(_purgeTextureListener, 1);
+    
+    _resetTextureListener = EventListenerCustom::create(FontAtlas::CMD_RESET_FONTATLAS, [this](EventCustom* event){
+        if (_fontAtlas && _currentLabelType == LabelType::TTF && event->getUserData() == _fontAtlas)
+        {
+            _fontAtlas = nullptr;
+            auto lineHeight = _lineHeight;
+            this->setTTFConfig(_fontConfig);
+            if (_currentLabelType != LabelType::STRING_TEXTURE)
+            {
+                setLineHeight(lineHeight);
+            }
+            for (auto&& it : _letters)
+            {
+                getLetter(it.first);
+            }
+        }
+    });
+    _eventDispatcher->addEventListenerWithFixedPriority(_resetTextureListener, 2);
+}
+
+Label::~Label()
+{
+    delete [] _horizontalKernings;
+
+    if (_fontAtlas)
+    {
+        Node::removeAllChildrenWithCleanup(true);
+        CC_SAFE_RELEASE_NULL(_reusedLetter);
+        _batchNodes.clear();
+        FontAtlasCache::releaseFontAtlas(_fontAtlas);
+    }
+    _batchCommands.clear();
+    _eventDispatcher->removeEventListener(_purgeTextureListener);
+    _eventDispatcher->removeEventListener(_resetTextureListener);
+
+    CC_SAFE_RELEASE_NULL(_textSprite);
+    CC_SAFE_RELEASE_NULL(_shadowNode);
+}
+
+void Label::reset()
+{
+    CC_SAFE_RELEASE_NULL(_textSprite);
+    CC_SAFE_RELEASE_NULL(_shadowNode);
+    Node::removeAllChildrenWithCleanup(true);
+    CC_SAFE_RELEASE_NULL(_reusedLetter);
+    _letters.clear();
+    _batchNodes.clear();
+    _batchCommands.clear();
+    _lettersInfo.clear();
+    if (_fontAtlas)
+    {
+        FontAtlasCache::releaseFontAtlas(_fontAtlas);
+        _fontAtlas = nullptr;
+    }
+
+    _currentLabelType = LabelType::STRING_TEXTURE;
+    _currLabelEffect = LabelEffect::NORMAL;
+    _contentDirty = false;
+    _numberOfLines = 0;
+    _lengthOfString = 0;
+    _utf32Text.clear();
+    _utf8Text.clear();
+
+    TTFConfig temp;
+    _fontConfig = temp;
+    _outlineSize = 0.f;
+    _bmFontPath = "";
+    _systemFontDirty = false;
+    _systemFont = "Helvetica";
+    _systemFontSize = CC_DEFAULT_FONT_LABEL_SIZE;
+
+    if (_horizontalKernings)
+    {
+        delete[] _horizontalKernings;
+        _horizontalKernings = nullptr;
+    }
+    _additionalKerning = 0.f;
+    _lineHeight = 0.f;
+    _lineSpacing = 0.f;
+    _maxLineWidth = 0.f;
+    _labelDimensions.width = 0.f;
+    _labelDimensions.height = 0.f;
+    _labelWidth = 0.f;
+    _labelHeight = 0.f;
+    _lineBreakWithoutSpaces = false;
+    _hAlignment = TextHAlignment::LEFT;
+    _vAlignment = TextVAlignment::TOP;
+
+    _effectColorF = Color4F::BLACK;
+    _textColor = Color4B::WHITE;
+    _textColorF = Color4F::WHITE;
+    setColor(Color3B::WHITE);
+
+    _shadowDirty = false;
+    _shadowEnabled = false;
+    _shadowBlurRadius = 0.f;
+
+    _uniformEffectColor = -1;
+    _uniformEffectType = -1;
+    _uniformTextColor = -1;
+
+    _useDistanceField = false;
+    _useA8Shader = false;
+    _clipEnabled = false;
+    _blendFuncDirty = false;
+    _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+    _isOpacityModifyRGB = false;
+    _insideBounds = true;
+    _enableWrap = true;
+    _bmFontSize = -1;
+    _bmfontScale = 1.0f;
+    _overflow = Overflow::NONE;
+    _originalFontSize = 0.0f;
+    _boldEnabled = false;
+    if (_underlineNode)
+    {
+        removeChild(_underlineNode);
+        _underlineNode = nullptr;
+    }
+    _strikethroughEnabled = false;
+    setRotationSkewX(0);        // reverse italics
+}
+
+//  ETC1 ALPHA supports, for LabelType::BMFONT & LabelType::CHARMAP
+static Texture2D* _getTexture(Label* label)
+{
+    auto fontAtlas = label->getFontAtlas();
+    Texture2D* texture = nullptr;
+    if (fontAtlas != nullptr) {
+        auto textures = fontAtlas->getTextures();
+        if(!textures.empty()) {
+            texture = textures.begin()->second;
+        }
+    }
+    return texture;
+}
+
+void Label::setVertexLayout(PipelineDescriptor& pipelineDescriptor)
+{
+    auto vertexLayout = _programState->getVertexLayout();
+    ///a_position
+    vertexLayout->setAttribute(backend::ATTRIBUTE_NAME_POSITION,
+                        _programState->getAttributeLocation(backend::Attribute::POSITION),
+                        backend::VertexFormat::FLOAT3,
+                        0,
+                        false);
+    
+    ///a_texCoord
+    vertexLayout->setAttribute(backend::ATTRIBUTE_NAME_TEXCOORD,
+                        _programState->getAttributeLocation(backend::Attribute::TEXCOORD),
+                        backend::VertexFormat::FLOAT2,
+                        offsetof(V3F_C4B_T2F, texCoords),
+                        false);
+    
+    ///a_color
+    vertexLayout->setAttribute(backend::ATTRIBUTE_NAME_COLOR,
+                        _programState->getAttributeLocation(backend::Attribute::COLOR),
+                        backend::VertexFormat::UBYTE4, 
+                        offsetof(V3F_C4B_T2F, colors), 
+                        true);
+    vertexLayout->setLayout(sizeof(V3F_C4B_T2F));
+}
+
+void Label::setProgramState(backend::ProgramState *programState)
+{
+    if (_programState != programState)
+    {
+        CC_SAFE_RELEASE_NULL(_programState);
+        _programState = programState;
+        CC_SAFE_RETAIN(programState);
+    }
+    updateUniformLocations();
+    for (auto &batch : _batchCommands)
+    {
+        updateBatchCommand(batch);
+    }
+
+    auto &quadPipeline = _quadCommand.getPipelineDescriptor();
+    setVertexLayout(quadPipeline);
+    quadPipeline.programState = _programState;
+}
+
+void Label::updateShaderProgram()
+{
+    auto programType = backend::ProgramType::POSITION_TEXTURE_COLOR;
+    if (_currentLabelType == LabelType::BMFONT || _currentLabelType == LabelType::CHARMAP)
+    {
+        auto texture = _getTexture(this);
+        if(texture && texture->getAlphaTextureName())
+        {
+            programType = backend::ProgramType::ETC1;
+        }
+    }
+    else
+    {
+        switch (_currLabelEffect)
+        {
+            case cocos2d::LabelEffect::NORMAL:
+                if (_useDistanceField)
+                {
+                    programType = backend::ProgramType::LABEL_DISTANCE_NORMAL;
+                }
+                else if (_useA8Shader)
+                {
+                    programType = backend::ProgramType::LABEL_NORMAL;
+                }
+                else
+                {
+                    auto texture = _getTexture(this);
+                    if(texture && texture->getAlphaTextureName())
+                    {
+                        programType = backend::ProgramType::ETC1;
+                    }
+                }
+                break;
+            case cocos2d::LabelEffect::OUTLINE:
+                {
+                    programType = backend::ProgramType::LABLE_OUTLINE;
+                }
+                break;
+            case cocos2d::LabelEffect::GLOW:
+                if (_useDistanceField)
+                {
+                    programType = backend::ProgramType::LABLE_DISTANCEFIELD_GLOW;
+                }
+                break;
+            default:
+                return;
+        }
+    }
+
+    CC_SAFE_RELEASE(_programState);
+    auto* program = backend::Program::getBuiltinProgram(programType);
+    _programState = new backend::ProgramState(program);
+
+    updateUniformLocations();
+
+    for (auto &batch : _batchCommands)
+    {
+        updateBatchCommand(batch);
+    }
+
+    auto &quadPipeline = _quadCommand.getPipelineDescriptor();
+    setVertexLayout(quadPipeline);
+    quadPipeline.programState = _programState;
+}
+
+void Label::updateBatchCommand(Label::BatchCommand &batch)
+{
+    CCASSERT(_programState, "programState should be set!");
+
+    auto& pipelineDescriptor = batch.textCommand.getPipelineDescriptor();
+    CC_SAFE_RELEASE_NULL(pipelineDescriptor.programState);
+    pipelineDescriptor.programState = _programState->clone();
+    setVertexLayout(pipelineDescriptor);
+
+    auto &pipelineShadow = batch.shadowCommand.getPipelineDescriptor();
+    CC_SAFE_RELEASE_NULL(pipelineShadow.programState);
+    pipelineShadow.programState = _programState->clone();;
+    setVertexLayout(pipelineShadow);
+
+    auto &pipelineOutline = batch.outLineCommand.getPipelineDescriptor();
+    CC_SAFE_RELEASE_NULL(pipelineOutline.programState);
+    pipelineOutline.programState = _programState->clone();
+    setVertexLayout(pipelineOutline);
+
+}
+
+void Label::updateUniformLocations()
+{
+    _mvpMatrixLocation      = _programState->getUniformLocation(backend::Uniform::MVP_MATRIX);
+    _textureLocation        = _programState->getUniformLocation(backend::Uniform::TEXTURE);
+    _alphaTextureLocation   = _programState->getUniformLocation(backend::Uniform::TEXTURE1);
+    _textColorLocation      = _programState->getUniformLocation(backend::Uniform::TEXT_COLOR);
+    _effectColorLocation    = _programState->getUniformLocation(backend::Uniform::EFFECT_COLOR);
+    _effectTypeLocation     = _programState->getUniformLocation(backend::Uniform::EFFECT_TYPE);
+}
+
+void Label::setFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = false */, bool useA8Shader /* = false */)
+{
+    if(atlas)
+    {
+        _systemFontDirty = false;
+    }
+
+    if (atlas == _fontAtlas)
+        return;
+
+    CC_SAFE_RETAIN(atlas);
+    if (_fontAtlas)
+    {
+        _batchNodes.clear();
+        FontAtlasCache::releaseFontAtlas(_fontAtlas);
+    }
+    _fontAtlas = atlas;
+    
+    if (_reusedLetter == nullptr)
+    {
+        _reusedLetter = Sprite::create();
+        _reusedLetter->setOpacityModifyRGB(_isOpacityModifyRGB);            
+        _reusedLetter->retain();
+        _reusedLetter->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT);
+    }
+
+    if (_fontAtlas)
+    {
+        _lineHeight = _fontAtlas->getLineHeight();
+        _contentDirty = true;
+        _systemFontDirty = false;
+    }
+    _useDistanceField = distanceFieldEnabled;
+    _useA8Shader = useA8Shader;
+
+    if (_currentLabelType != LabelType::TTF)
+    {
+        _currLabelEffect = LabelEffect::NORMAL;
+        updateShaderProgram();
+    }
+}
+
+bool Label::setTTFConfig(const TTFConfig& ttfConfig)
+{
+    _originalFontSize = ttfConfig.fontSize;
+    return setTTFConfigInternal(ttfConfig);
+}
+
+bool Label::setBMFontFilePath(const std::string& bmfontFilePath, const Vec2& imageOffset, float fontSize)
+{
+    FontAtlas *newAtlas = FontAtlasCache::getFontAtlasFNT(bmfontFilePath,imageOffset);
+    
+    if (!newAtlas)
+    {
+        reset();
+        return false;
+    }
+
+    //assign the default fontSize
+    if (std::abs(fontSize) < FLT_EPSILON) {
+        FontFNT *bmFont = (FontFNT*)newAtlas->getFont();
+        if (bmFont) {
+            float originalFontSize = bmFont->getOriginalFontSize();
+            _bmFontSize = originalFontSize / CC_CONTENT_SCALE_FACTOR();
+        }
+    }
+
+    if(fontSize > 0.0f){
+        _bmFontSize = fontSize;
+    }
+
+    _bmFontPath = bmfontFilePath;
+
+    _currentLabelType = LabelType::BMFONT;
+    setFontAtlas(newAtlas);
+
+    return true;
+}
+
+void Label::setString(const std::string& text)
+{
+    if (text.compare(_utf8Text))
+    {
+        _utf8Text = text;
+        _contentDirty = true;
+
+        std::u32string utf32String;
+        if (StringUtils::UTF8ToUTF32(_utf8Text, utf32String))
+        {
+            _utf32Text  = utf32String;
+        }
+    }
+}
+
+void Label::setAlignment(TextHAlignment hAlignment,TextVAlignment vAlignment)
+{
+    if (hAlignment != _hAlignment || vAlignment != _vAlignment)
+    {
+        _hAlignment = hAlignment;
+        _vAlignment = vAlignment;
+
+        _contentDirty = true;
+    }
+}
+
+void Label::setMaxLineWidth(float maxLineWidth)
+{
+    if (_labelWidth == 0 && _maxLineWidth != maxLineWidth)
+    {
+        _maxLineWidth = maxLineWidth;
+        _contentDirty = true;
+    }
+}
+
+void Label::setDimensions(float width, float height)
+{
+    if(_overflow == Overflow::RESIZE_HEIGHT){
+        height = 0;
+    }
+    if (height != _labelHeight || width != _labelWidth)
+    {
+        _labelWidth = width;
+        _labelHeight = height;
+        _labelDimensions.width = width;
+        _labelDimensions.height = height;
+
+        _maxLineWidth = width;
+        _contentDirty = true;
+
+        if(_overflow == Overflow::SHRINK){
+            if (_originalFontSize > 0) {
+                this->restoreFontSize();
+            }
+        }
+    }
+}
+
+void Label::restoreFontSize()
+{
+    if(_currentLabelType == LabelType::TTF){
+        auto ttfConfig = this->getTTFConfig();
+        ttfConfig.fontSize = _originalFontSize;
+        this->setTTFConfigInternal(ttfConfig);
+    }else if(_currentLabelType == LabelType::BMFONT){
+        this->setBMFontSizeInternal(_originalFontSize);
+    }else if(_currentLabelType == LabelType::STRING_TEXTURE){
+        this->setSystemFontSize(_originalFontSize);
+    }
+}
+
+void Label::setLineBreakWithoutSpace(bool breakWithoutSpace)
+{
+    if (breakWithoutSpace != _lineBreakWithoutSpaces)
+    {
+        _lineBreakWithoutSpaces = breakWithoutSpace;
+        _contentDirty = true;     
+    }
+}
+
+void Label::updateLabelLetters()
+{
+    if (!_letters.empty())
+    {
+        Rect uvRect;
+        LabelLetter* letterSprite;
+        int letterIndex;
+
+        for (auto it = _letters.begin(); it != _letters.end();)
+        {
+            letterIndex = it->first;
+            letterSprite = (LabelLetter*)it->second;
+
+            if (letterIndex >= _lengthOfString)
+            {
+                Node::removeChild(letterSprite, true);
+                it = _letters.erase(it);
+            }
+            else
+            {
+                auto& letterInfo = _lettersInfo[letterIndex];
+                if (letterInfo.valid)
+                {
+                    auto& letterDef = _fontAtlas->_letterDefinitions[letterInfo.utf32Char];
+                    uvRect.size.height = letterDef.height;
+                    uvRect.size.width = letterDef.width;
+                    uvRect.origin.x = letterDef.U;
+                    uvRect.origin.y = letterDef.V;
+
+                    auto batchNode = _batchNodes.at(letterDef.textureID);
+                    letterSprite->setTextureAtlas(batchNode->getTextureAtlas());
+                    letterSprite->setTexture(_fontAtlas->getTexture(letterDef.textureID));
+                    if (letterDef.width <= 0.f || letterDef.height <= 0.f)
+                    {
+                        letterSprite->setTextureAtlas(nullptr);
+                    }
+                    else
+                    {
+                        letterSprite->setTextureRect(uvRect, false, uvRect.size);
+                        letterSprite->setTextureAtlas(_batchNodes.at(letterDef.textureID)->getTextureAtlas());
+                        letterSprite->setAtlasIndex(_lettersInfo[letterIndex].atlasIndex);
+                    }
+
+                    auto px = letterInfo.positionX + letterDef.width / 2 + _linesOffsetX[letterInfo.lineIndex];
+                    auto py = letterInfo.positionY - letterDef.height / 2 + _letterOffsetY;
+                    letterSprite->setPosition(px, py);
+                }
+                else
+                {
+                    letterSprite->setTextureAtlas(nullptr);
+                }
+                this->updateLetterSpriteScale(letterSprite);
+                ++it;
+            }
+        }
+    }
+}
+
+bool Label::alignText()
+{
+    if (_fontAtlas == nullptr || _utf32Text.empty())
+    {
+        setContentSize(Size::ZERO);
+        return true;
+    }
+
+    bool ret = true;
+    do {
+        _fontAtlas->prepareLetterDefinitions(_utf32Text);
+        auto& textures = _fontAtlas->getTextures();
+        auto size = textures.size();
+        if (size > static_cast<size_t>(_batchNodes.size()))
+        {
+            for (auto index = static_cast<size_t>(_batchNodes.size()); index < size; ++index)
+            {
+                auto batchNode = SpriteBatchNode::createWithTexture(textures.at(index));
+                if (batchNode)
+                {
+                    _isOpacityModifyRGB = batchNode->getTexture()->hasPremultipliedAlpha();
+                    _blendFunc = batchNode->getBlendFunc();
+                    batchNode->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT);
+                    batchNode->setPosition(Vec2::ZERO);
+                    _batchNodes.pushBack(batchNode);
+                }
+            }
+        }
+        if (_batchNodes.empty())
+        {
+            return true;
+        }
+        // optimize for one-texture-only scenario
+        // if multiple textures, then we should count how many chars
+        // are per texture
+        if (_batchNodes.size()==1)
+            _batchNodes.at(0)->reserveCapacity(_utf32Text.size());
+
+        _reusedLetter->setBatchNode(_batchNodes.at(0));
+        
+        _lengthOfString = 0;
+        _textDesiredHeight = 0.f;
+        _linesWidth.clear();
+        if (_maxLineWidth > 0.f && !_lineBreakWithoutSpaces)
+        {
+            multilineTextWrapByWord();
+        }
+        else
+        {
+            multilineTextWrapByChar();
+        }
+        computeAlignmentOffset();
+
+        if(_overflow == Overflow::SHRINK){
+            float fontSize = this->getRenderingFontSize();
+
+            if(fontSize > 0 &&  isVerticalClamp()){
+                this->shrinkLabelToContentSize(CC_CALLBACK_0(Label::isVerticalClamp, this));
+            }
+        }
+
+        if(!updateQuads()){
+            ret = false;
+            if(_overflow == Overflow::SHRINK){
+                this->shrinkLabelToContentSize(CC_CALLBACK_0(Label::isHorizontalClamp, this));
+            }
+            break;
+        }
+    
+        updateLabelLetters();
+        
+        updateColor();
+    }while (0);
+
+    return ret;
+}
+
+bool Label::computeHorizontalKernings(const std::u32string& stringToRender)
+{
+    if (_horizontalKernings)
+    {
+        delete [] _horizontalKernings;
+        _horizontalKernings = nullptr;
+    }
+
+    int letterCount = 0;
+    _horizontalKernings = _fontAtlas->getFont()->getHorizontalKerningForTextUTF32(stringToRender, letterCount);
+
+    if(!_horizontalKernings)
+        return false;
+    else
+        return true;
+}
+
+bool Label::isHorizontalClamped(float letterPositionX, int lineIndex)
+{
+    auto wordWidth = this->_linesWidth[lineIndex];
+    bool letterOverClamp = (letterPositionX > _contentSize.width || letterPositionX < 0);
+    if (!_enableWrap) {
+        return letterOverClamp;
+    }else{
+        return (wordWidth > this->_contentSize.width && letterOverClamp);
+    }
+}
+
+bool Label::updateQuads()
+{
+    bool ret = true;
+    for (auto&& batchNode : _batchNodes)
+    {
+        batchNode->getTextureAtlas()->removeAllQuads();
+    }
+    
+    for (int ctr = 0; ctr < _lengthOfString; ++ctr)
+    {
+        if (_lettersInfo[ctr].valid)
+        {
+            auto& letterDef = _fontAtlas->_letterDefinitions[_lettersInfo[ctr].utf32Char];
+            
+            _reusedRect.size.height = letterDef.height;
+            _reusedRect.size.width  = letterDef.width;
+            _reusedRect.origin.x    = letterDef.U;
+            _reusedRect.origin.y    = letterDef.V;
+
+            auto py = _lettersInfo[ctr].positionY + _letterOffsetY;
+            if (_labelHeight > 0.f) {
+                if (py > _tailoredTopY)
+                {
+                    auto clipTop = py - _tailoredTopY;
+                    _reusedRect.origin.y += clipTop;
+                    _reusedRect.size.height -= clipTop;
+                    py -= clipTop;
+                }
+                if (py - letterDef.height * _bmfontScale < _tailoredBottomY)
+                {
+                    _reusedRect.size.height = (py < _tailoredBottomY) ? 0.f : (py - _tailoredBottomY);
+                }
+            }
+
+            auto lineIndex = _lettersInfo[ctr].lineIndex;
+            auto px = _lettersInfo[ctr].positionX + letterDef.width/2 * _bmfontScale + _linesOffsetX[lineIndex];
+
+            if(_labelWidth > 0.f){
+                if (this->isHorizontalClamped(px, lineIndex)) {
+                    if(_overflow == Overflow::CLAMP){
+                        _reusedRect.size.width = 0;
+                    }else if(_overflow == Overflow::SHRINK){
+                        if (_contentSize.width > letterDef.width) {
+                            ret = false;
+                            break;
+                        }else{
+                            _reusedRect.size.width = 0;
+                        }
+
+                    }
+                }
+            }
+
+
+            if (_reusedRect.size.height > 0.f && _reusedRect.size.width > 0.f)
+            {
+                _reusedLetter->setTextureRect(_reusedRect, false, _reusedRect.size);
+                float letterPositionX = _lettersInfo[ctr].positionX + _linesOffsetX[_lettersInfo[ctr].lineIndex];
+                _reusedLetter->setPosition(letterPositionX, py);
+                auto index = static_cast<int>(_batchNodes.at(letterDef.textureID)->getTextureAtlas()->getTotalQuads());
+                _lettersInfo[ctr].atlasIndex = index;
+
+                this->updateLetterSpriteScale(_reusedLetter);
+
+                _batchNodes.at(letterDef.textureID)->insertQuadFromSprite(_reusedLetter, index);
+            }
+        }     
+    }
+
+
+    return ret;
+}
+
+bool Label::setTTFConfigInternal(const TTFConfig& ttfConfig)
+{
+    FontAtlas *newAtlas = FontAtlasCache::getFontAtlasTTF(&ttfConfig);
+
+    if (!newAtlas)
+    {
+        reset();
+        return false;
+    }
+
+    _currentLabelType = LabelType::TTF;
+    setFontAtlas(newAtlas,ttfConfig.distanceFieldEnabled,true);
+
+    _fontConfig = ttfConfig;
+
+    if (_fontConfig.outlineSize > 0)
+    {
+        _fontConfig.distanceFieldEnabled = false;
+        _useDistanceField = false;
+        _useA8Shader = false;
+        _currLabelEffect = LabelEffect::OUTLINE;
+        updateShaderProgram();
+    }
+    else
+    {
+        _currLabelEffect = LabelEffect::NORMAL;
+        updateShaderProgram();
+    }
+
+    if (_fontConfig.italics)
+        this->enableItalics();
+    if (_fontConfig.bold)
+        this->enableBold();
+    if (_fontConfig.underline)
+        this->enableUnderline();
+    if (_fontConfig.strikethrough)
+        this->enableStrikethrough();
+
+    return true;
+}
+
+void Label::setBMFontSizeInternal(float fontSize)
+{
+    if(_currentLabelType == LabelType::BMFONT){
+        this->setBMFontFilePath(_bmFontPath, Vec2::ZERO, fontSize);
+        _contentDirty = true;
+    }
+}
+
+void Label::scaleFontSizeDown(float fontSize)
+{
+    bool shouldUpdateContent = true;
+    if(_currentLabelType == LabelType::TTF){
+        auto ttfConfig = this->getTTFConfig();
+        ttfConfig.fontSize = fontSize;
+        this->setTTFConfigInternal(ttfConfig);
+    }else if(_currentLabelType == LabelType::BMFONT){
+        if (std::abs(fontSize) < FLT_EPSILON) {
+            fontSize = 0.1f;
+            shouldUpdateContent = false;
+        }
+        this->setBMFontSizeInternal(fontSize);
+    }else if (_currentLabelType == LabelType::STRING_TEXTURE){
+        this->setSystemFontSize(fontSize);
+    }
+    
+    if (shouldUpdateContent) {
+        this->updateContent();
+    }
+}
+
+void Label::enableGlow(const Color4B& glowColor)
+{
+    if (_currentLabelType == LabelType::TTF)
+    {
+        if (_fontConfig.distanceFieldEnabled == false)
+        {
+            auto config = _fontConfig;
+            config.outlineSize = 0;
+            config.distanceFieldEnabled = true;
+            setTTFConfig(config);
+            _contentDirty = true;
+        }
+        _currLabelEffect = LabelEffect::GLOW;
+        _effectColorF.r = glowColor.r / 255.0f;
+        _effectColorF.g = glowColor.g / 255.0f;
+        _effectColorF.b = glowColor.b / 255.0f;
+        _effectColorF.a = glowColor.a / 255.0f;
+        updateShaderProgram();
+    }
+}
+
+void Label::enableOutline(const Color4B& outlineColor,int outlineSize /* = -1 */)
+{
+    CCASSERT(_currentLabelType == LabelType::STRING_TEXTURE || _currentLabelType == LabelType::TTF, "Only supported system font and TTF!");
+
+    if (outlineSize > 0 || _currLabelEffect == LabelEffect::OUTLINE)
+    {
+        if (_currentLabelType == LabelType::TTF)
+        {
+            _effectColorF.r = outlineColor.r / 255.0f;
+            _effectColorF.g = outlineColor.g / 255.0f;
+            _effectColorF.b = outlineColor.b / 255.0f;
+            _effectColorF.a = outlineColor.a / 255.0f;
+
+            if (outlineSize > 0 && _fontConfig.outlineSize != outlineSize)
+            {
+                _fontConfig.outlineSize = outlineSize;
+                setTTFConfig(_fontConfig);
+            }
+        }
+        else if (_effectColorF != outlineColor || _outlineSize != outlineSize)
+        {
+            _effectColorF.r = outlineColor.r / 255.f;
+            _effectColorF.g = outlineColor.g / 255.f;
+            _effectColorF.b = outlineColor.b / 255.f;
+            _effectColorF.a = outlineColor.a / 255.f;
+            _currLabelEffect = LabelEffect::OUTLINE;
+            _contentDirty = true;
+        }
+        _outlineSize = outlineSize;
+    }
+}
+
+void Label::enableShadow(const Color4B& shadowColor /* = Color4B::BLACK */,
+                         const Size &offset /* = Size(2 ,-2)*/,
+                         int /* blurRadius = 0 */)
+{
+    _shadowEnabled = true;
+    _shadowDirty = true;
+
+    _shadowOffset.width = offset.width;
+    _shadowOffset.height = offset.height;
+    //TODO: support blur for shadow
+
+    _shadowColor3B.r = shadowColor.r;
+    _shadowColor3B.g = shadowColor.g;
+    _shadowColor3B.b = shadowColor.b;
+    _shadowOpacity = shadowColor.a;
+
+    if (!_systemFontDirty && !_contentDirty && _textSprite)
+    {
+        auto fontDef = _getFontDefinition();
+        if (_shadowNode)
+        {
+            if (shadowColor != _shadowColor4F)
+            {
+                _shadowNode->release();
+                _shadowNode = nullptr;
+                createShadowSpriteForSystemFont(fontDef);
+            }
+            else
+            {
+                _shadowNode->setPosition(_shadowOffset.width, _shadowOffset.height);
+            }
+        }
+        else
+        {
+            createShadowSpriteForSystemFont(fontDef);
+        }
+    }
+
+    _shadowColor4F.r = shadowColor.r / 255.0f;
+    _shadowColor4F.g = shadowColor.g / 255.0f;
+    _shadowColor4F.b = shadowColor.b / 255.0f;
+    _shadowColor4F.a = shadowColor.a / 255.0f;
+
+    if (_currentLabelType == LabelType::BMFONT || _currentLabelType == LabelType::CHARMAP)
+    {
+        updateShaderProgram();
+    }
+}
+
+void Label::enableItalics()
+{
+    setRotationSkewX(12);
+}
+
+void Label::enableBold()
+{
+    if (!_boldEnabled)
+    {
+        // bold is implemented with outline
+        enableShadow(Color4B::WHITE, Size(0.9f, 0), 0);
+        // add one to kerning
+        setAdditionalKerning(_additionalKerning+1);
+        _boldEnabled = true;
+    }
+}
+
+void Label::enableUnderline()
+{
+    // remove it, just in case to prevent adding two or more
+    if (!_underlineNode)
+    {
+        _underlineNode = DrawNode::create();
+        addChild(_underlineNode, 100000);
+        _contentDirty = true;
+    }
+}
+
+void Label::enableStrikethrough()
+{
+    if (!_strikethroughEnabled)
+    {
+        enableUnderline();
+        _strikethroughEnabled = true;
+    }
+}
+
+void Label::disableEffect()
+{
+    disableEffect(LabelEffect::ALL);
+}
+
+void Label::disableEffect(LabelEffect effect)
+{
+    switch (effect)
+    {
+        case cocos2d::LabelEffect::NORMAL:
+            break;
+        case cocos2d::LabelEffect::OUTLINE:
+            if (_currLabelEffect == LabelEffect::OUTLINE)
+            {
+                if (_currentLabelType == LabelType::TTF)
+                {
+                    _fontConfig.outlineSize = 0;
+                    setTTFConfig(_fontConfig);
+                }
+                _currLabelEffect = LabelEffect::NORMAL;
+                _contentDirty = true;
+            }
+            break;
+        case cocos2d::LabelEffect::SHADOW:
+            if (_shadowEnabled)
+            {
+                _shadowEnabled = false;
+                CC_SAFE_RELEASE_NULL(_shadowNode);
+                updateShaderProgram();
+            }
+            break;
+        case cocos2d::LabelEffect::GLOW:
+            if (_currLabelEffect == LabelEffect::GLOW)
+            {
+                _currLabelEffect = LabelEffect::NORMAL;
+                updateShaderProgram();
+            }
+            break;
+        case cocos2d::LabelEffect::ITALICS:
+            setRotationSkewX(0);
+            break;
+        case cocos2d::LabelEffect::BOLD:
+            if (_boldEnabled) {
+                _boldEnabled = false;
+                _additionalKerning -= 1;
+                disableEffect(LabelEffect::SHADOW);
+            }
+            break;
+        case cocos2d::LabelEffect::UNDERLINE:
+            if (_underlineNode) {
+                removeChild(_underlineNode);
+                _underlineNode = nullptr;
+            }
+            break;
+        case cocos2d::LabelEffect::STRIKETHROUGH:
+            _strikethroughEnabled = false;
+            // since it is based on underline, disable it as well
+            disableEffect(LabelEffect::UNDERLINE);
+            break;
+        case LabelEffect::ALL:
+        {
+            disableEffect(LabelEffect::SHADOW);
+            disableEffect(LabelEffect::GLOW);
+            disableEffect(LabelEffect::OUTLINE);
+            disableEffect(LabelEffect::ITALICS);
+            disableEffect(LabelEffect::BOLD);
+            disableEffect(LabelEffect::UNDERLINE);
+            disableEffect(LabelEffect::STRIKETHROUGH);
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+void Label::createSpriteForSystemFont(const FontDefinition& fontDef)
+{
+    _currentLabelType = LabelType::STRING_TEXTURE;
+
+    auto texture = new (std::nothrow) Texture2D;
+    texture->initWithString(_utf8Text.c_str(), fontDef);
+
+    _textSprite = Sprite::createWithTexture(texture);
+    //set camera mask using label's camera mask, because _textSprite may be null when setting camera mask to label
+    _textSprite->setCameraMask(getCameraMask());
+    _textSprite->setGlobalZOrder(getGlobalZOrder());
+    _textSprite->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT);
+    this->setContentSize(_textSprite->getContentSize());
+    texture->release();
+    if (_blendFuncDirty)
+    {
+        _textSprite->setBlendFunc(_blendFunc);
+    }
+
+    _textSprite->retain();
+    _textSprite->updateDisplayedColor(_displayedColor);
+    _textSprite->updateDisplayedOpacity(_displayedOpacity);
+}
+
+void Label::createShadowSpriteForSystemFont(const FontDefinition& fontDef)
+{
+    if (!fontDef._stroke._strokeEnabled && fontDef._fontFillColor == _shadowColor3B
+        && (fontDef._fontAlpha == _shadowOpacity))
+    {
+        _shadowNode = Sprite::createWithTexture(_textSprite->getTexture());
+    }
+    else
+    {
+        FontDefinition shadowFontDefinition = fontDef;
+        shadowFontDefinition._fontFillColor.r = _shadowColor3B.r;
+        shadowFontDefinition._fontFillColor.g = _shadowColor3B.g;
+        shadowFontDefinition._fontFillColor.b = _shadowColor3B.b;
+        shadowFontDefinition._fontAlpha = _shadowOpacity;
+
+        shadowFontDefinition._stroke._strokeColor = shadowFontDefinition._fontFillColor;
+        shadowFontDefinition._stroke._strokeAlpha = shadowFontDefinition._fontAlpha;
+
+        auto texture = new (std::nothrow) Texture2D;
+        texture->initWithString(_utf8Text.c_str(), shadowFontDefinition);
+        _shadowNode = Sprite::createWithTexture(texture);
+        texture->release();
+    }
+
+    if (_shadowNode)
+    {
+        if (_blendFuncDirty)
+        {
+            _shadowNode->setBlendFunc(_blendFunc);
+        }
+        _shadowNode->setCameraMask(getCameraMask());
+        _shadowNode->setGlobalZOrder(getGlobalZOrder());
+        _shadowNode->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT);
+        _shadowNode->setPosition(_shadowOffset.width, _shadowOffset.height);
+
+        _shadowNode->retain();
+        _shadowNode->updateDisplayedColor(_displayedColor);
+        _shadowNode->updateDisplayedOpacity(_displayedOpacity);
+    }
+}
+
+void Label::setCameraMask(unsigned short mask, bool applyChildren)
+{
+    Node::setCameraMask(mask, applyChildren);
+    
+    if (_textSprite)
+    {
+        _textSprite->setCameraMask(mask, applyChildren);
+    }
+    if (_shadowNode)
+    {
+        _shadowNode->setCameraMask(mask, applyChildren);
+    }
+}
+
+void Label::updateContent()
+{
+    if (_systemFontDirty)
+    {
+        if (_fontAtlas)
+        {
+            _batchNodes.clear();
+            _batchCommands.clear();
+            CC_SAFE_RELEASE_NULL(_reusedLetter);
+            FontAtlasCache::releaseFontAtlas(_fontAtlas);
+            _fontAtlas = nullptr;
+        }
+
+        _systemFontDirty = false;
+    }
+
+    CC_SAFE_RELEASE_NULL(_textSprite);
+    CC_SAFE_RELEASE_NULL(_shadowNode);
+    bool updateFinished = true;
+
+    if (_fontAtlas)
+    {
+        std::u32string utf32String;
+        if (StringUtils::UTF8ToUTF32(_utf8Text, utf32String))
+        {
+            _utf32Text = utf32String;
+        }
+
+        computeHorizontalKernings(_utf32Text);
+        updateFinished = alignText();
+    }
+    else
+    {
+        auto fontDef = _getFontDefinition();
+        createSpriteForSystemFont(fontDef);
+        if (_shadowEnabled)
+        {
+            createShadowSpriteForSystemFont(fontDef);
+        }
+    }
+
+    if (_underlineNode)
+    {
+        _underlineNode->clear();
+
+        if (_numberOfLines)
+        {
+            // This is the logic for TTF fonts
+            const float charheight = (_textDesiredHeight / _numberOfLines);
+            _underlineNode->setLineWidth(charheight/6);
+
+            // atlas font
+            for (int i=0; i<_numberOfLines; ++i)
+            {
+                float offsety = 0;
+                if (_strikethroughEnabled)
+                    offsety += charheight / 2;
+                // FIXME: Might not work with different vertical alignments
+                float y = (_numberOfLines - i - 1) * charheight + offsety;
+
+                // Github issue #15214. Uses _displayedColor instead of _textColor for the underline.
+                // This is to have the same behavior of SystemFonts.
+                _underlineNode->drawLine(Vec2(_linesOffsetX[i],y), Vec2(_linesWidth[i] + _linesOffsetX[i],y), Color4F(_displayedColor));
+            }
+        }
+        else if (_textSprite)
+        {
+            // ...and is the logic for System fonts
+            float y = 0;
+            const auto spriteSize = _textSprite->getContentSize();
+            _underlineNode->setLineWidth(spriteSize.height/6);
+
+            if (_strikethroughEnabled)
+                // FIXME: system fonts don't report the height of the font correctly. only the size of the texture, which is POT
+                y += spriteSize.height / 2;
+            // FIXME: Might not work with different vertical alignments
+            _underlineNode->drawLine(Vec2(0.0f,y), Vec2(spriteSize.width,y), Color4F(_textSprite->getDisplayedColor()));
+        }
+    }
+
+    if(updateFinished){
+        _contentDirty = false;
+    }
+
+#if CC_LABEL_DEBUG_DRAW
+    _debugDrawNode->clear();
+    Vec2 vertices[4] =
+    {
+        Vec2::ZERO,
+        Vec2(_contentSize.width, 0.0f),
+        Vec2(_contentSize.width, _contentSize.height),
+        Vec2(0.0f, _contentSize.height)
+    };
+    _debugDrawNode->drawPoly(vertices, 4, true, Color4F::WHITE);
+#endif
+}
+
+void Label::setBMFontSize(float fontSize)
+{
+    this->setBMFontSizeInternal(fontSize);
+    _originalFontSize = fontSize;
+}
+
+float Label::getBMFontSize()const
+{
+    return _bmFontSize;
+}
+
+void Label::updateBuffer(TextureAtlas* textureAtlas, CustomCommand& customCommand)
+{
+    if (textureAtlas->getTotalQuads() > customCommand.getVertexCapacity())
+    {
+        customCommand.createVertexBuffer((unsigned int)sizeof(V3F_C4B_T2F_Quad), (unsigned int)textureAtlas->getTotalQuads(), CustomCommand::BufferUsage::DYNAMIC);
+        customCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, (unsigned int)textureAtlas->getTotalQuads() * 6, CustomCommand::BufferUsage::DYNAMIC);
+    }
+    customCommand.updateVertexBuffer(textureAtlas->getQuads(), (unsigned int)(textureAtlas->getTotalQuads() * sizeof(V3F_C4B_T2F_Quad)));
+    customCommand.updateIndexBuffer(textureAtlas->getIndices(), (unsigned int)(textureAtlas->getTotalQuads() * 6 * sizeof(unsigned short)));
+    customCommand.setIndexDrawInfo(0, (unsigned int)(textureAtlas->getTotalQuads() * 6));
+}
+
+void Label::updateEffectUniforms(BatchCommand &batch, TextureAtlas* textureAtlas, Renderer *renderer, const Mat4 &transform)
+{
+    updateBuffer(textureAtlas, batch.textCommand);
+
+    auto & matrixProjection = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+
+    if (_shadowEnabled) {
+        updateBuffer(textureAtlas, batch.shadowCommand);
+        auto shadowMatrix = matrixProjection * _shadowTransform;
+        batch.shadowCommand.getPipelineDescriptor().programState->setUniform(_mvpMatrixLocation, shadowMatrix.m, sizeof(shadowMatrix.m));
+    }
+
+    if(_currentLabelType == LabelType::TTF)
+    {
+        switch (_currLabelEffect) {
+            case LabelEffect::OUTLINE:
+            {
+                int effectType = 0;
+                Vec4 effectColor(_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
+                
+                //draw shadow
+                if(_shadowEnabled)
+                {
+                    effectType = 2;
+                    Vec4 shadowColor = Vec4(_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
+                    auto *programStateShadow = batch.shadowCommand.getPipelineDescriptor().programState;
+                    programStateShadow->setUniform(_effectColorLocation, &shadowColor, sizeof(Vec4));
+                    programStateShadow->setUniform(_effectTypeLocation, &effectType, sizeof(effectType));
+                    batch.shadowCommand.init(_globalZOrder);
+                    renderer->addCommand(&batch.shadowCommand);
+                }
+                
+                //draw outline
+                {
+                    effectType = 1;
+                    updateBuffer(textureAtlas, batch.outLineCommand);
+                    auto *programStateOutline = batch.outLineCommand.getPipelineDescriptor().programState;
+                    programStateOutline->setUniform(_effectColorLocation, &effectColor, sizeof(Vec4));
+                    programStateOutline->setUniform(_effectTypeLocation, &effectType, sizeof(effectType));
+                    batch.outLineCommand.init(_globalZOrder);
+                    renderer->addCommand(&batch.outLineCommand);
+                }
+              
+                //draw text
+                {
+                    effectType = 0;
+                    auto *programStateText= batch.textCommand.getPipelineDescriptor().programState;
+
+                    programStateText->setUniform(_effectColorLocation, &effectColor, sizeof(effectColor));
+                    programStateText->setUniform(_effectTypeLocation, &effectType, sizeof(effectType));
+                }
+            }
+                break;
+            case LabelEffect::NORMAL:
+            {
+                if (_shadowEnabled) {
+                    Vec4 shadowColor = Vec4(_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
+                    auto *programStateShadow = batch.shadowCommand.getPipelineDescriptor().programState;
+                    programStateShadow->setUniform(_textColorLocation, &shadowColor, sizeof(Vec4));
+                    batch.shadowCommand.init(_globalZOrder);
+                    renderer->addCommand(&batch.shadowCommand);
+                }
+            }
+                break;
+            case LabelEffect::GLOW:
+            {
+                //draw shadow
+                if(_shadowEnabled)
+                {
+                    Vec4 shadowColor = Vec4(_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
+                    auto *programStateShadow = batch.shadowCommand.getPipelineDescriptor().programState;
+                    programStateShadow->setUniform(_textColorLocation, &shadowColor, sizeof(Vec4));
+                    programStateShadow->setUniform(_effectColorLocation, &shadowColor, sizeof(Vec4));
+                    batch.shadowCommand.init(_globalZOrder);
+                    renderer->addCommand(&batch.shadowCommand);
+                }
+                
+                Vec4 effectColor(_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
+                batch.textCommand.getPipelineDescriptor().programState->setUniform(_effectColorLocation, &effectColor, sizeof(Vec4));
+            }
+                break;
+            default:
+                break;
+        }
+    }
+    else
+    {
+        if (_shadowEnabled) {
+            Color3B oldColor = _realColor;
+            uint8_t oldOPacity = _displayedOpacity;
+            _displayedOpacity = _shadowColor4F.a * (oldOPacity / 255.0f) * 255;
+            setColor(Color3B(_shadowColor4F));
+            batch.shadowCommand.updateVertexBuffer(textureAtlas->getQuads(), (unsigned int)(textureAtlas->getTotalQuads() * sizeof(V3F_C4B_T2F_Quad)) );
+            batch.shadowCommand.init(_globalZOrder);
+            renderer->addCommand(&batch.shadowCommand);
+            
+            _displayedOpacity = oldOPacity;
+            setColor(oldColor);
+        }
+    }
+
+    batch.textCommand.init(_globalZOrder);
+    renderer->addCommand(&batch.textCommand);
+}
+
+void Label::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
+{
+    if (_batchNodes.empty() || _lengthOfString <= 0)
+    {
+        return;
+    }
+    // Don't do calculate the culling if the transform was not updated
+    bool transformUpdated = flags & FLAGS_TRANSFORM_DIRTY;
+#if CC_USE_CULLING
+    auto visitingCamera = Camera::getVisitingCamera();
+    auto defaultCamera = Camera::getDefaultCamera();
+    if (visitingCamera == defaultCamera) {
+        _insideBounds = (transformUpdated || visitingCamera->isViewProjectionUpdated()) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
+    }
+    else
+    {
+        _insideBounds = renderer->checkVisibility(transform, _contentSize);
+    }
+
+    if (_insideBounds)
+#endif
+    {
+        cocos2d::Mat4 matrixProjection = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+        if (!_shadowEnabled && (_currentLabelType == LabelType::BMFONT || _currentLabelType == LabelType::CHARMAP))
+        {
+            updateBlendState();
+            for (auto&& it : _letters)
+            {
+                it.second->updateTransform();
+            }
+            // ETC1 ALPHA supports for BMFONT & CHARMAP
+            auto textureAtlas = _batchNodes.at(0)->getTextureAtlas();
+            if(!textureAtlas->getTotalQuads())
+                return;
+            
+            auto texture = textureAtlas->getTexture();
+            auto& pipelineQuad = _quadCommand.getPipelineDescriptor();
+            pipelineQuad.programState->setUniform(_mvpMatrixLocation, matrixProjection.m, sizeof(matrixProjection.m));
+            pipelineQuad.programState->setTexture(_textureLocation, 0, texture->getBackendTexture());
+            auto alphaTexture = textureAtlas->getTexture()->getAlphaTexture();
+            if(alphaTexture && alphaTexture->getBackendTexture())
+            {
+                pipelineQuad.programState->setTexture(_alphaTextureLocation, 1, alphaTexture->getBackendTexture());
+            }
+            _quadCommand.init(_globalZOrder, texture, _blendFunc, textureAtlas->getQuads(), textureAtlas->getTotalQuads(), transform, flags);
+            renderer->addCommand(&_quadCommand);
+        }
+        else
+        {
+            cocos2d::Mat4 matrixMVP = matrixProjection * transform;
+
+            for (auto &&it : _letters)
+            {
+                it.second->updateTransform();
+            }
+            int i = 0;
+
+            if (_batchCommands.size() != _batchNodes.size())
+            {
+                _batchCommands.resize(_batchNodes.size());
+                updateShaderProgram();
+            }
+
+            updateBlendState();
+
+            for (auto&& batchNode : _batchNodes)
+            {
+                auto textureAtlas = batchNode->getTextureAtlas();
+                if (!textureAtlas->getTotalQuads())
+                    return;
+
+                auto &batch = _batchCommands[i++];
+                auto &&commands = batch.getCommandArray();
+                for (auto command : commands)
+                {
+                    auto *programState = command->getPipelineDescriptor().programState;
+                    Vec4 textColor(_textColorF.r, _textColorF.g, _textColorF.b, _textColorF.a);
+                    programState->setUniform(_textColorLocation, &textColor, sizeof(Vec4));
+                    programState->setTexture(_textureLocation, 0, textureAtlas->getTexture()->getBackendTexture());
+                    auto alphaTexture = textureAtlas->getTexture()->getAlphaTexture();
+                    if (alphaTexture && alphaTexture->getBackendTexture())
+                    {
+                        programState->setTexture(_alphaTextureLocation, 1, alphaTexture->getBackendTexture());
+                    }
+                }
+                batch.textCommand.getPipelineDescriptor().programState->setUniform(_mvpMatrixLocation, matrixMVP.m, sizeof(matrixMVP.m));
+                batch.outLineCommand.getPipelineDescriptor().programState->setUniform(_mvpMatrixLocation, matrixMVP.m, sizeof(matrixMVP.m));
+                updateEffectUniforms(batch, textureAtlas, renderer, transform);
+            }
+        }
+    }
+}
+
+void Label::updateBlendState()
+{
+    setOpacityModifyRGB(_blendFunc != BlendFunc::ALPHA_NON_PREMULTIPLIED);
+    for(auto &batch: _batchCommands)
+    {
+        for(auto *command : batch.getCommandArray()) {
+            auto & blendDescriptor = command->getPipelineDescriptor().blendDescriptor;
+            updateBlend(blendDescriptor, _blendFunc);
+        }
+    }
+    updateBlend(_quadCommand.getPipelineDescriptor().blendDescriptor, _blendFunc);
+}
+
+void Label::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
+{
+    if (! _visible || (_utf8Text.empty() && _children.empty()) )
+    {
+        return;
+    }
+    
+    if (_systemFontDirty || _contentDirty)
+    {
+        updateContent();
+    }
+    
+    uint32_t flags = processParentFlags(parentTransform, parentFlags);
+
+    if (!_utf8Text.empty() && _shadowEnabled && (_shadowDirty || (flags & FLAGS_DIRTY_MASK)))
+    {
+        _position.x += _shadowOffset.width;
+        _position.y += _shadowOffset.height;
+        _transformDirty = _inverseDirty = true;
+
+        _shadowTransform = transform(parentTransform);
+
+        _position.x -= _shadowOffset.width;
+        _position.y -= _shadowOffset.height;
+        _transformDirty = _inverseDirty = true;
+
+        _shadowDirty = false;
+    }
+
+    bool visibleByCamera = isVisitableByVisitingCamera();
+    if (_children.empty() && !_textSprite && !visibleByCamera)
+    {
+        return;
+    }
+
+    // IMPORTANT:
+    // To ease the migration to v3.0, we still support the Mat4 stack,
+    // but it is deprecated and your code should not rely on it
+    _director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+    _director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
+    
+    if (!_children.empty())
+    {
+        sortAllChildren();
+
+        int i = 0;
+        // draw children zOrder < 0
+        for (auto size = _children.size(); i < size; ++i)
+        {
+            auto node = _children.at(i);
+
+            if (node && node->getLocalZOrder() < 0)
+                node->visit(renderer, _modelViewTransform, flags);
+            else
+                break;
+        }
+        
+        this->drawSelf(visibleByCamera, renderer, flags);
+
+        for (auto it = _children.cbegin() + i, itCend = _children.cend(); it != itCend; ++it)
+        {
+            (*it)->visit(renderer, _modelViewTransform, flags);
+        }
+            
+    }
+    else
+    {
+        this->drawSelf(visibleByCamera, renderer, flags);
+    }
+
+    _director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+}
+
+void Label::drawSelf(bool visibleByCamera, Renderer* renderer, uint32_t flags)
+{
+    if (_textSprite)
+    {
+        if (_shadowNode)
+        {
+            _shadowNode->visit(renderer, _modelViewTransform, flags);
+        }
+        _textSprite->visit(renderer, _modelViewTransform, flags);
+    }
+    else if (visibleByCamera && !_utf8Text.empty())
+    {
+        draw(renderer, _modelViewTransform, flags);
+    }
+}
+
+void Label::setSystemFontName(const std::string& systemFont)
+{
+    if (systemFont != _systemFont)
+    {
+        _systemFont = systemFont;
+        _currentLabelType = LabelType::STRING_TEXTURE;
+        _systemFontDirty = true;
+    }
+}
+
+void Label::setSystemFontSize(float fontSize)
+{
+    if (_systemFontSize != fontSize)
+    {
+        _systemFontSize = fontSize;
+        _originalFontSize = fontSize;
+        _currentLabelType = LabelType::STRING_TEXTURE;
+        _systemFontDirty = true;
+    }
+}
+
+///// PROTOCOL STUFF
+Sprite* Label::getLetter(int letterIndex)
+{
+    Sprite* letter = nullptr;
+    do
+    {
+        if (_systemFontDirty || _currentLabelType == LabelType::STRING_TEXTURE)
+        {
+            break;
+        }
+
+        auto contentDirty = _contentDirty;
+        if (contentDirty)
+        {
+            updateContent();
+        }
+
+        if (_textSprite == nullptr && letterIndex < _lengthOfString)
+        {
+            const auto &letterInfo = _lettersInfo[letterIndex];
+            if (!letterInfo.valid || letterInfo.atlasIndex<0)
+            {
+                break;
+            }
+
+            if (_letters.find(letterIndex) != _letters.end())
+            {
+                letter = _letters[letterIndex];
+            }
+
+            if (letter == nullptr)
+            {
+                auto& letterDef = _fontAtlas->_letterDefinitions[letterInfo.utf32Char];
+                auto textureID = letterDef.textureID;
+                Rect uvRect;
+                uvRect.size.height = letterDef.height;
+                uvRect.size.width = letterDef.width;
+                uvRect.origin.x = letterDef.U;
+                uvRect.origin.y = letterDef.V;
+
+                if (letterDef.width <= 0.f || letterDef.height <= 0.f)
+                {
+                    letter = LabelLetter::create();
+                }
+                else
+                {
+                    this->updateBMFontScale();
+                    letter = LabelLetter::createWithTexture(_fontAtlas->getTexture(textureID), uvRect);
+                    letter->setTextureAtlas(_batchNodes.at(textureID)->getTextureAtlas());
+                    letter->setAtlasIndex(letterInfo.atlasIndex);
+                    auto px = letterInfo.positionX + _bmfontScale * uvRect.size.width / 2 + _linesOffsetX[letterInfo.lineIndex];
+                    auto py = letterInfo.positionY - _bmfontScale * uvRect.size.height / 2 + _letterOffsetY;
+                    letter->setPosition(px,py);
+                    letter->setOpacity(_realOpacity);
+                    this->updateLetterSpriteScale(letter);
+                }
+                
+                addChild(letter);
+                _letters[letterIndex] = letter;
+            }
+        }
+    } while (false);
+
+    return letter;
+}
+
+void Label::setLineHeight(float height)
+{
+    CCASSERT(_currentLabelType != LabelType::STRING_TEXTURE, "Not supported system font!");
+
+    if (_lineHeight != height)
+    {
+        _lineHeight = height;
+        _contentDirty = true;
+    }
+}
+
+float Label::getLineHeight() const
+{
+    CCASSERT(_currentLabelType != LabelType::STRING_TEXTURE, "Not supported system font!");
+    return _textSprite ? 0.0f : _lineHeight * _bmfontScale;
+}
+
+void Label::setLineSpacing(float height)
+{
+    if (_lineSpacing != height)
+    {
+        _lineSpacing = height;
+        _contentDirty = true;
+    }
+}
+
+float Label::getLineSpacing() const
+{
+    return _lineSpacing;
+}
+
+void Label::setAdditionalKerning(float space)
+{
+
+    if (_currentLabelType != LabelType::STRING_TEXTURE)
+    {
+        if (_additionalKerning != space)
+        {
+            _additionalKerning = space;
+            _contentDirty = true;
+        }
+    }
+    else
+        CCLOG("Label::setAdditionalKerning not supported on LabelType::STRING_TEXTURE");
+}
+
+float Label::getAdditionalKerning() const
+{
+    CCASSERT(_currentLabelType != LabelType::STRING_TEXTURE, "Not supported system font!");
+
+    return _additionalKerning;
+}
+
+void Label::computeStringNumLines()
+{
+    int quantityOfLines = 1;
+
+    if (_utf32Text.empty())
+    {
+        _numberOfLines = 0;
+        return;
+    }
+
+    // count number of lines
+    size_t stringLen = _utf32Text.length();
+    for (size_t i = 0; i < stringLen - 1; ++i)
+    {
+        if (_utf32Text[i] == StringUtils::UnicodeCharacters::NewLine)
+        {
+            quantityOfLines++;
+        }
+    }
+
+    _numberOfLines = quantityOfLines;
+}
+
+int Label::getStringNumLines()
+{
+    if (_contentDirty)
+    {
+        updateContent();
+    }
+
+    if (_currentLabelType == LabelType::STRING_TEXTURE)
+    {
+        computeStringNumLines();
+    }
+
+    return _numberOfLines;
+}
+
+int Label::getStringLength()
+{
+    _lengthOfString = static_cast<int>(_utf32Text.length());
+    return _lengthOfString;
+}
+
+// RGBA protocol
+void Label::setOpacityModifyRGB(bool isOpacityModifyRGB)
+{
+    if (isOpacityModifyRGB != _isOpacityModifyRGB)
+    {
+        _isOpacityModifyRGB = isOpacityModifyRGB;
+        updateColor();
+    }
+}
+
+void Label::updateDisplayedColor(const Color3B& parentColor)
+{
+    Node::updateDisplayedColor(parentColor);
+
+    if (_textSprite)
+    {
+        _textSprite->updateDisplayedColor(_displayedColor);
+    }
+
+    if (_shadowNode)
+    {
+        _shadowNode->updateDisplayedColor(_displayedColor);
+    }
+
+    if (_underlineNode)
+    {
+        // FIXME: _underlineNode is not a sprite/label. It is a DrawNode
+        // and updating its color doesn't work. it must be re-drawn,
+        // which makes it super expensive to change update it frequently
+        // Correct solution is to update the DrawNode directly since we know it is
+        // a line. Returning a pointer to the line is an option
+        _contentDirty = true;
+    }
+
+    for (auto&& it : _letters)
+    {
+        it.second->updateDisplayedColor(_displayedColor);
+    }
+}
+
+void Label::updateDisplayedOpacity(uint8_t parentOpacity)
+{
+    Node::updateDisplayedOpacity(parentOpacity);
+
+    if (_textSprite)
+    {
+        _textSprite->updateDisplayedOpacity(_displayedOpacity);
+        if (_shadowNode)
+        {
+            _shadowNode->updateDisplayedOpacity(_displayedOpacity);
+        }
+    }
+
+    for (auto&& it : _letters)
+    {
+        it.second->updateDisplayedOpacity(_displayedOpacity);
+    }
+}
+
+// FIXME: it is not clear what is the difference between setTextColor() and setColor()
+// if setTextColor() only changes the text and nothing but the text (no glow, no outline, not underline)
+// that's fine but it should be documented
+void Label::setTextColor(const Color4B &color)
+{
+    CCASSERT(_currentLabelType == LabelType::TTF || _currentLabelType == LabelType::STRING_TEXTURE, "Only supported system font and ttf!");
+
+    if (_currentLabelType == LabelType::STRING_TEXTURE && _textColor != color)
+    {
+        _contentDirty = true;
+    }
+
+    _textColor = color;
+    _textColorF.r = _textColor.r / 255.0f;
+    _textColorF.g = _textColor.g / 255.0f;
+    _textColorF.b = _textColor.b / 255.0f;
+    _textColorF.a = _textColor.a / 255.0f;
+}
+
+void Label::updateColor()
+{
+    if (_batchNodes.empty())
+    {
+        return;
+    }
+
+    Color4B color4( _displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity );
+
+    // special opacity for premultiplied textures
+    if (_isOpacityModifyRGB)
+    {
+        color4.r *= _displayedOpacity/255.0f;
+        color4.g *= _displayedOpacity/255.0f;
+        color4.b *= _displayedOpacity/255.0f;
+    }
+
+    cocos2d::TextureAtlas* textureAtlas;
+    V3F_C4B_T2F_Quad *quads;
+    for (auto&& batchNode:_batchNodes)
+    {
+        textureAtlas = batchNode->getTextureAtlas();
+        quads = textureAtlas->getQuads();
+        auto count = textureAtlas->getTotalQuads();
+
+        for (int index = 0; index < count; ++index)
+        {
+            quads[index].bl.colors = color4;
+            quads[index].br.colors = color4;
+            quads[index].tl.colors = color4;
+            quads[index].tr.colors = color4;
+            textureAtlas->updateQuad(&quads[index], index);
+        }
+    }
+}
+
+std::string Label::getDescription() const
+{
+    char tmp[50];
+    sprintf(tmp, "<Label | Tag = %d, Label = >", _tag);
+    std::string ret = tmp;
+    ret += _utf8Text;
+
+    return ret;
+}
+
+const Size& Label::getContentSize() const
+{
+    if (_systemFontDirty || _contentDirty)
+    {
+        const_cast<Label*>(this)->updateContent();
+    }
+    return _contentSize;
+}
+
+Rect Label::getBoundingBox() const
+{
+    const_cast<Label*>(this)->getContentSize();
+
+    return Node::getBoundingBox();
+}
+
+void Label::setBlendFunc(const BlendFunc &blendFunc)
+{
+    _blendFunc = blendFunc;
+    _blendFuncDirty = true;
+    if (_textSprite)
+    {
+        _textSprite->setBlendFunc(blendFunc);
+        if (_shadowNode)
+        {
+            _shadowNode->setBlendFunc(blendFunc);
+        }
+    }
+}
+
+void Label::removeAllChildrenWithCleanup(bool cleanup)
+{
+    Node::removeAllChildrenWithCleanup(cleanup);
+    _letters.clear();
+}
+
+void Label::removeChild(Node* child, bool cleanup /* = true */)
+{
+    Node::removeChild(child, cleanup);
+    for (auto&& it : _letters)
+    {
+        if (it.second == child)
+        {
+            _letters.erase(it.first);
+            break;
+        }
+    }
+}
+
+FontDefinition Label::_getFontDefinition() const
+{
+    FontDefinition systemFontDef;
+
+    std::string fontName = _systemFont;
+    if (_fontAtlas && !_fontAtlas->getFontName().empty()) fontName = _fontAtlas->getFontName();
+
+    systemFontDef._fontName = fontName;
+    systemFontDef._fontSize = _systemFontSize;
+    systemFontDef._alignment = _hAlignment;
+    systemFontDef._vertAlignment = _vAlignment;
+    systemFontDef._dimensions.width = _labelWidth;
+    systemFontDef._dimensions.height = _labelHeight;
+    systemFontDef._fontFillColor.r = _textColor.r;
+    systemFontDef._fontFillColor.g = _textColor.g;
+    systemFontDef._fontFillColor.b = _textColor.b;
+    systemFontDef._fontAlpha = _textColor.a;
+    systemFontDef._shadow._shadowEnabled = false;
+    systemFontDef._enableWrap = _enableWrap;
+    systemFontDef._overflow = (int)_overflow;
+
+    if (_currLabelEffect == LabelEffect::OUTLINE && _outlineSize > 0.f)
+    {
+        systemFontDef._stroke._strokeEnabled = true;
+        systemFontDef._stroke._strokeSize = _outlineSize;
+        systemFontDef._stroke._strokeColor.r = _effectColorF.r * 255;
+        systemFontDef._stroke._strokeColor.g = _effectColorF.g * 255;
+        systemFontDef._stroke._strokeColor.b = _effectColorF.b * 255;
+        systemFontDef._stroke._strokeAlpha = _effectColorF.a * 255;
+    }
+    else
+    {
+        systemFontDef._stroke._strokeEnabled = false;
+    }
+
+#if (CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID) && (CC_TARGET_PLATFORM != CC_PLATFORM_IOS)
+    if (systemFontDef._stroke._strokeEnabled)
+    {
+        CCLOGERROR("Stroke Currently only supported on iOS and Android!");
+    }
+    systemFontDef._stroke._strokeEnabled = false;
+#endif
+
+    return systemFontDef;
+}
+
+void Label::setGlobalZOrder(float globalZOrder)
+{
+    Node::setGlobalZOrder(globalZOrder);
+    if (_textSprite)
+    {
+        _textSprite->setGlobalZOrder(globalZOrder);
+        if (_shadowNode)
+        {
+            _shadowNode->setGlobalZOrder(globalZOrder);
+        }
+    }
+}
+
+float Label::getRenderingFontSize()const
+{
+    float fontSize;
+    if (_currentLabelType == LabelType::BMFONT) {
+        fontSize = _bmFontSize;
+    }else if(_currentLabelType == LabelType::TTF){
+        fontSize = this->getTTFConfig().fontSize;
+    }else if(_currentLabelType == LabelType::STRING_TEXTURE){
+        fontSize = _systemFontSize;
+    }else{ //FIXME: find a way to calculate char map font size
+        fontSize = this->getLineHeight();
+    }
+    return fontSize;
+}
+
+void Label::enableWrap(bool enable)
+{
+    if(enable == _enableWrap || _overflow == Overflow::RESIZE_HEIGHT){
+        return;
+    }
+
+    this->_enableWrap = enable;
+   
+    this->rescaleWithOriginalFontSize();
+    
+    _contentDirty = true;
+}
+
+bool Label::isWrapEnabled()const
+{
+    return this->_enableWrap;
+}
+
+void Label::setOverflow(Overflow overflow)
+{
+    if(_overflow == overflow){
+        return;
+    }
+    
+    if (_currentLabelType == LabelType::CHARMAP) {
+        if (overflow == Overflow::SHRINK) {
+            return;
+        }
+    }
+
+    if(overflow == Overflow::RESIZE_HEIGHT){
+        this->setDimensions(_labelDimensions.width,0);
+        this->enableWrap(true);
+    }
+    _overflow = overflow;
+    
+    this->rescaleWithOriginalFontSize();
+    
+    _contentDirty = true;
+}
+
+void Label::rescaleWithOriginalFontSize()
+{
+    auto renderingFontSize = this->getRenderingFontSize();
+    if (_originalFontSize - renderingFontSize >= 1) {
+        this->scaleFontSizeDown(_originalFontSize);
+    }
+}
+
+Label::Overflow Label::getOverflow()const
+{
+    return _overflow;
+}
+
+void Label::updateLetterSpriteScale(Sprite* sprite)
+{
+    if (_currentLabelType == LabelType::BMFONT && _bmFontSize > 0)
+    {
+        sprite->setScale(_bmfontScale);
+    }
+    else
+    {
+        if (std::abs(_bmFontSize) < FLT_EPSILON)
+        {
+            sprite->setScale(0);
+        }
+        else
+        {
+            sprite->setScale(1.0);
+        }
+    }
+}
+
+NS_CC_END

+ 828 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLabel.h

@@ -0,0 +1,828 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#ifndef _COCOS2D_CCLABEL_H_
+#define _COCOS2D_CCLABEL_H_
+
+#include "2d/CCNode.h"
+#include "renderer/CCCustomCommand.h"
+#include "renderer/CCQuadCommand.h"
+#include "2d/CCFontAtlas.h"
+#include "base/ccTypes.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+#define CC_DEFAULT_FONT_LABEL_SIZE  12
+
+/**
+ * @struct TTFConfig
+ * @see `GlyphCollection`
+ */
+typedef struct _ttfConfig
+{
+    std::string fontFilePath;
+    float fontSize;
+
+    GlyphCollection glyphs;
+    const char *customGlyphs;
+
+    bool distanceFieldEnabled;
+    int outlineSize;
+
+    bool italics;
+    bool bold;
+    bool underline;
+    bool strikethrough;
+
+    _ttfConfig(const std::string& filePath = "",float size = CC_DEFAULT_FONT_LABEL_SIZE, const GlyphCollection& glyphCollection = GlyphCollection::DYNAMIC,
+        const char *customGlyphCollection = nullptr, bool useDistanceField = false, int outline = 0,
+               bool useItalics = false, bool useBold = false, bool useUnderline = false, bool useStrikethrough = false)
+        : fontFilePath(filePath)
+        , fontSize(size)
+        , glyphs(glyphCollection)
+        , customGlyphs(customGlyphCollection)
+        , distanceFieldEnabled(useDistanceField)
+        , outlineSize(outline)
+        , italics(useItalics)
+        , bold(useBold)
+        , underline(useUnderline)
+        , strikethrough(useStrikethrough)
+    {
+        if(outline > 0)
+        {
+            distanceFieldEnabled = false;
+        }
+    }
+} TTFConfig;
+
+class Sprite;
+class SpriteBatchNode;
+class DrawNode;
+class EventListenerCustom;
+class TextureAtlas;
+
+/**
+ * @brief Label is a subclass of Node that knows how to render text labels.
+ *
+ * Label can be created with:
+ * - A true type font file.
+ * - A bitmap font file.
+ * - A char map file.
+ * - The built in system font.
+ *
+ * Bitmap Font supported editors:
+ * - http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)
+ * - http://www.n4te.com/hiero/hiero.jnlp (Free, Java)
+ * - http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)
+ * - http://www.angelcode.com/products/bmfont/ (Free, Windows only)
+ * @js NA
+ */
+class CC_DLL Label : public Node, public LabelProtocol, public BlendProtocol
+{
+public:
+    enum class Overflow
+    {
+        //In NONE mode, the dimensions is (0,0) and the content size will change dynamically to fit the label.
+        NONE,
+        /**
+         *In CLAMP mode, when label content goes out of the bounding box, it will be clipped.
+         */
+        CLAMP,
+        /**
+         * In SHRINK mode, the font size will change dynamically to adapt the content size.
+         */
+        SHRINK,
+        /**
+         *In RESIZE_HEIGHT mode, you can only change the width of label and the height is changed automatically.
+         */
+        RESIZE_HEIGHT
+    };
+    
+    enum class LabelType {
+        TTF,
+        BMFONT,
+        CHARMAP,
+        STRING_TEXTURE
+    };
+    
+    /// @name Creators
+    /// @{
+
+    /**
+    * Allocates and initializes a Label, with default settings.
+    *
+    * @return An automatically released Label object.
+    */
+    static Label* create();
+
+    /**
+     * Allocates and initializes a Label, base on platform-dependent API.
+     *
+     * @param text The initial text.
+     * @param font A font file or a font family name.
+     * @param fontSize The font size. This value must be > 0.
+     * @param dimensions
+     * @param hAlignment The text horizontal alignment.
+     * @param vAlignment The text vertical alignment.
+     *
+     * @warning It will generate texture by the platform-dependent code.
+     *
+     * @return An automatically released Label object.
+     */
+    static Label* createWithSystemFont(const std::string& text, const std::string& font, float fontSize,
+        const Size& dimensions = Size::ZERO, TextHAlignment hAlignment = TextHAlignment::LEFT,
+        TextVAlignment vAlignment = TextVAlignment::TOP);
+
+    /**
+    * Allocates and initializes a Label, base on FreeType2.
+    *
+    * @param text The initial text.
+    * @param fontFilePath A font file.
+    * @param fontSize The font size. This value must be > 0.
+    * @param dimensions
+    * @param hAlignment The text horizontal alignment.
+    * @param vAlignment The text vertical alignment.
+    *
+    * @return An automatically released Label object.
+    */
+    static Label * createWithTTF(const std::string& text, const std::string& fontFilePath, float fontSize,
+        const Size& dimensions = Size::ZERO, TextHAlignment hAlignment = TextHAlignment::LEFT,
+        TextVAlignment vAlignment = TextVAlignment::TOP);
+
+    /**
+    * Allocates and initializes a Label, base on FreeType2.
+    *
+    * @param ttfConfig A pointer to a TTFConfig object.
+    * @param text The initial text.
+    * @param hAlignment The text horizontal alignment.
+    * @param maxLineWidth The max line width.
+    *
+    * @return An automatically released Label object.
+    * @see TTFConfig setTTFConfig setMaxLineWidth
+    */
+    static Label* createWithTTF(const TTFConfig& ttfConfig, const std::string& text, 
+        TextHAlignment hAlignment = TextHAlignment::LEFT, int maxLineWidth = 0);
+
+    /**
+    * Allocates and initializes a Label, with a bitmap font file.
+    *
+    * @param bmfontPath A bitmap font file, it's a FNT format.
+    * @param text The initial text.
+    * @param hAlignment Text horizontal alignment.
+    * @param maxLineWidth The max line width.
+    * @param imageOffset
+    *
+    * @return An automatically released Label object.
+    * @see setBMFontFilePath setMaxLineWidth
+    */
+    static Label* createWithBMFont(const std::string& bmfontPath, const std::string& text,
+        const TextHAlignment& hAlignment = TextHAlignment::LEFT, int maxLineWidth = 0,
+        const Vec2& imageOffset = Vec2::ZERO);
+
+    /**
+    * Allocates and initializes a Label, with char map configuration.
+    *
+    * @param charMapFile A char map file, it's a PNG format.
+    * @param itemWidth The width in points of each element.
+    * @param itemHeight The height in points of each element.
+    * @param startCharMap The starting char of the char map.
+    *
+    * @return An automatically released Label object.
+    */
+    static Label * createWithCharMap(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap);
+
+    /**
+    * Allocates and initializes a Label, with char map configuration.
+    *
+    * @param texture A pointer to an existing Texture2D object.
+    * @param itemWidth The width in points of each element.
+    * @param itemHeight The height in points of each element.
+    * @param startCharMap The starting char of the char map.
+    *
+    * @return An automatically released Label object.
+    */
+    static Label * createWithCharMap(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap);
+
+    /**
+    * Allocates and initializes a Label, with char map configuration.
+    *
+    * @param plistFile A configuration file of char map.
+    *
+    * @return An automatically released Label object.
+    */
+    static Label * createWithCharMap(const std::string& plistFile);
+
+    //  end of creators group
+    /// @}
+
+    /// @{
+    /// @name Font methods
+
+    /**
+     * Sets a new TTF configuration to Label.
+     * @see `TTFConfig`
+     */
+    virtual bool setTTFConfig(const TTFConfig& ttfConfig);
+
+    /**
+     * Returns the TTF configuration object used by the Label.
+     * @see `TTFConfig`
+     */
+    virtual const TTFConfig& getTTFConfig() const { return _fontConfig;}
+
+    /** Sets a new bitmap font to Label */
+    virtual bool setBMFontFilePath(const std::string& bmfontFilePath, const Vec2& imageOffset = Vec2::ZERO, float fontSize = 0);
+
+    /** Returns the bitmap font used by the Label.*/
+    const std::string& getBMFontFilePath() const { return _bmFontPath;}
+
+    /**
+     * Sets a new char map configuration to Label.
+     *
+     * @see `createWithCharMap(const std::string&,int,int,int)`
+     */
+    virtual bool setCharMap(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap);
+
+    /**
+     * Sets a new char map configuration to Label.
+     *
+     * @see `createWithCharMap(Texture2D*,int,int,int)`
+     */
+    virtual bool setCharMap(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap);
+
+    /**
+     * Sets a new char map configuration to Label.
+     *
+     * @see `createWithCharMap(const std::string&)`
+     */
+    virtual bool setCharMap(const std::string& plistFile);
+
+    /**
+     * Sets a new system font to Label.
+     *
+     * @param font A font file or a font family name.
+     * @warning
+     */
+    virtual void setSystemFontName(const std::string& font);
+
+    /** Returns the system font used by the Label.*/
+    virtual const std::string& getSystemFontName() const { return _systemFont;}
+
+    /* Sets the system font size of Label.*/
+    virtual void setSystemFontSize(float fontSize);
+
+    /** Returns the bitmap font path used by the Label.*/
+    virtual float getSystemFontSize() const { return _systemFontSize;}
+
+    /**
+     * @warning This method is not recommended for game developers.
+     */
+    virtual void requestSystemFontRefresh() { _systemFontDirty = true;}
+
+    //  end of font methods
+    /// @}
+
+    /** Sets the text that this Label is to display.*/
+    virtual void setString(const std::string& text) override;
+
+    /** Return the text the Label is currently displaying.*/
+    virtual const std::string& getString() const override {  return _utf8Text; }
+
+    /**
+     * Return the number of lines of text.
+     */
+    int getStringNumLines();
+
+    /**
+     * Return length of string.
+     */
+    int getStringLength();
+
+    /**
+     * Sets the text color of Label.
+     *
+     * The text color is different from the color of Node.
+     *
+     * @warning Limiting use to only when the Label created with true type font or system font.
+     */
+    virtual void setTextColor(const Color4B &color);
+
+    /** Returns the text color of the Label.*/
+    const Color4B& getTextColor() const { return _textColor;}
+
+    /**
+     * Enable shadow effect to Label.
+     *
+     * @todo Support blur for shadow effect.
+     */
+    virtual void enableShadow(const Color4B& shadowColor = Color4B::BLACK,const Size &offset = Size(2,-2), int blurRadius = 0);
+
+    /**
+     * Enable outline effect to Label.
+     * @warning Limiting use to only when the Label created with true type font or system font.
+     */
+    virtual void enableOutline(const Color4B& outlineColor,int outlineSize = -1);
+
+    /**
+    * Enable glow effect to Label.
+    * @warning Limiting use to only when the Label created with true type font.
+    */
+    virtual void enableGlow(const Color4B& glowColor);
+
+    /**
+     * Enable italics rendering
+     */
+    void enableItalics();
+
+    /**
+     * Enable bold rendering
+     */
+    void enableBold();
+
+    /**
+     * Enable underline
+     */
+    void enableUnderline();
+
+    /**
+     * Enables strikethrough.
+     * Underline and Strikethrough cannot be enabled at the same time.
+     * Strikethrough is like an underline but at the middle of the glyph
+     */
+    void enableStrikethrough();
+    /**
+     * Disable all effect applied to Label.
+     * @warning Please use disableEffect(LabelEffect::ALL) instead of this API.
+     */
+    virtual void disableEffect();
+
+    /**
+     * Disable effect to Label.
+     *
+     * @see `LabelEffect`
+     */
+    virtual void disableEffect(LabelEffect effect);
+
+    /**
+    * Return whether the shadow effect is enabled.
+    */
+    bool isShadowEnabled() const { return _shadowEnabled; }
+
+    /**
+    * Return shadow effect offset value.
+    */
+    Size getShadowOffset() const { return _shadowOffset; }
+
+    /**
+    * Return the shadow effect blur radius.
+    */
+    float getShadowBlurRadius() const { return _shadowBlurRadius; }
+
+    /**
+    * Return the shadow effect color value.
+    */
+    Color4F getShadowColor() const { return _shadowColor4F; }
+
+    /**
+    * Return the outline effect size value.
+    */
+    float getOutlineSize() const { return _outlineSize; }
+
+    /**
+    * Return current effect type.
+    */
+    LabelEffect getLabelEffectType() const { return _currLabelEffect; }
+
+    /**
+    * Return current effect color value.
+    */
+    Color4F getEffectColor() const { return _effectColorF; }
+
+    /** Sets the Label's text horizontal alignment.*/
+    void setAlignment(TextHAlignment hAlignment) { setAlignment(hAlignment,_vAlignment);}
+
+    /** Returns the Label's text horizontal alignment.*/
+    TextHAlignment getTextAlignment() const { return _hAlignment;}
+
+    /** Sets the Label's text alignment.*/
+    void setAlignment(TextHAlignment hAlignment,TextVAlignment vAlignment);
+
+    /** Sets the Label's text horizontal alignment.*/
+    void setHorizontalAlignment(TextHAlignment hAlignment) { setAlignment(hAlignment,_vAlignment); }
+
+    /** Returns the Label's text horizontal alignment.*/
+    TextHAlignment getHorizontalAlignment() const { return _hAlignment; }
+
+    /** Sets the Label's text vertical alignment.*/
+    void setVerticalAlignment(TextVAlignment vAlignment) { setAlignment(_hAlignment,vAlignment); }
+
+    /** Returns the Label's text vertical alignment.*/
+    TextVAlignment getVerticalAlignment() const { return _vAlignment; }
+
+    /**
+     * Specify what happens when a line is too long for Label.
+     *
+     * @param breakWithoutSpace Lines are automatically broken between words if this value is false.
+     */
+    void setLineBreakWithoutSpace(bool breakWithoutSpace);
+
+    /**
+     * Makes the Label at most this line untransformed width.
+     * The Label's max line width be used for force line breaks if the value not equal zero.
+     */
+    void setMaxLineWidth(float maxLineWidth);
+    float getMaxLineWidth() { return _maxLineWidth; }
+    /**
+     * Change font size of label type BMFONT
+     * Note: This function only scale the BMFONT letter to mimic the font size change effect.
+     *
+     * @param fontSize The desired font size in float.
+     */
+    void setBMFontSize(float fontSize);
+
+    /**
+     * Return the user define BMFont size.
+     *
+     * @return The BMFont size in float value.
+     */
+    float getBMFontSize()const;
+
+    /**
+     * Toggle wrap option of the label.
+     * Note: System font doesn't support manually toggle wrap.
+     *
+     * @param enable Set true to enable wrap and false to disable wrap.
+     */
+    void enableWrap(bool enable);
+
+    /**
+     * Query the wrap is enabled or not.
+     * Note: System font will always return true.
+     */
+    bool isWrapEnabled()const;
+
+    /**
+     * Change the label's Overflow type, currently only TTF and BMFont support all the valid Overflow type.
+     * Char Map font supports all the Overflow type except for SHRINK, because we can't measure it's font size.
+     * System font only support Overflow::Normal and Overflow::RESIZE_HEIGHT.
+     *
+     * @param overflow   see `Overflow`
+     */
+    void setOverflow(Overflow overflow);
+
+    /**
+     * Query the label's Overflow type.
+     *
+     * @return see `Overflow`
+     */
+    Overflow getOverflow()const;
+    /**
+     * Makes the Label exactly this untransformed width.
+     *
+     * The Label's width be used for text align if the value not equal zero.
+                                                           */
+    void setWidth(float width) { setDimensions(width,_labelHeight);}
+    float getWidth() const { return _labelWidth; }
+
+    /**
+     * Makes the Label exactly this untransformed height.
+     *
+     * The Label's height be used for text align if the value not equal zero.
+     * The text will display incomplete if the size of Label is not large enough to display all text.
+     */
+    void setHeight(float height){ setDimensions(_labelWidth, height); }
+    float getHeight() const { return _labelHeight; }
+
+    /** Sets the untransformed size of the Label in a more efficient way. */
+    void setDimensions(float width, float height);
+    const Size& getDimensions() const{ return _labelDimensions;}
+
+    /** Update content immediately.*/
+    virtual void updateContent();
+
+    /**
+     * Provides a way to treat each character like a Sprite.
+     * @warning No support system font.
+     */
+    virtual Sprite * getLetter(int lettetIndex);
+
+    /** Clips upper and lower margin to reduce height of Label.*/
+    void setClipMarginEnabled(bool clipEnabled) { _clipEnabled = clipEnabled; }
+
+    bool isClipMarginEnabled() const { return _clipEnabled; }
+
+    /** Sets the line height of the Label.
+     * @warning Not support system font.
+     * @since v3.2.0
+     */
+    void setLineHeight(float height);
+
+    /**
+     * Returns the line height of this Label.
+     * @warning Not support system font.
+     * @since v3.2.0
+     */
+    float getLineHeight() const;
+
+    void setLineSpacing(float height);
+    float getLineSpacing() const;
+    
+    /**
+     Returns type of label
+     
+     @warning Not support system font.
+     @return the type of label
+     @since v3.18.0
+     */
+    LabelType getLabelType() const { return _currentLabelType; }
+    
+    /**
+     Returns font size
+     */
+    float getRenderingFontSize()const;
+
+    /**
+     * Sets the additional kerning of the Label.
+     *
+     * @warning Not support system font.
+     * @since v3.2.0
+     */
+    void setAdditionalKerning(float space);
+
+    /**
+     * Returns the additional kerning of the Label.
+     *
+     * @warning Not support system font.
+     * @since v3.2.0
+     */
+    float getAdditionalKerning() const;
+
+    /**
+    * set ProgramState of current render command
+    */
+    virtual void setProgramState(backend::ProgramState *programState) override;
+
+    FontAtlas* getFontAtlas() { return _fontAtlas; }
+
+    virtual const BlendFunc& getBlendFunc() const override { return _blendFunc; }
+    virtual void setBlendFunc(const BlendFunc &blendFunc) override;
+
+    virtual bool isOpacityModifyRGB() const override { return _isOpacityModifyRGB; }
+    virtual void setOpacityModifyRGB(bool isOpacityModifyRGB) override;
+    virtual void updateDisplayedColor(const Color3B& parentColor) override;
+    virtual void updateDisplayedOpacity(uint8_t parentOpacity) override;
+
+    virtual std::string getDescription() const override;
+
+    virtual const Size& getContentSize() const override;
+    virtual Rect getBoundingBox() const override;
+
+    virtual void visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;
+    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
+
+    virtual void setCameraMask(unsigned short mask, bool applyChildren = true) override;
+
+    virtual void removeAllChildrenWithCleanup(bool cleanup) override;
+    virtual void removeChild(Node* child, bool cleanup = true) override;
+    virtual void setGlobalZOrder(float globalZOrder) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * Constructor of Label.
+     * @js NA
+     */
+    Label(TextHAlignment hAlignment = TextHAlignment::LEFT,
+      TextVAlignment vAlignment = TextVAlignment::TOP);
+
+    /**
+     * Destructor of Label.
+     * @js NA
+     * @lua NA
+     */
+    virtual ~Label();
+
+    bool initWithTTF(const std::string& text, const std::string& fontFilePath, float fontSize,
+                     const Size& dimensions = Size::ZERO, TextHAlignment hAlignment = TextHAlignment::LEFT,
+                     TextVAlignment vAlignment = TextVAlignment::TOP);
+
+    bool initWithTTF(const TTFConfig& ttfConfig, const std::string& text,
+                     TextHAlignment hAlignment = TextHAlignment::LEFT, int maxLineWidth = 0);
+
+protected:
+    struct LetterInfo
+    {
+        char32_t utf32Char;
+        bool valid;
+        float positionX;
+        float positionY;
+        int atlasIndex;
+        int lineIndex;
+    };
+
+    struct BatchCommand {
+        BatchCommand();
+        ~BatchCommand();
+
+        CustomCommand textCommand;
+        CustomCommand outLineCommand;
+        CustomCommand shadowCommand;
+
+        std::array<CustomCommand*, 3> getCommandArray();
+    };
+
+    virtual void setFontAtlas(FontAtlas* atlas, bool distanceFieldEnabled = false, bool useA8Shader = false);
+    bool getFontLetterDef(char32_t character, FontLetterDefinition& letterDef) const;
+
+    void computeStringNumLines();
+
+    void drawSelf(bool visibleByCamera, Renderer* renderer, uint32_t flags);
+
+    bool multilineTextWrapByChar();
+    bool multilineTextWrapByWord();
+    bool multilineTextWrap(const std::function<int(const std::u32string&, int, int)>& lambda);
+    void shrinkLabelToContentSize(const std::function<bool(void)>& lambda);
+    bool isHorizontalClamp();
+    bool isVerticalClamp();
+    void rescaleWithOriginalFontSize();
+
+    void updateLabelLetters();
+    virtual bool alignText();
+    void computeAlignmentOffset();
+    bool computeHorizontalKernings(const std::u32string& stringToRender);
+
+    void recordLetterInfo(const cocos2d::Vec2& point, char32_t utf32Char, int letterIndex, int lineIndex);
+    void recordPlaceholderInfo(int letterIndex, char32_t utf16Char);
+    
+    bool updateQuads();
+
+    void createSpriteForSystemFont(const FontDefinition& fontDef);
+    void createShadowSpriteForSystemFont(const FontDefinition& fontDef);
+
+    virtual void updateShaderProgram();
+    void updateBMFontScale();
+    void scaleFontSizeDown(float fontSize);
+    bool setTTFConfigInternal(const TTFConfig& ttfConfig);
+    void setBMFontSizeInternal(float fontSize);
+    bool isHorizontalClamped(float letterPositionX, int lineIndex);
+    void restoreFontSize();
+    void updateLetterSpriteScale(Sprite* sprite);
+    int getFirstCharLen(const std::u32string& utf32Text, int startIndex, int textLen) const;
+    int getFirstWordLen(const std::u32string& utf32Text, int startIndex, int textLen) const;
+
+    void reset();
+
+    FontDefinition _getFontDefinition() const;
+
+    virtual void updateColor() override;
+    
+    void updateUniformLocations();
+    void setVertexLayout(PipelineDescriptor& vertexLayout);
+    void updateBlendState();
+    void updateEffectUniforms(BatchCommand &batch, TextureAtlas* textureAtlas, Renderer *renderer, const Mat4 &transform);
+    void updateBuffer(TextureAtlas* textureAtlas, CustomCommand& customCommand);
+
+    void updateBatchCommand(BatchCommand &batch);
+
+    LabelType _currentLabelType;
+    bool _contentDirty;
+    std::u32string _utf32Text;
+    std::string _utf8Text;
+    int _numberOfLines;
+
+    std::string _bmFontPath;
+    TTFConfig _fontConfig;
+    float _outlineSize;
+
+    bool _systemFontDirty;
+    std::string _systemFont;
+    float _systemFontSize;
+    Sprite* _textSprite;
+    Sprite* _shadowNode;
+
+    FontAtlas* _fontAtlas;
+    Vector<SpriteBatchNode*> _batchNodes;
+    std::vector<LetterInfo> _lettersInfo;
+
+    //! used for optimization
+    Sprite *_reusedLetter;
+    Rect _reusedRect;
+    int _lengthOfString;
+
+    //layout relevant properties.
+    float _lineHeight;
+    float _lineSpacing;
+    float _additionalKerning;
+    int* _horizontalKernings;
+    bool _lineBreakWithoutSpaces;
+    float _maxLineWidth;
+    Size _labelDimensions;
+    float _labelWidth;
+    float _labelHeight;
+    TextHAlignment _hAlignment;
+    TextVAlignment _vAlignment;
+
+    float _textDesiredHeight;
+    std::vector<float> _linesWidth;
+    std::vector<float> _linesOffsetX;
+    float _letterOffsetY;
+    float _tailoredTopY;
+    float _tailoredBottomY;
+
+    LabelEffect _currLabelEffect;
+    Color4F _effectColorF;
+    Color4B _textColor;
+    Color4F _textColorF;
+
+    QuadCommand _quadCommand;
+
+    std::vector<BatchCommand> _batchCommands;
+    
+    Mat4  _shadowTransform;
+    int  _uniformEffectColor;
+    int  _uniformEffectType; // 0: None, 1: Outline, 2: Shadow; Only used when outline is enabled.
+    int  _uniformTextColor;
+    bool _useDistanceField;
+    bool _useA8Shader;
+
+    bool _shadowDirty;
+    bool _shadowEnabled;
+    Size _shadowOffset;
+    
+    Color4F _shadowColor4F;
+    Color3B _shadowColor3B;
+    uint8_t _shadowOpacity;
+    float _shadowBlurRadius;
+
+    bool _clipEnabled;
+    bool _blendFuncDirty;
+    BlendFunc _blendFunc;
+
+    /// whether or not the label was inside bounds the previous frame
+    bool _insideBounds;
+
+    bool _isOpacityModifyRGB;
+
+    std::unordered_map<int, Sprite*> _letters;
+
+    EventListenerCustom* _purgeTextureListener;
+    EventListenerCustom* _resetTextureListener;
+
+#if CC_LABEL_DEBUG_DRAW
+    DrawNode* _debugDrawNode;
+#endif
+
+    bool _enableWrap;
+    float _bmFontSize;
+    float _bmfontScale;
+    Overflow _overflow;
+    float _originalFontSize;
+
+    bool _boldEnabled;
+    DrawNode* _underlineNode;
+    bool _strikethroughEnabled;
+    
+    backend::UniformLocation _mvpMatrixLocation;
+    backend::UniformLocation _textureLocation;
+    backend::UniformLocation _alphaTextureLocation;
+    backend::UniformLocation _textColorLocation;
+    backend::UniformLocation _effectColorLocation;
+    backend::UniformLocation _effectTypeLocation;
+    
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Label);
+};
+
+// end group
+/// @}
+
+NS_CC_END
+
+#endif /*__COCOS2D_CCLABEL_H */

+ 278 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLabelAtlas.cpp

@@ -0,0 +1,278 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCLabelAtlas.h"
+#include "renderer/CCTextureAtlas.h"
+#include "platform/CCFileUtils.h"
+#include "base/CCDirector.h"
+#include "base/ccUTF8.h"
+#include "renderer/CCTextureCache.h"
+
+#if CC_LABELATLAS_DEBUG_DRAW
+#include "renderer/CCRenderer.h"
+#endif
+
+NS_CC_BEGIN
+
+//CCLabelAtlas - Creation & Init
+
+LabelAtlas* LabelAtlas::create()
+{
+    LabelAtlas* ret = new (std::nothrow) LabelAtlas();
+    if (ret)
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_RELEASE_NULL(ret);
+    }
+    
+    return ret;
+}
+
+LabelAtlas* LabelAtlas::create(const std::string& string, const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap)
+{
+    LabelAtlas* ret = new (std::nothrow) LabelAtlas();
+    if(ret && ret->initWithString(string, charMapFile, itemWidth, itemHeight, startCharMap))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool LabelAtlas::initWithString(const std::string& string, const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap)
+{
+    Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(charMapFile);
+    return initWithString(string, texture, itemWidth, itemHeight, startCharMap);
+}
+
+bool LabelAtlas::initWithString(const std::string& string, Texture2D* texture, int itemWidth, int itemHeight, int startCharMap)
+{
+    if (AtlasNode::initWithTexture(texture, itemWidth, itemHeight, static_cast<int>(string.size())))
+    {
+        _mapStartChar = startCharMap;
+        this->setString(string);
+        return true;
+    }
+    return false;
+}
+
+LabelAtlas* LabelAtlas::create(const std::string& string, const std::string& fntFile)
+{    
+    LabelAtlas *ret = new (std::nothrow) LabelAtlas();
+    if (ret)
+    {
+        if (ret->initWithString(string, fntFile))
+        {
+            ret->autorelease();
+        }
+        else 
+        {
+            CC_SAFE_RELEASE_NULL(ret);
+        }
+    }
+    
+    return ret;
+}
+
+bool LabelAtlas::initWithString(const std::string& theString, const std::string& fntFile)
+{
+    std::string pathStr = FileUtils::getInstance()->fullPathForFilename(fntFile);
+    std::string relPathStr = pathStr.substr(0, pathStr.find_last_of('/'))+"/";
+    
+    ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(pathStr);
+
+    CCASSERT(dict["version"].asInt() == 1, "Unsupported version. Upgrade cocos2d version");
+
+    std::string textureFilename = relPathStr + dict["textureFilename"].asString();
+
+    unsigned int width = dict["itemWidth"].asInt() / CC_CONTENT_SCALE_FACTOR();
+    unsigned int height = dict["itemHeight"].asInt() / CC_CONTENT_SCALE_FACTOR();
+    unsigned int startChar = dict["firstChar"].asInt();
+
+
+    this->initWithString(theString, textureFilename, width, height, startChar);
+
+    return true;
+}
+
+//CCLabelAtlas - Atlas generation
+void LabelAtlas::updateAtlasValues()
+{
+    if(_itemsPerRow == 0)
+    {
+        return;
+    }
+
+    ssize_t n = _string.length();
+
+    const unsigned char *s = (unsigned char*)_string.c_str();
+
+    Texture2D *texture = _textureAtlas->getTexture();
+    float textureWide = (float) texture->getPixelsWide();
+    float textureHigh = (float) texture->getPixelsHigh();
+    float itemWidthInPixels = _itemWidth * CC_CONTENT_SCALE_FACTOR();
+    float itemHeightInPixels = _itemHeight * CC_CONTENT_SCALE_FACTOR();
+    if (_ignoreContentScaleFactor)
+    {
+        itemWidthInPixels = _itemWidth;
+        itemHeightInPixels = _itemHeight;
+    }
+
+    CCASSERT(n <= _textureAtlas->getCapacity(), "updateAtlasValues: Invalid String length");
+    V3F_C4B_T2F_Quad* quads = _textureAtlas->getQuads();
+    for(ssize_t i = 0; i < n; i++) {
+
+        unsigned char a = s[i] - _mapStartChar;
+        float row = (float) (a % _itemsPerRow);
+        float col = (float) (a / _itemsPerRow);
+
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+        // Issue #938. Don't use texStepX & texStepY
+        float left        = (2 * row * itemWidthInPixels + 1) / (2 * textureWide);
+        float right        = left + (itemWidthInPixels * 2 - 2) / (2 * textureWide);
+        float top        = (2 * col * itemHeightInPixels + 1) / (2 * textureHigh);
+        float bottom    = top + (itemHeightInPixels * 2 - 2) / (2 * textureHigh);
+#else
+        float left        = row * itemWidthInPixels / textureWide;
+        float right        = left + itemWidthInPixels / textureWide;
+        float top        = col * itemHeightInPixels / textureHigh;
+        float bottom    = top + itemHeightInPixels / textureHigh;
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+
+        quads[i].tl.texCoords.u = left;
+        quads[i].tl.texCoords.v = top;
+        quads[i].tr.texCoords.u = right;
+        quads[i].tr.texCoords.v = top;
+        quads[i].bl.texCoords.u = left;
+        quads[i].bl.texCoords.v = bottom;
+        quads[i].br.texCoords.u = right;
+        quads[i].br.texCoords.v = bottom;
+
+        quads[i].bl.vertices.x = (float) (i * _itemWidth);
+        quads[i].bl.vertices.y = 0;
+        quads[i].bl.vertices.z = 0.0f;
+        quads[i].br.vertices.x = (float)(i * _itemWidth + _itemWidth);
+        quads[i].br.vertices.y = 0;
+        quads[i].br.vertices.z = 0.0f;
+        quads[i].tl.vertices.x = (float)(i * _itemWidth);
+        quads[i].tl.vertices.y = (float)(_itemHeight);
+        quads[i].tl.vertices.z = 0.0f;
+        quads[i].tr.vertices.x = (float)(i * _itemWidth + _itemWidth);
+        quads[i].tr.vertices.y = (float)(_itemHeight);
+        quads[i].tr.vertices.z = 0.0f;
+        Color4B c(_displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity);
+        quads[i].tl.colors = c;
+        quads[i].tr.colors = c;
+        quads[i].bl.colors = c;
+        quads[i].br.colors = c;
+    }
+    if (n > 0 ){
+        _textureAtlas->setDirty(true);
+        ssize_t totalQuads = _textureAtlas->getTotalQuads();
+        if (n > totalQuads) {
+            _textureAtlas->increaseTotalQuadsWith(static_cast<int>(n - totalQuads));
+        }
+    }
+}
+
+//CCLabelAtlas - LabelProtocol
+void LabelAtlas::setString(const std::string &label)
+{
+    ssize_t len = label.size();
+    if (len > _textureAtlas->getTotalQuads())
+    {
+        _textureAtlas->resizeCapacity(len);
+    }
+    _string.clear();
+    _string = label;
+    this->updateAtlasValues();
+
+    Size s = Size(len * _itemWidth, _itemHeight);
+
+    this->setContentSize(s);
+
+    _quadsToDraw = len;
+}
+
+const std::string& LabelAtlas::getString() const
+{
+    return _string;
+}
+
+void LabelAtlas::updateColor()
+{
+    if (_textureAtlas)
+    {
+        Color4B color4( _displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity );
+        if (_isOpacityModifyRGB)
+        {
+            color4.r *= _displayedOpacity/255.0f;
+            color4.g *= _displayedOpacity/255.0f;
+            color4.b *= _displayedOpacity/255.0f;
+        }
+        auto quads = _textureAtlas->getQuads();
+        ssize_t length = _string.length();
+        for (int index = 0; index < length; index++)
+        {
+            quads[index].bl.colors = color4;
+            quads[index].br.colors = color4;
+            quads[index].tl.colors = color4;
+            quads[index].tr.colors = color4;
+            _textureAtlas->updateQuad(&quads[index], index);
+        }
+    }
+}
+
+//CCLabelAtlas - draw
+#if CC_LABELATLAS_DEBUG_DRAW
+void LabelAtlas::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
+{
+    AtlasNode::draw(renderer, transform, _transformUpdated);
+
+    _debugDrawNode->clear();
+    auto size = getContentSize();
+    Vec2 vertices[4]=
+    {
+        Vec2::ZERO,
+        Vec2(size.width, 0),
+        Vec2(size.width, size.height),
+        Vec2(0.0f, size.height)
+    };
+    _debugDrawNode->drawPoly(vertices, 4, true, Color4F(1.0, 1.0, 1.0, 1.0));
+}
+#endif
+
+std::string LabelAtlas::getDescription() const
+{
+    return StringUtils::format("<LabelAtlas | Tag = %d, Label = '%s'>", _tag, _string.c_str());
+}
+
+NS_CC_END

+ 133 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLabelAtlas.h

@@ -0,0 +1,133 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __CCLABEL_ATLAS_H__
+#define __CCLABEL_ATLAS_H__
+
+#include "2d/CCAtlasNode.h"
+#if CC_LABELATLAS_DEBUG_DRAW
+#include "renderer/CCCustomCommand.h"
+#include "2d/CCDrawNode.h"
+#endif
+NS_CC_BEGIN
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** 
+ * @class LabelAtlas
+ * @brief LabelAtlas is a subclass of AtlasNode.
+ *
+ * It can be as a replacement of Label since it is MUCH faster.
+ * 
+ * LabelAtlas versus Label:
+ * - LabelAtlas is MUCH faster than Label.
+ * - LabelAtlas "characters" have a fixed height and width.
+ * - LabelAtlas "characters" can be anything you want since they are taken from an image file.
+ * 
+ * A more flexible class is LabelBMFont. It supports variable width characters and it also has a nice editor.
+ */
+class CC_DLL LabelAtlas : public AtlasNode, public LabelProtocol
+{
+public:
+    /** 
+     * Creates an empty LabelAtlas.
+     * User need to call initWithString(...) later to make this object work properly.
+     */
+    static LabelAtlas* create();
+    
+    /** Creates the LabelAtlas with a string, a char map file(the atlas), the width and height of each element and the starting char of the atlas. */
+    static LabelAtlas* create(const std::string& string, const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap);
+    
+    /**
+     * Creates the LabelAtlas with a string and a configuration file.
+     * @since v2.0
+     */
+    static LabelAtlas* create(const std::string& string, const std::string& fntFile);
+
+    /** Initializes the LabelAtlas with a string, a char map file(the atlas), the width and height of each element and the starting char of the atlas. */
+    bool initWithString(const std::string& string, const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap);
+    
+    /** 
+     * Initializes the LabelAtlas with a string and a configuration file.
+     * @since v2.0
+     */
+    bool initWithString(const std::string& string, const std::string& fntFile);
+    
+    /** Initializes the LabelAtlas with a string, a texture, the width and height in points of each element and the starting char of the atlas */
+    bool initWithString(const std::string& string, Texture2D* texture, int itemWidth, int itemHeight, int startCharMap);
+    
+    virtual void setString(const std::string &label) override;
+    virtual const std::string& getString() const override;
+
+    virtual void updateAtlasValues() override;
+	/**
+     * @js NA
+     */
+    virtual std::string getDescription() const override;
+
+#if CC_LABELATLAS_DEBUG_DRAW
+    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
+#endif
+
+CC_CONSTRUCTOR_ACCESS:
+    LabelAtlas()
+    :_string("")
+    {
+#if CC_LABELATLAS_DEBUG_DRAW
+        _debugDrawNode = DrawNode::create();
+        addChild(_debugDrawNode);
+#endif
+    }
+
+    virtual ~LabelAtlas()
+    {
+        _string.clear();
+    }
+    
+protected:
+    virtual void updateColor() override;
+
+#if CC_LABELATLAS_DEBUG_DRAW
+    DrawNode *_debugDrawNode;
+#endif
+
+    // string to render
+    std::string _string;
+    // the first char in the char map
+    int _mapStartChar;
+};
+
+// end group
+/// @}
+
+
+NS_CC_END
+
+#endif //__CCLABEL_ATLAS_H__

+ 439 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLabelTextFormatter.cpp

@@ -0,0 +1,439 @@
+/****************************************************************************
+ Copyright (c) 2013      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#include "2d/CCLabel.h"
+#include <vector>
+#include "base/ccUTF8.h"
+#include "base/CCDirector.h"
+#include "2d/CCFontAtlas.h"
+#include "2d/CCFontFNT.h"
+
+NS_CC_BEGIN
+
+void Label::computeAlignmentOffset()
+{
+    _linesOffsetX.clear();
+    switch (_hAlignment)
+    {
+    case cocos2d::TextHAlignment::LEFT:
+        _linesOffsetX.assign(_numberOfLines, 0);
+        break;
+    case cocos2d::TextHAlignment::CENTER:
+        for (auto lineWidth : _linesWidth)
+        {
+            _linesOffsetX.push_back((_contentSize.width - lineWidth) / 2.f);
+        }
+        break;
+    case cocos2d::TextHAlignment::RIGHT:
+        for (auto lineWidth : _linesWidth)
+        {
+            _linesOffsetX.push_back(_contentSize.width - lineWidth);
+        }
+        break;
+    default:
+        break;
+    }
+
+    switch (_vAlignment)
+    {
+    case cocos2d::TextVAlignment::TOP:
+        _letterOffsetY = _contentSize.height;
+        break;
+    case cocos2d::TextVAlignment::CENTER:
+        _letterOffsetY = (_contentSize.height + _textDesiredHeight) / 2.f;
+        break;
+    case cocos2d::TextVAlignment::BOTTOM:
+        _letterOffsetY = _textDesiredHeight;
+        break;
+    default:
+        break;
+    }
+}
+
+int Label::getFirstCharLen(const std::u32string& /*utf32Text*/, int /*startIndex*/, int /*textLen*/) const
+{
+    return 1;
+}
+
+int Label::getFirstWordLen(const std::u32string& utf32Text, int startIndex, int textLen) const
+{
+    int len = 0;
+    auto nextLetterX = 0;
+    FontLetterDefinition letterDef;
+    auto contentScaleFactor = CC_CONTENT_SCALE_FACTOR();
+
+    for (int index = startIndex; index < textLen; ++index)
+    {
+        char32_t character = utf32Text[index];
+
+        if (character == StringUtils::UnicodeCharacters::NewLine
+            || (!StringUtils::isUnicodeNonBreaking(character)
+                && (StringUtils::isUnicodeSpace(character)
+                    || StringUtils::isCJKUnicode(character))))
+        {
+            break;
+        }
+
+        if (!getFontLetterDef(character, letterDef))
+        {
+            break;
+        }
+
+        if (_maxLineWidth > 0.f)
+        {
+            auto letterX = (nextLetterX + letterDef.offsetX * _bmfontScale) / contentScaleFactor;
+
+            if (letterX + letterDef.width * _bmfontScale > _maxLineWidth)
+                break;
+        }
+
+        nextLetterX += letterDef.xAdvance * _bmfontScale + _additionalKerning;
+
+        len++;
+    }
+
+    if (len == 0 && textLen)
+        len = 1;
+
+    return len;
+}
+
+bool Label::getFontLetterDef(char32_t character, FontLetterDefinition& letterDef) const
+{
+    if (character == StringUtils::UnicodeCharacters::NoBreakSpace)
+    {
+        // change no-break space to regular space
+        // reason: some fonts have issue with no-break space:
+        //   * no letter definition
+        //   * not normal big width
+        character = StringUtils::UnicodeCharacters::Space;
+    }
+
+    return _fontAtlas->getLetterDefinitionForChar(character, letterDef);
+}
+
+void Label::updateBMFontScale()
+{
+    auto font = _fontAtlas->getFont();
+    if (_currentLabelType == LabelType::BMFONT) {
+        FontFNT *bmFont = (FontFNT*)font;
+        float originalFontSize = bmFont->getOriginalFontSize();
+        _bmfontScale = _bmFontSize * CC_CONTENT_SCALE_FACTOR() / originalFontSize;
+    }else{
+        _bmfontScale = 1.0f;
+    }
+}
+
+bool Label::multilineTextWrap(const std::function<int(const std::u32string&, int, int)>& nextTokenLen)
+{
+    int textLen = getStringLength();
+    int lineIndex = 0;
+    float nextTokenX = 0.f;
+    float nextTokenY = 0.f;
+    float longestLine = 0.f;
+    float letterRight = 0.f;
+    float nextWhitespaceWidth = 0.f;
+
+    auto contentScaleFactor = CC_CONTENT_SCALE_FACTOR();
+    float lineSpacing = _lineSpacing * contentScaleFactor;
+    float highestY = 0.f;
+    float lowestY = 0.f;
+    FontLetterDefinition letterDef;
+    Vec2 letterPosition;
+    bool nextChangeSize = true;
+
+    this->updateBMFontScale();
+
+    for (int index = 0; index < textLen; )
+    {
+        char32_t character = _utf32Text[index];
+        if (character == StringUtils::UnicodeCharacters::NewLine)
+        {
+            _linesWidth.push_back(letterRight);
+            letterRight = 0.f;
+            lineIndex++;
+            nextTokenX = 0.f;
+            nextTokenY -= _lineHeight*_bmfontScale + lineSpacing;
+            recordPlaceholderInfo(index, character);
+            index++;
+            continue;
+        }
+
+        auto tokenLen = nextTokenLen(_utf32Text, index, textLen);
+        float tokenHighestY = highestY;
+        float tokenLowestY = lowestY;
+        float tokenRight = letterRight;
+        float nextLetterX = nextTokenX;
+        float whitespaceWidth = nextWhitespaceWidth;
+        bool newLine = false;
+        for (int tmp = 0; tmp < tokenLen;++tmp)
+        {
+            int letterIndex = index + tmp;
+            character = _utf32Text[letterIndex];
+            if (character == StringUtils::UnicodeCharacters::CarriageReturn)
+            {
+                recordPlaceholderInfo(letterIndex, character);
+                continue;
+            }
+
+            // \b - Next char not change x position
+            if (character == StringUtils::UnicodeCharacters::NextCharNoChangeX)
+            {
+                nextChangeSize = false;
+                recordPlaceholderInfo(letterIndex, character);
+                continue;
+            }
+
+            if (!getFontLetterDef(character, letterDef))
+            {
+                recordPlaceholderInfo(letterIndex, character);
+                CCLOG("LabelTextFormatter error: can't find letter definition in font file for letter: 0x%x", character);
+                continue;
+            }
+
+            auto letterX = (nextLetterX + letterDef.offsetX * _bmfontScale) / contentScaleFactor;
+            if (_enableWrap && _maxLineWidth > 0.f && nextTokenX > 0.f && letterX + letterDef.width * _bmfontScale > _maxLineWidth
+                && !StringUtils::isUnicodeSpace(character) && nextChangeSize)
+            {
+                _linesWidth.push_back(letterRight - whitespaceWidth);
+                nextWhitespaceWidth = 0.f;
+                letterRight = 0.f;
+                lineIndex++;
+                nextTokenX = 0.f;
+                nextTokenY -= (_lineHeight*_bmfontScale + lineSpacing);
+                newLine = true;
+                break;
+            }
+            else
+            {
+                letterPosition.x = letterX;
+            }
+            letterPosition.y = (nextTokenY - letterDef.offsetY * _bmfontScale) / contentScaleFactor;
+            recordLetterInfo(letterPosition, character, letterIndex, lineIndex);
+
+            if (nextChangeSize)
+            {
+                float newLetterWidth = 0.f;
+                if (_horizontalKernings && letterIndex < textLen - 1)
+                    newLetterWidth = _horizontalKernings[letterIndex + 1];
+                newLetterWidth += letterDef.xAdvance * _bmfontScale + _additionalKerning;
+
+                nextLetterX += newLetterWidth;
+                tokenRight = nextLetterX / contentScaleFactor;
+
+                if (StringUtils::isUnicodeSpace(character))
+                {
+                    nextWhitespaceWidth += newLetterWidth / contentScaleFactor;
+                }
+                else
+                {
+                    nextWhitespaceWidth = 0;
+                }
+            }
+            nextChangeSize = true;
+
+            if (tokenHighestY < letterPosition.y)
+                tokenHighestY = letterPosition.y;
+            if (tokenLowestY > letterPosition.y - letterDef.height * _bmfontScale)
+                tokenLowestY = letterPosition.y - letterDef.height * _bmfontScale;
+        }
+
+        if (newLine)
+        {
+            continue;
+        }
+
+        nextTokenX = nextLetterX;
+        letterRight = tokenRight;
+        if (highestY < tokenHighestY)
+            highestY = tokenHighestY;
+        if (lowestY > tokenLowestY)
+            lowestY = tokenLowestY;
+
+        index += tokenLen;
+    }
+
+    if (_linesWidth.empty())
+    {
+        _linesWidth.push_back(letterRight);
+        longestLine = letterRight;
+    }
+    else
+    {
+        _linesWidth.push_back(letterRight - nextWhitespaceWidth);
+        for (auto && lineWidth : _linesWidth)
+        {
+            if (longestLine < lineWidth)
+                longestLine = lineWidth;
+        }
+    }
+
+    _numberOfLines = lineIndex + 1;
+    _textDesiredHeight = (_numberOfLines * _lineHeight * _bmfontScale) / contentScaleFactor;
+    if (_numberOfLines > 1)
+        _textDesiredHeight += (_numberOfLines - 1) * _lineSpacing;
+    Size contentSize(_labelWidth, _labelHeight);
+    if (_labelWidth <= 0.f)
+        contentSize.width = longestLine;
+    if (_labelHeight <= 0.f)
+        contentSize.height = _textDesiredHeight;
+    setContentSize(contentSize);
+
+    _tailoredTopY = contentSize.height;
+    _tailoredBottomY = 0.f;
+    if (highestY > 0.f)
+        _tailoredTopY = contentSize.height + highestY;
+    if (lowestY < -_textDesiredHeight)
+        _tailoredBottomY = _textDesiredHeight + lowestY;
+
+    return true;
+}
+
+bool Label::multilineTextWrapByWord()
+{
+    return multilineTextWrap(CC_CALLBACK_3(Label::getFirstWordLen, this));
+}
+
+bool Label::multilineTextWrapByChar()
+{
+    return multilineTextWrap(CC_CALLBACK_3(Label::getFirstCharLen, this));
+}
+
+bool Label::isVerticalClamp()
+{
+    if (_textDesiredHeight > _contentSize.height)
+    {
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+bool Label::isHorizontalClamp()
+{
+    bool letterClamp = false;
+    for (int ctr = 0; ctr < _lengthOfString; ++ctr)
+    {
+        if (_lettersInfo[ctr].valid)
+        {
+            auto& letterDef = _fontAtlas->_letterDefinitions[_lettersInfo[ctr].utf32Char];
+
+            auto px = _lettersInfo[ctr].positionX + letterDef.width/2 * _bmfontScale;
+            auto lineIndex = _lettersInfo[ctr].lineIndex;
+
+            if(_labelWidth > 0.f){
+                if(!this->_enableWrap){
+                    if (px > _contentSize.width) {
+                        letterClamp = true;
+                        break;
+                    }
+                }
+                else{
+                    auto wordWidth = this->_linesWidth[lineIndex];
+                    if(wordWidth > this->_contentSize.width && (px > _contentSize.width)){
+                        letterClamp = true;
+                        break;
+                    }
+
+                }
+
+            }
+        }
+    }
+
+    return letterClamp;
+}
+
+void Label::shrinkLabelToContentSize(const std::function<bool(void)>& lambda)
+{
+    float fontSize = this->getRenderingFontSize();
+
+    int i = 0;
+    auto letterDefinition = _fontAtlas->_letterDefinitions;
+    auto tempLetterDefinition = letterDefinition;
+    float originalLineHeight = _lineHeight;
+    bool flag = true;
+    while (lambda()) {
+        ++i;
+        float newFontSize = fontSize - i;
+        flag = false;
+        if (newFontSize <= 0) {
+            break;
+        }
+        float scale = newFontSize / fontSize;
+        std::swap(_fontAtlas->_letterDefinitions, tempLetterDefinition);
+        _fontAtlas->scaleFontLetterDefinition(scale);
+        this->setLineHeight(originalLineHeight * scale);
+        if (_maxLineWidth > 0.f && !_lineBreakWithoutSpaces)
+        {
+            multilineTextWrapByWord();
+        }
+        else
+        {
+            multilineTextWrapByChar();
+        }
+        computeAlignmentOffset();
+        tempLetterDefinition = letterDefinition;
+    }
+    this->setLineHeight(originalLineHeight);
+    std::swap(_fontAtlas->_letterDefinitions, letterDefinition);
+
+    if (!flag) {
+        if (fontSize - i >= 0) {
+            this->scaleFontSizeDown(fontSize - i);
+        }
+    }
+}
+
+void Label::recordLetterInfo(const cocos2d::Vec2& point, char32_t utf32Char, int letterIndex, int lineIndex)
+{
+    if (static_cast<std::size_t>(letterIndex) >= _lettersInfo.size())
+    {
+        LetterInfo tmpInfo;
+        _lettersInfo.push_back(tmpInfo);
+    }
+    _lettersInfo[letterIndex].lineIndex = lineIndex;
+    _lettersInfo[letterIndex].utf32Char = utf32Char;
+    _lettersInfo[letterIndex].valid = _fontAtlas->_letterDefinitions[utf32Char].validDefinition;
+    _lettersInfo[letterIndex].positionX = point.x;
+    _lettersInfo[letterIndex].positionY = point.y;
+    _lettersInfo[letterIndex].atlasIndex = -1;
+}
+
+void Label::recordPlaceholderInfo(int letterIndex, char32_t utf32Char)
+{
+    if (static_cast<std::size_t>(letterIndex) >= _lettersInfo.size())
+    {
+        LetterInfo tmpInfo;
+        _lettersInfo.push_back(tmpInfo);
+    }
+    _lettersInfo[letterIndex].utf32Char = utf32Char;
+    _lettersInfo[letterIndex].valid = false;
+}
+
+NS_CC_END

+ 24 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLabelTextFormatter.h

@@ -0,0 +1,24 @@
+/****************************************************************************
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+

+ 1090 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLayer.cpp

@@ -0,0 +1,1090 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include <stdarg.h>
+#include "2d/CCLayer.h"
+#include "base/CCScriptSupport.h"
+#include "base/ccUtils.h"
+#include "platform/CCDevice.h"
+#include "renderer/CCRenderer.h"
+#include "base/CCDirector.h"
+#include "base/CCEventDispatcher.h"
+#include "base/CCEventListenerTouch.h"
+#include "base/CCEventTouch.h"
+#include "base/CCEventKeyboard.h"
+#include "base/CCEventListenerKeyboard.h"
+#include "base/CCEventAcceleration.h"
+#include "base/CCEventListenerAcceleration.h"
+#include "base/ccUTF8.h"
+#include "renderer/backend/Buffer.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/ProgramState.h"
+
+#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
+#include "platform/desktop/CCGLViewImpl-desktop.h"
+#endif
+
+NS_CC_BEGIN
+
+// Layer
+Layer::Layer()
+: _touchEnabled(false)
+, _accelerometerEnabled(false)
+, _keyboardEnabled(false)
+, _touchListener(nullptr)
+, _keyboardListener(nullptr)
+, _accelerationListener(nullptr)
+, _touchMode(Touch::DispatchMode::ALL_AT_ONCE)
+, _swallowsTouches(true)
+{
+    _ignoreAnchorPointForPosition = true;
+    setAnchorPoint(Vec2(0.5f, 0.5f));
+}
+
+Layer::~Layer()
+{
+
+}
+
+bool Layer::init()
+{
+    Director * director = Director::getInstance();
+    setContentSize(director->getWinSize());
+    return true;
+}
+
+Layer *Layer::create()
+{
+    Layer *ret = new (std::nothrow) Layer();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+        return ret;
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+        return nullptr;
+    }
+}
+
+int Layer::executeScriptTouchHandler(EventTouch::EventCode eventType, Touch* touch, Event* event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        TouchScriptData data(eventType, this, touch, event);
+        ScriptEvent scriptEvent(kTouchEvent, &data);
+        return ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);
+    }
+#else
+    CC_UNUSED_PARAM(eventType);
+    CC_UNUSED_PARAM(touch);
+    CC_UNUSED_PARAM(event);
+#endif
+    return 0;
+}
+
+int Layer::executeScriptTouchesHandler(EventTouch::EventCode eventType, const std::vector<Touch*>& touches, Event* event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        TouchesScriptData data(eventType, this, touches, event);
+        ScriptEvent scriptEvent(kTouchesEvent, &data);
+        return ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);
+    }
+#else
+    CC_UNUSED_PARAM(eventType);
+    CC_UNUSED_PARAM(touches);
+    CC_UNUSED_PARAM(event);
+#endif
+    return 0;
+}
+
+
+void Layer::onAcceleration(Acceleration* acc, Event* /*unused_event*/)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if(kScriptTypeNone != _scriptType)
+    {
+        BasicScriptData data(this,(void*)acc);
+        ScriptEvent event(kAccelerometerEvent,&data);
+        ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&event);
+    }
+#else
+    CC_UNUSED_PARAM(acc);
+#endif
+}
+
+void Layer::onKeyPressed(EventKeyboard::KeyCode /*keyCode*/, Event* /*unused_event*/)
+{
+}
+
+void Layer::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* /*unused_event*/)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if(kScriptTypeNone != _scriptType)
+    {
+        KeypadScriptData data(keyCode, this);
+        ScriptEvent event(kKeypadEvent,&data);
+        ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&event);
+    }
+#else
+    CC_UNUSED_PARAM(keyCode);
+#endif
+}
+
+/// Callbacks
+
+bool Layer::onTouchBegan(Touch *touch, Event *event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        return executeScriptTouchHandler(EventTouch::EventCode::BEGAN, touch, event) == 0 ? false : true;
+    }
+#else
+    CC_UNUSED_PARAM(touch);
+    CC_UNUSED_PARAM(event);
+#endif
+    CCASSERT(false, "Layer#ccTouchBegan override me");
+    return true;
+}
+
+void Layer::onTouchMoved(Touch *touch, Event *event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        executeScriptTouchHandler(EventTouch::EventCode::MOVED, touch, event);
+        return;
+    }
+#else
+    CC_UNUSED_PARAM(touch);
+    CC_UNUSED_PARAM(event);
+#endif
+}
+
+void Layer::onTouchEnded(Touch *touch, Event *event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        executeScriptTouchHandler(EventTouch::EventCode::ENDED, touch, event);
+        return;
+    }
+#else
+    CC_UNUSED_PARAM(touch);
+    CC_UNUSED_PARAM(event);
+#endif
+}
+
+void Layer::onTouchCancelled(Touch *touch, Event *event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        executeScriptTouchHandler(EventTouch::EventCode::CANCELLED, touch, event);
+        return;
+    }
+#else
+    CC_UNUSED_PARAM(touch);
+    CC_UNUSED_PARAM(event);
+#endif
+}    
+
+void Layer::onTouchesBegan(const std::vector<Touch*>& touches, Event *event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        executeScriptTouchesHandler(EventTouch::EventCode::BEGAN, touches, event);
+        return;
+    }
+#else
+    CC_UNUSED_PARAM(touches);
+    CC_UNUSED_PARAM(event);
+#endif
+}
+
+void Layer::onTouchesMoved(const std::vector<Touch*>& touches, Event *event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        executeScriptTouchesHandler(EventTouch::EventCode::MOVED, touches, event);
+        return;
+    }
+#else
+    CC_UNUSED_PARAM(touches);
+    CC_UNUSED_PARAM(event);
+#endif
+}
+
+void Layer::onTouchesEnded(const std::vector<Touch*>& touches, Event *event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        executeScriptTouchesHandler(EventTouch::EventCode::ENDED, touches, event);
+        return;
+    }
+#else
+    CC_UNUSED_PARAM(touches);
+    CC_UNUSED_PARAM(event);
+#endif
+}
+
+void Layer::onTouchesCancelled(const std::vector<Touch*>& touches, Event *event)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (kScriptTypeLua == _scriptType)
+    {
+        executeScriptTouchesHandler(EventTouch::EventCode::CANCELLED, touches, event);
+        return;
+    }
+#else
+    CC_UNUSED_PARAM(touches);
+    CC_UNUSED_PARAM(event);
+#endif
+}
+
+std::string Layer::getDescription() const
+{
+    return StringUtils::format("<Layer | Tag = %d>", _tag);
+}
+
+/// LayerColor
+
+LayerColor::LayerColor()
+{
+    // default blend function
+    _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+    
+    auto& pipelineDescriptor = _customCommand.getPipelineDescriptor();
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_COLOR);
+    _programState = new (std::nothrow) backend::ProgramState(program);
+    pipelineDescriptor.programState = _programState;
+    
+    auto vertexLayout = _programState->getVertexLayout();
+    const auto& attributeInfo = _programState->getProgram()->getActiveAttributes();
+    auto iter = attributeInfo.find("a_position");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT3, 0, false);
+    }
+    iter = attributeInfo.find("a_color");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_color", iter->second.location, backend::VertexFormat::FLOAT4, sizeof(_vertexData[0].vertices), false);
+    }
+    vertexLayout->setLayout(sizeof(_vertexData[0]));
+    
+    _mvpMatrixLocation = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix");
+    
+    _customCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, 6, CustomCommand::BufferUsage::STATIC);
+    unsigned short indices[] = {0, 1, 2, 2, 1, 3};
+    _customCommand.updateIndexBuffer(indices, sizeof(indices));
+    
+    _customCommand.createVertexBuffer(sizeof(_vertexData[0]), 4, CustomCommand::BufferUsage::DYNAMIC);
+    
+    _customCommand.setDrawType(CustomCommand::DrawType::ELEMENT);
+    _customCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
+}
+    
+LayerColor::~LayerColor()
+{
+}
+
+/// blendFunc getter
+const BlendFunc &LayerColor::getBlendFunc() const
+{
+    return _blendFunc;
+}
+/// blendFunc setter
+void LayerColor::setBlendFunc(const BlendFunc &var)
+{
+    _blendFunc = var;
+}
+
+LayerColor* LayerColor::create()
+{
+    LayerColor* ret = new (std::nothrow) LayerColor();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+LayerColor * LayerColor::create(const Color4B& color, float width, float height)
+{
+    LayerColor * layer = new (std::nothrow) LayerColor();
+    if( layer && layer->initWithColor(color,width,height))
+    {
+        layer->autorelease();
+        return layer;
+    }
+    CC_SAFE_DELETE(layer);
+    return nullptr;
+}
+
+LayerColor * LayerColor::create(const Color4B& color)
+{
+    LayerColor * layer = new (std::nothrow) LayerColor();
+    if(layer && layer->initWithColor(color))
+    {
+        layer->autorelease();
+        return layer;
+    }
+    CC_SAFE_DELETE(layer);
+    return nullptr;
+}
+
+bool LayerColor::init()
+{
+    Size s = Director::getInstance()->getWinSize();
+    return initWithColor(Color4B(0,0,0,0), s.width, s.height);
+}
+
+bool LayerColor::initWithColor(const Color4B& color, float w, float h)
+{
+    if (Layer::init())
+    {
+
+        // default blend function
+        _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
+
+        _displayedColor.r = _realColor.r = color.r;
+        _displayedColor.g = _realColor.g = color.g;
+        _displayedColor.b = _realColor.b = color.b;
+        _displayedOpacity = _realOpacity = color.a;
+
+        for (size_t i = 0; i<sizeof(_squareVertices) / sizeof( _squareVertices[0]); i++ )
+        {
+            _squareVertices[i].x = 0.0f;
+            _squareVertices[i].y = 0.0f;
+        }
+
+        updateColor();
+        setContentSize(Size(w, h));
+
+        return true;
+    }
+    return false;
+}
+
+bool LayerColor::initWithColor(const Color4B& color)
+{
+    Size s = Director::getInstance()->getWinSize();
+    return initWithColor(color, s.width, s.height);
+}
+
+/// override contentSize
+void LayerColor::setContentSize(const Size & size)
+{
+    _squareVertices[1].x = size.width;
+    _squareVertices[2].y = size.height;
+    _squareVertices[3].x = size.width;
+    _squareVertices[3].y = size.height;
+
+    Layer::setContentSize(size);
+}
+
+void LayerColor::changeWidthAndHeight(float w ,float h)
+{
+    this->setContentSize(Size(w, h));
+}
+
+void LayerColor::changeWidth(float w)
+{
+    this->setContentSize(Size(w, _contentSize.height));
+}
+
+void LayerColor::changeHeight(float h)
+{
+    this->setContentSize(Size(_contentSize.width, h));
+}
+
+void LayerColor::updateColor()
+{
+    for (int i = 0; i < 4; i++ )
+    {
+        _vertexData[i].colors.r = _displayedColor.r / 255.0f;
+        _vertexData[i].colors.g = _displayedColor.g / 255.0f;
+        _vertexData[i].colors.b = _displayedColor.b / 255.0f;
+        _vertexData[i].colors.a = _displayedOpacity / 255.0f;
+    }
+}
+
+void LayerColor::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
+{    
+    _customCommand.init(_globalZOrder, _blendFunc);
+    renderer->addCommand(&_customCommand);
+    
+    cocos2d::Mat4 projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    auto& pipelineDescriptor = _customCommand.getPipelineDescriptor();
+    pipelineDescriptor.programState->setUniform(_mvpMatrixLocation, projectionMat.m, sizeof(projectionMat.m));
+    
+    for(int i = 0; i < 4; ++i)
+    {
+        Vec4 pos;
+        pos.x = _squareVertices[i].x; pos.y = _squareVertices[i].y; pos.z = _positionZ;
+        pos.w = 1;
+        _modelViewTransform.transformVector(&pos);
+        _vertexData[i].vertices = Vec3(pos.x,pos.y,pos.z)/pos.w;
+    }
+    updateVertexBuffer();
+}
+
+void LayerColor::updateVertexBuffer()
+{
+    _customCommand.updateVertexBuffer(_vertexData, sizeof(_vertexData));
+}
+
+//
+// LayerGradient
+//
+LayerGradient::LayerGradient()
+{
+}
+
+LayerGradient::~LayerGradient()
+{
+}
+
+LayerGradient* LayerGradient::create(const Color4B& start, const Color4B& end)
+{
+    LayerGradient * layer = new (std::nothrow) LayerGradient();
+    if( layer && layer->initWithColor(start, end))
+    {
+        layer->autorelease();
+        return layer;
+    }
+    CC_SAFE_DELETE(layer);
+    return nullptr;
+}
+
+LayerGradient* LayerGradient::create(const Color4B& start, const Color4B& end, const Vec2& v)
+{
+    LayerGradient * layer = new (std::nothrow) LayerGradient();
+    if( layer && layer->initWithColor(start, end, v))
+    {
+        layer->autorelease();
+        return layer;
+    }
+    CC_SAFE_DELETE(layer);
+    return nullptr;
+}
+
+LayerGradient* LayerGradient::create()
+{
+    LayerGradient* ret = new (std::nothrow) LayerGradient();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool LayerGradient::init()
+{
+    return initWithColor(Color4B(0, 0, 0, 255), Color4B(0, 0, 0, 255));
+}
+
+bool LayerGradient::initWithColor(const Color4B& start, const Color4B& end)
+{
+    return initWithColor(start, end, Vec2(0, -1));
+}
+
+bool LayerGradient::initWithColor(const Color4B& start, const Color4B& end, const Vec2& v)
+{
+    _endColor.r  = end.r;
+    _endColor.g  = end.g;
+    _endColor.b  = end.b;
+
+    _endOpacity     = end.a;
+    _startOpacity   = start.a;
+    _alongVector    = v;
+
+    _compressedInterpolation = true;
+
+    return LayerColor::initWithColor(Color4B(start.r, start.g, start.b, 255));
+}
+
+void LayerGradient::updateColor()
+{
+    LayerColor::updateColor();
+
+    float h = _alongVector.getLength();
+    if (h == 0)
+        return;
+
+    float c = sqrtf(2.0f);
+    Vec2 u(_alongVector.x / h, _alongVector.y / h);
+
+    // Compressed Interpolation mode
+    if (_compressedInterpolation)
+    {
+        float h2 = 1 / ( fabsf(u.x) + fabsf(u.y) );
+        u = u * (h2 * (float)c);
+    }
+
+    float opacityf = (float)_displayedOpacity / 255.0f;
+
+    Color4F S(
+        _displayedColor.r / 255.0f,
+        _displayedColor.g / 255.0f,
+        _displayedColor.b / 255.0f,
+        _startOpacity * opacityf / 255.0f
+    );
+
+    Color4F E(
+        _endColor.r / 255.0f,
+        _endColor.g / 255.0f,
+        _endColor.b / 255.0f,
+        _endOpacity * opacityf / 255.0f
+    );
+
+    // (-1, -1)
+   _vertexData[0].colors.r = E.r + (S.r - E.r) * ((c + u.x + u.y) / (2.0f * c));
+   _vertexData[0].colors.g = E.g + (S.g - E.g) * ((c + u.x + u.y) / (2.0f * c));
+   _vertexData[0].colors.b = E.b + (S.b - E.b) * ((c + u.x + u.y) / (2.0f * c));
+   _vertexData[0].colors.a = E.a + (S.a - E.a) * ((c + u.x + u.y) / (2.0f * c));
+   // (1, -1)
+   _vertexData[1].colors.r = E.r + (S.r - E.r) * ((c - u.x + u.y) / (2.0f * c));
+   _vertexData[1].colors.g = E.g + (S.g - E.g) * ((c - u.x + u.y) / (2.0f * c));
+   _vertexData[1].colors.b = E.b + (S.b - E.b) * ((c - u.x + u.y) / (2.0f * c));
+   _vertexData[1].colors.a = E.a + (S.a - E.a) * ((c - u.x + u.y) / (2.0f * c));
+   // (-1, 1)
+   _vertexData[2].colors.r = E.r + (S.r - E.r) * ((c + u.x - u.y) / (2.0f * c));
+   _vertexData[2].colors.g = E.g + (S.g - E.g) * ((c + u.x - u.y) / (2.0f * c));
+   _vertexData[2].colors.b = E.b + (S.b - E.b) * ((c + u.x - u.y) / (2.0f * c));
+   _vertexData[2].colors.a = E.a + (S.a - E.a) * ((c + u.x - u.y) / (2.0f * c));
+   // (1, 1)
+   _vertexData[3].colors.r = E.r + (S.r - E.r) * ((c - u.x - u.y) / (2.0f * c));
+   _vertexData[3].colors.g = E.g + (S.g - E.g) * ((c - u.x - u.y) / (2.0f * c));
+   _vertexData[3].colors.b = E.b + (S.b - E.b) * ((c - u.x - u.y) / (2.0f * c));
+   _vertexData[3].colors.a = E.a + (S.a - E.a) * ((c - u.x - u.y) / (2.0f * c));
+}
+
+const Color3B& LayerGradient::getStartColor() const
+{
+    return _realColor;
+}
+
+void LayerGradient::setStartColor(const Color3B& color)
+{
+    setColor(color);
+}
+
+void LayerGradient::setEndColor(const Color3B& color)
+{
+    _endColor = color;
+    updateColor();
+}
+
+const Color3B& LayerGradient::getEndColor() const
+{
+    return _endColor;
+}
+
+void LayerGradient::setStartOpacity(uint8_t o)
+{
+    _startOpacity = o;
+    updateColor();
+}
+
+uint8_t LayerGradient::getStartOpacity() const
+{
+    return _startOpacity;
+}
+
+void LayerGradient::setEndOpacity(uint8_t o)
+{
+    _endOpacity = o;
+    updateColor();
+}
+
+uint8_t LayerGradient::getEndOpacity() const
+{
+    return _endOpacity;
+}
+
+void LayerGradient::setVector(const Vec2& var)
+{
+    _alongVector = var;
+    updateColor();
+}
+
+const Vec2& LayerGradient::getVector() const
+{
+    return _alongVector;
+}
+
+bool LayerGradient::isCompressedInterpolation() const
+{
+    return _compressedInterpolation;
+}
+
+void LayerGradient::setCompressedInterpolation(bool compress)
+{
+    _compressedInterpolation = compress;
+    updateColor();
+}
+
+std::string LayerGradient::getDescription() const
+{
+    return StringUtils::format("<LayerGradient | Tag = %d>", _tag);
+}
+
+/**
+ * LayerRadialGradient
+ */
+LayerRadialGradient* LayerRadialGradient::create(const Color4B& startColor, const Color4B& endColor, float radius, const Vec2& center, float expand)
+{
+    auto layerGradient = new LayerRadialGradient();
+    if (layerGradient && layerGradient->initWithColor(startColor, endColor, radius, center, expand))
+    {
+        layerGradient->autorelease();
+        return layerGradient;
+    }
+    
+    delete layerGradient;
+    return nullptr;
+}
+
+LayerRadialGradient* LayerRadialGradient::create()
+{
+    auto layerGradient = new LayerRadialGradient();
+    if (layerGradient && layerGradient->initWithColor(Color4B::BLACK, Color4B::BLACK, 0, Vec2(0,0), 0))
+    {
+        layerGradient->autorelease();
+        return layerGradient;
+    }
+    
+    delete layerGradient;
+    return nullptr;
+}
+
+LayerRadialGradient::LayerRadialGradient()
+{
+    auto& pipelineDescriptor = _customCommand.getPipelineDescriptor();
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::LAYER_RADIA_GRADIENT);
+    _programState = new (std::nothrow) backend::ProgramState(program);
+    pipelineDescriptor.programState = _programState;
+    _mvpMatrixLocation = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix");
+    _startColorLocation = pipelineDescriptor.programState->getUniformLocation("u_startColor");
+    _endColorLocation = pipelineDescriptor.programState->getUniformLocation("u_endColor");
+    _centerLocation = pipelineDescriptor.programState->getUniformLocation("u_center");
+    _radiusLocation = pipelineDescriptor.programState->getUniformLocation("u_radius");
+    _expandLocation = pipelineDescriptor.programState->getUniformLocation("u_expand");
+
+    auto vertexLayout = _programState->getVertexLayout();
+    const auto& attributeInfo = _programState->getProgram()->getActiveAttributes();
+    auto iter = attributeInfo.find("a_position");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT2, 0, false);
+    }
+    vertexLayout->setLayout(sizeof(_vertices[0]));
+
+    _customCommand.createVertexBuffer(sizeof(_vertices[0]), sizeof(_vertices) / sizeof(_vertices[0]), CustomCommand::BufferUsage::STATIC);
+    _customCommand.setDrawType(CustomCommand::DrawType::ARRAY);
+    _customCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE_STRIP);
+}
+
+LayerRadialGradient::~LayerRadialGradient()
+{
+}
+
+bool LayerRadialGradient::initWithColor(const cocos2d::Color4B &startColor, const cocos2d::Color4B &endColor, float radius, const Vec2& center, float expand)
+{
+    // should do it before Layer::init()
+    for (int i = 0; i < 4; ++i)
+        _vertices[i] = {0.0f, 0.0f};
+    
+    if (Layer::init())
+    {
+        convertColor4B24F(_startColorRend, startColor);
+        _startColor = startColor;
+        
+        convertColor4B24F(_endColorRend, endColor);
+        _endColor = endColor;
+        
+        _expand = expand;
+        
+        setRadius(radius);
+        setCenter(center);
+        
+        return true;
+    }
+    
+    return false;
+}
+
+void LayerRadialGradient::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
+{
+    _customCommand.init(_globalZOrder, _blendFunc);
+    renderer->addCommand(&_customCommand);
+
+    const auto& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    auto programState = _customCommand.getPipelineDescriptor().programState;
+    Mat4 finalMat = projectionMat * transform;
+    programState->setUniform(_mvpMatrixLocation, finalMat.m, sizeof(finalMat.m));
+
+    programState->setUniform(_startColorLocation, &_startColorRend, sizeof(_startColorRend));
+    programState->setUniform(_endColorLocation, &_endColorRend, sizeof(_endColorRend));
+    programState->setUniform(_centerLocation, &_center, sizeof(_center));
+    programState->setUniform(_radiusLocation, &_radius, sizeof(_radius));
+    programState->setUniform(_expandLocation, &_expand, sizeof(_expand));
+}
+
+void LayerRadialGradient::setContentSize(const Size& size)
+{
+    _vertices[1].x = size.width;
+    _vertices[2].y = size.height;
+    _vertices[3].x = size.width;
+    _vertices[3].y = size.height;
+    Layer::setContentSize(size);
+
+    _customCommand.updateVertexBuffer(_vertices, sizeof(_vertices));
+}
+
+void LayerRadialGradient::setStartOpacity(uint8_t opacity)
+{
+    _startColorRend.a = opacity / 255.0f;
+    _startColor.a = opacity;
+}
+
+uint8_t LayerRadialGradient::getStartOpacity() const
+{
+    return _startColor.a;
+}
+
+void LayerRadialGradient::setEndOpacity(uint8_t opacity)
+{
+    _endColorRend.a = opacity / 255.0f;
+    _endColor.a = opacity;
+}
+
+uint8_t LayerRadialGradient::getEndOpacity() const
+{
+    return _endColor.a;
+}
+
+void LayerRadialGradient::setRadius(float radius)
+{
+    _radius = radius;
+}
+
+float LayerRadialGradient::getRadius() const
+{
+    return _radius;
+}
+
+void LayerRadialGradient::setCenter(const Vec2& center)
+{
+    _center = center;
+}
+
+Vec2 LayerRadialGradient::getCenter() const
+{
+    return _center;
+}
+
+void LayerRadialGradient::setExpand(float expand)
+{
+    _expand = expand;
+}
+
+float LayerRadialGradient::getExpand() const
+{
+    return _expand;
+}
+
+void LayerRadialGradient::setStartColor(const Color3B& color)
+{
+    setStartColor(Color4B(color));
+}
+
+void LayerRadialGradient::setStartColor(const cocos2d::Color4B &color)
+{
+    _startColor = color;
+    convertColor4B24F(_startColorRend, _startColor);
+}
+
+Color4B LayerRadialGradient::getStartColor() const
+{
+    return _startColor;
+}
+
+Color3B LayerRadialGradient::getStartColor3B() const
+{
+    return Color3B(_startColor);
+}
+
+void LayerRadialGradient::setEndColor(const Color3B& color)
+{
+    setEndColor(Color4B(color));
+}
+
+void LayerRadialGradient::setEndColor(const cocos2d::Color4B &color)
+{
+    _endColor = color;
+    convertColor4B24F(_endColorRend, _endColor);
+}
+
+Color4B LayerRadialGradient::getEndColor() const
+{
+    return _endColor;
+}
+
+Color3B LayerRadialGradient::getEndColor3B() const
+{
+    return Color3B(_endColor);
+}
+
+void LayerRadialGradient::setBlendFunc(const BlendFunc& blendFunc)
+{
+    _blendFunc = blendFunc;
+}
+
+BlendFunc LayerRadialGradient::getBlendFunc() const
+{
+    return _blendFunc;
+}
+
+void LayerRadialGradient::convertColor4B24F(Color4F& outColor, const Color4B& inColor)
+{
+    outColor.r = inColor.r / 255.0f;
+    outColor.g = inColor.g / 255.0f;
+    outColor.b = inColor.b / 255.0f;
+    outColor.a = inColor.a / 255.0f;
+}
+
+
+/// MultiplexLayer
+
+LayerMultiplex::LayerMultiplex()
+: _enabledLayer(0)
+{
+}
+
+LayerMultiplex::~LayerMultiplex()
+{
+    for(const auto &layer : _layers) {
+        layer->cleanup();
+    }
+}
+
+LayerMultiplex * LayerMultiplex::create(Layer * layer, ...)
+{
+    va_list args;
+    va_start(args,layer);
+
+    LayerMultiplex * multiplexLayer = new (std::nothrow) LayerMultiplex();
+    if(multiplexLayer && multiplexLayer->initWithLayers(layer, args))
+    {
+        multiplexLayer->autorelease();
+        va_end(args);
+        return multiplexLayer;
+    }
+    va_end(args);
+    CC_SAFE_DELETE(multiplexLayer);
+    return nullptr;
+}
+
+LayerMultiplex * LayerMultiplex::createWithLayer(Layer* layer)
+{
+    return LayerMultiplex::create(layer, nullptr);
+}
+
+LayerMultiplex* LayerMultiplex::create()
+{
+    LayerMultiplex* ret = new (std::nothrow) LayerMultiplex();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+LayerMultiplex* LayerMultiplex::createWithArray(const Vector<Layer*>& arrayOfLayers)
+{
+    LayerMultiplex* ret = new (std::nothrow) LayerMultiplex();
+    if (ret && ret->initWithArray(arrayOfLayers))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+void LayerMultiplex::addLayer(Layer* layer)
+{
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    if (sEngine)
+    {
+        sEngine->retainScriptObject(this, layer);
+    }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    _layers.pushBack(layer);
+}
+
+bool LayerMultiplex::init()
+{
+    if (Layer::init())
+    {
+        _enabledLayer = 0;
+        return true;
+    }
+    return false;
+}
+
+bool LayerMultiplex::initWithLayers(Layer *layer, va_list params)
+{
+    if (Layer::init())
+    {
+        _layers.reserve(5);
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+        if (sEngine)
+        {
+            sEngine->retainScriptObject(this, layer);
+        }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        _layers.pushBack(layer);
+
+        Layer *l = va_arg(params,Layer*);
+        while( l ) {
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+            if (sEngine)
+            {
+                sEngine->retainScriptObject(this, l);
+            }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+            _layers.pushBack(l);
+            l = va_arg(params,Layer*);
+        }
+
+        _enabledLayer = 0;
+        this->addChild(_layers.at(_enabledLayer));
+        return true;
+    }
+
+    return false;
+}
+
+bool LayerMultiplex::initWithArray(const Vector<Layer*>& arrayOfLayers)
+{
+    if (Layer::init())
+    {
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+        if (sEngine)
+        {
+            for (const auto &layer : arrayOfLayers)
+            {
+                if (layer)
+                {
+                    sEngine->retainScriptObject(this, layer);
+                }
+            }
+        }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        _layers.reserve(arrayOfLayers.size());
+        _layers.pushBack(arrayOfLayers);
+
+        _enabledLayer = 0;
+        this->addChild(_layers.at(_enabledLayer));
+        return true;
+    }
+    return false;
+}
+
+void LayerMultiplex::switchTo(int n)
+{
+    
+    switchTo(n, true);
+}
+
+void LayerMultiplex::switchTo(int n, bool cleanup)
+{
+    CCASSERT( n < _layers.size(), "Invalid index in MultiplexLayer switchTo message" );
+    
+    this->removeChild(_layers.at(_enabledLayer), cleanup);
+    
+    _enabledLayer = n;
+    
+    this->addChild(_layers.at(n));
+}
+
+void LayerMultiplex::switchToAndReleaseMe(int n)
+{
+    CCASSERT( n < _layers.size(), "Invalid index in MultiplexLayer switchTo message" );
+
+    this->removeChild(_layers.at(_enabledLayer), true);
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    if (sEngine)
+    {
+        sEngine->releaseScriptObject(this, _layers.at(_enabledLayer));
+    }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    
+    _layers.replace(_enabledLayer, nullptr);
+
+    _enabledLayer = n;
+
+    this->addChild(_layers.at(n));
+}
+
+std::string LayerMultiplex::getDescription() const
+{
+    return StringUtils::format("<LayerMultiplex | Tag = %d, Layers = %d", _tag, static_cast<int>(_children.size()));
+}
+
+NS_CC_END

+ 623 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLayer.h

@@ -0,0 +1,623 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#pragma once
+
+#include "2d/CCNode.h"
+#include "base/CCProtocols.h"
+#include "renderer/CCCustomCommand.h"
+
+#include <vector>
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+class __Set;
+class TouchScriptHandlerEntry;
+
+class EventListenerTouch;
+class EventListenerKeyboard;
+class EventListenerAcceleration;
+
+class Touch;
+
+//
+// Layer
+//
+/** @class Layer
+ * @brief Layer is a subclass of Node that implements the TouchEventsDelegate protocol.
+
+All features from Node are valid, plus the following new features:
+- It can receive iPhone Touches
+- It can receive Accelerometer input
+*/
+class CC_DLL Layer : public Node
+{
+public:    
+    /** Creates a fullscreen black layer.
+     *
+     * @return An autoreleased Layer object.
+     */
+    static Layer *create();
+    
+    /* Callback function should not be deprecated, it will generate lots of warnings.
+       Since 'setTouchEnabled' was deprecated, it will make warnings if developer overrides onTouchXXX and invokes setTouchEnabled(true) instead of using EventDispatcher::addEventListenerWithXXX.
+    */
+    /** Callback function for touch began.
+     *
+     * @param touch Touch information.
+     * @param unused_event Event information.
+     * @return if return false, onTouchMoved, onTouchEnded, onTouchCancelled will never called.
+     * @js NA
+     */
+    virtual bool onTouchBegan(Touch *touch, Event *unused_event);
+    /** Callback function for touch moved.
+    *
+    * @param touch Touch information.
+    * @param unused_event Event information.
+    * @js NA
+    */
+    virtual void onTouchMoved(Touch *touch, Event *unused_event);
+    /** Callback function for touch ended.
+    *
+    * @param touch Touch information.
+    * @param unused_event Event information.
+    * @js NA
+    */
+    virtual void onTouchEnded(Touch *touch, Event *unused_event);
+    /** Callback function for touch cancelled.
+    *
+    * @param touch Touch information.
+    * @param unused_event Event information.
+    * @js NA
+    */
+    virtual void onTouchCancelled(Touch *touch, Event *unused_event);
+
+    /** Callback function for multiple touches began.
+    *
+    * @param touches Touches information.
+    * @param unused_event Event information.
+    * @js NA
+    */
+    virtual void onTouchesBegan(const std::vector<Touch*>& touches, Event *unused_event);
+    /** Callback function for multiple touches moved.
+    *
+    * @param touches Touches information.
+    * @param unused_event Event information.
+    * @js NA
+    */
+    virtual void onTouchesMoved(const std::vector<Touch*>& touches, Event *unused_event);
+    /** Callback function for multiple touches ended.
+    *
+    * @param touches Touches information.
+    * @param unused_event Event information.
+    * @js NA
+    */
+    virtual void onTouchesEnded(const std::vector<Touch*>& touches, Event *unused_event);
+    /** Callback function for multiple touches cancelled.
+    *
+    * @param touches Touches information.
+    * @param unused_event Event information.
+    * @js NA
+    */
+    virtual void onTouchesCancelled(const std::vector<Touch*>&touches, Event *unused_event);
+
+	/* Callback function should not be deprecated, it will generate lots of warnings.
+	Since 'setAccelerometerEnabled' was deprecated, it will make warnings if developer overrides onAcceleration and invokes setAccelerometerEnabled(true) instead of using EventDispatcher::addEventListenerWithXXX.
+    */
+    /** Callback function for acceleration.
+     * @param acc Acceleration information.
+     * @param unused_event Event information.
+     * @js NA
+     */
+    virtual void onAcceleration(Acceleration* acc, Event* unused_event);
+
+
+	/* Callback function should not be deprecated, it will generate lots of warnings.
+	Since 'setKeyboardEnabled' was deprecated, it will make warnings if developer overrides onKeyXXX and invokes setKeyboardEnabled(true) instead of using EventDispatcher::addEventListenerWithXXX.
+    */
+    /** Callback function for key pressed.
+     * @param keyCode KeyCode information.
+     * @param event Event information.
+     * @js NA
+     */
+    virtual void onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event);
+    /** Callback function for key released.
+    * @param keyCode KeyCode information.
+    * @param event Event information.
+    * @js NA
+    */
+    virtual void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event);
+
+    // Overrides
+    virtual std::string getDescription() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    Layer();
+    virtual ~Layer();
+
+    virtual bool init() override;
+
+protected:
+    
+    int executeScriptTouchHandler(EventTouch::EventCode eventType, Touch* touch, Event* event);
+    int executeScriptTouchesHandler(EventTouch::EventCode eventType, const std::vector<Touch*>& touches, Event* event);
+
+    bool _touchEnabled;
+    bool _accelerometerEnabled;
+    bool _keyboardEnabled;
+    EventListener* _touchListener;
+    EventListenerKeyboard* _keyboardListener;
+    EventListenerAcceleration* _accelerationListener;
+
+    Touch::DispatchMode _touchMode;
+    bool _swallowsTouches;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Layer);
+
+};
+
+//
+// LayerColor
+//
+/** @class LayerColor
+ * @brief LayerColor is a subclass of Layer that implements the RGBAProtocol protocol.
+
+All features from Layer are valid, plus the following new features:
+- opacity
+- RGB colors
+*/
+class CC_DLL LayerColor : public Layer, public BlendProtocol
+{
+public:
+
+    /** Creates a fullscreen black layer.
+     *
+     * @return An autoreleased LayerColor object.
+     */
+    static LayerColor* create();
+    /** Creates a Layer with color, width and height in Points.
+     *
+     * @param color The color of layer.
+     * @param width The width of layer.
+     * @param height The height of layer.
+     * @return An autoreleased LayerColor object.
+     */
+    static LayerColor * create(const Color4B& color, float width, float height);
+    /** Creates a Layer with color. Width and height are the window size.
+     *
+     * @param color The color of layer.
+     * @return An autoreleased LayerColor object.
+     */
+    static LayerColor * create(const Color4B& color);
+
+    /** Change width in Points.
+     * 
+     * @param w The width of layer.
+     */
+    void changeWidth(float w);
+    /** Change height in Points.
+     *
+     * @param h The height of layer.
+     */
+    void changeHeight(float h);
+    /** Change width and height in Points.
+     * 
+     * @param w The width of layer.
+     * @param h The Height of layer.
+    @since v0.8
+    */
+    void changeWidthAndHeight(float w, float h);
+
+    //
+    // Overrides
+    //
+    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
+
+    virtual void setContentSize(const Size & var) override;
+    /** BlendFunction. Conforms to BlendProtocol protocol */
+    /**
+    * @lua NA
+    */
+    virtual const BlendFunc& getBlendFunc() const override;
+    /**
+    *@code
+    *When this function bound into js or lua,the parameter will be changed
+    *In js: var setBlendFunc(var src, var dst)
+    *In lua: local setBlendFunc(local src, local dst)
+    *@endcode
+    */
+    virtual void setBlendFunc(const BlendFunc& blendFunc) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    LayerColor();
+    virtual ~LayerColor();
+    
+    bool init() override;
+    bool initWithColor(const Color4B& color, float width, float height);
+    bool initWithColor(const Color4B& color);
+
+protected:
+
+    virtual void updateColor() override;
+    void updateVertexBuffer();
+
+    BlendFunc _blendFunc;
+    Vec2 _squareVertices[4];
+    CustomCommand _customCommand;
+
+    V3F_C4F _vertexData[4];
+    
+    backend::UniformLocation _mvpMatrixLocation;
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(LayerColor);
+
+};
+
+//
+// LayerGradient
+//
+/** @class LayerGradient
+ * @brief LayerGradient is a subclass of LayerColor that draws gradients across the background.
+
+All features from LayerColor are valid, plus the following new features:
+- direction
+- final color
+- interpolation mode
+
+Color is interpolated between the startColor and endColor along the given
+vector (starting at the origin, ending at the terminus).  If no vector is
+supplied, it defaults to (0, -1) -- a fade from top to bottom.
+
+If 'compressedInterpolation' is disabled, you will not see either the start or end color for
+non-cardinal vectors; a smooth gradient implying both end points will be still
+be drawn, however.
+
+If ' compressedInterpolation' is enabled (default mode) you will see both the start and end colors of the gradient.
+
+@since v0.99.5
+*/
+class CC_DLL LayerGradient : public LayerColor
+{
+public:
+    /** Creates a fullscreen black layer.
+     *
+     * @return An autoreleased LayerGradient object.
+     */
+    static LayerGradient* create();
+
+    /** Creates a full-screen Layer with a gradient between start and end.
+     *
+     * @param start The start color.
+     * @param end The end color.
+     * @return An autoreleased LayerGradient object.
+     */
+    static LayerGradient* create(const Color4B& start, const Color4B& end);
+
+    /** Creates a full-screen Layer with a gradient between start and end in the direction of v.
+     *
+     * @param start The start color.
+     * @param end The end color.
+     * @param v The direction of gradient color.
+     * @return An autoreleased LayerGradient object.
+     */
+    static LayerGradient* create(const Color4B& start, const Color4B& end, const Vec2& v);
+    
+    /** Whether or not the interpolation will be compressed in order to display all the colors of the gradient both in canonical and non canonical vectors.
+     Default: true.
+     *
+     * @param compressedInterpolation The interpolation will be compressed if true.
+     */
+    void setCompressedInterpolation(bool compressedInterpolation);
+    /** Get the compressedInterpolation
+     *
+     * @return The interpolation will be compressed if true.
+     */
+    bool isCompressedInterpolation() const;
+
+    /** Sets the start color of the gradient.
+     * 
+     * @param startColor The start color.
+     */
+    void setStartColor( const Color3B& startColor );
+    /** Returns the start color of the gradient.
+     *
+     * @return The start color.
+     */
+    const Color3B& getStartColor() const;
+
+    /** Sets the end color of the gradient.
+     *
+     * @param endColor The end color.
+     */
+    void setEndColor( const Color3B& endColor );
+    /** Returns the end color of the gradient.
+     *
+     * @return The end color.
+     */
+    const Color3B& getEndColor() const;
+
+    /** Returns the start opacity of the gradient.
+     *
+     * @param startOpacity The start opacity, from 0 to 255.
+     */
+    void setStartOpacity(uint8_t startOpacity );
+    /** Returns the start opacity of the gradient.
+     *
+     * @return The start opacity.
+     */
+    uint8_t getStartOpacity() const;
+
+    /** Returns the end opacity of the gradient.
+     *
+     * @param endOpacity The end opacity, from 0 to 255.
+     */
+    void setEndOpacity(uint8_t endOpacity );
+    /** Returns the end opacity of the gradient.
+     *
+     * @return The end opacity.
+     */
+    uint8_t getEndOpacity() const;
+
+    /** Sets the directional vector that will be used for the gradient.
+    The default value is vertical direction (0,-1). 
+     *
+     * @param alongVector The direction of gradient.
+     */
+    void setVector(const Vec2& alongVector);
+    /** Returns the directional vector used for the gradient.
+     *
+     * @return The direction of gradient.
+     */
+    const Vec2& getVector() const;
+
+    virtual std::string getDescription() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    LayerGradient();
+    virtual ~LayerGradient();
+    
+    virtual bool init() override;
+    /** Initializes the Layer with a gradient between start and end.
+     * @js init
+     * @lua init
+     */
+    bool initWithColor(const Color4B& start, const Color4B& end);
+    
+    /** Initializes the Layer with a gradient between start and end in the direction of v.
+     * @js init
+     * @lua init
+     */
+    bool initWithColor(const Color4B& start, const Color4B& end, const Vec2& v);
+
+protected:
+    virtual void updateColor() override;
+
+    Color3B _startColor = Color3B::BLACK;
+    Color3B _endColor = Color3B::BLACK;
+    uint8_t _startOpacity = 255;
+    uint8_t _endOpacity = 255;
+    Vec2   _alongVector = {0.0f, -1.0f};
+    bool    _compressedInterpolation = true;
+};
+
+
+/** @class LayerRadialGradient
+ * @brief LayerRadialGradient is a subclass of Layer that draws radial gradients across the background.
+ @since v3.16
+ */
+class CC_DLL LayerRadialGradient : public Layer
+{
+public:
+    /** Create a LayerRadialGradient
+     * @param startColor the inner color of the gradient
+     * @param endColor the out color of the gradient
+     * @param radius the radius of the gradient(length from center of gradient to outer color)
+     * @param center the position of the center of the gradient
+     * @param expand an alpha value(0.f-1.f) that specifies how much of that radius in only inner color(the gradient
+                     starts outside of that amount)
+     */
+    static LayerRadialGradient* create(const Color4B& startColor, const Color4B& endColor, float radius, const Vec2& center, float expand);
+    static LayerRadialGradient* create();
+    
+    //
+    // overrides
+    //
+    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
+    virtual void setContentSize(const Size& size) override;
+    
+    void setStartOpacity(uint8_t opacity);
+    uint8_t getStartOpacity() const;
+    
+    void setEndOpacity(uint8_t opacity);
+    uint8_t getEndOpacity() const;
+    
+    void setRadius(float radius);
+    float getRadius() const;
+    
+    void setCenter(const Vec2& center);
+    Vec2 getCenter() const;
+    
+    void setExpand(float expand);
+    float getExpand() const;
+    
+    void setStartColor(const Color3B& color);
+    void setStartColor(const Color4B& color);
+    Color4B getStartColor() const;
+    Color3B getStartColor3B() const;
+    
+    void setEndColor(const Color3B& color);
+    void setEndColor(const Color4B& color);
+    Color4B getEndColor() const;
+    Color3B getEndColor3B() const;
+    
+    void setBlendFunc(const BlendFunc& blendFunc);
+    BlendFunc getBlendFunc() const;
+    
+CC_CONSTRUCTOR_ACCESS:
+    LayerRadialGradient();
+    virtual ~LayerRadialGradient();
+    
+    bool initWithColor(const Color4B& startColor, const Color4B& endColor, float radius, const Vec2& center, float expand);
+    
+private:
+    void convertColor4B24F(Color4F& outColor, const Color4B& inColor);
+    
+    Color4B _startColor = Color4B::BLACK;
+    Color4F _startColorRend = Color4F::BLACK; // start color used in shader
+    
+    Color4B _endColor = Color4B::BLACK;
+    Color4F _endColorRend = Color4F::BLACK; // end color used in shader
+
+    Vec2 _vertices[4];
+    Vec2 _center;
+    float _radius = 0.f;
+    float _expand = 0.f;
+    CustomCommand _customCommand;
+    
+    BlendFunc _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
+    
+    backend::UniformLocation _mvpMatrixLocation;
+    backend::UniformLocation _startColorLocation;
+    backend::UniformLocation _endColorLocation;
+    backend::UniformLocation _centerLocation;
+    backend::UniformLocation _radiusLocation;
+    backend::UniformLocation _expandLocation;
+};
+
+
+/** @class LayerMultiplex
+ * @brief MultipleLayer is a Layer with the ability to multiplex it's children.
+Features:
+- It supports one or more children
+- Only one children will be active a time
+*/
+class CC_DLL LayerMultiplex : public Layer
+{
+public:
+    /** Creates and initializes a LayerMultiplex object.
+     * @lua NA
+     * 
+     * @return An autoreleased LayerMultiplex object.
+     */
+    static LayerMultiplex* create();
+
+    /** Creates a LayerMultiplex with an array of layers.
+     @since v2.1
+     * @js NA
+     *
+     * @param arrayOfLayers An array of layers.
+     * @return An autoreleased LayerMultiplex object.
+     */
+    static LayerMultiplex* createWithArray(const Vector<Layer*>& arrayOfLayers);
+
+    /** Creates a LayerMultiplex with one or more layers using a variable argument list.
+     * @code
+     * When this function bound to lua or js,the input params are changed.
+     * In js:var create(...)
+     * In lua:local create(...)
+     * @endcode
+     */
+    static LayerMultiplex * create(Layer* layer, ... );
+
+    /** Creates a LayerMultiplex with one layer.
+     * Lua script can not init with undetermined number of variables
+     * so add these functions to be used with lua.
+     * @js NA
+     * @lua NA
+     *
+     * @param layer A certain layer.
+     * @return An autoreleased LayerMultiplex object.
+     */
+    static LayerMultiplex * createWithLayer(Layer* layer);
+
+
+    /** Add a certain layer to LayerMultiplex.
+     *
+     * @param layer A layer need to be added to the LayerMultiplex.
+     */
+    void addLayer(Layer* layer);
+
+    /** Switches to a certain layer indexed by n.
+     The current (old) layer will be removed from it's parent with 'cleanup=true'.
+     *
+     * @param n The layer indexed by n will display.
+     */
+    void switchTo(int n);
+    /** The same as switchTo(int), but has a parameter to set if need to clean up child.
+     */
+    void switchTo(int n, bool cleanup);
+    /** release the current layer and switches to another layer indexed by n.
+    The current (old) layer will be removed from it's parent with 'cleanup=true'.
+     *
+     * @param n The layer indexed by n will display.
+     */
+    void switchToAndReleaseMe(int n);
+
+    virtual std::string getDescription() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    LayerMultiplex();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~LayerMultiplex();
+    
+    virtual bool init() override;
+    /** initializes a MultiplexLayer with one or more layers using a variable argument list.
+     * @js NA
+     * @lua NA
+     */
+    bool initWithLayers(Layer* layer, va_list params);
+    
+    /** initializes a MultiplexLayer with an array of layers
+     @since v2.1
+     */
+    bool initWithArray(const Vector<Layer*>& arrayOfLayers);
+
+protected:
+    unsigned int _enabledLayer;
+    Vector<Layer*>    _layers;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(LayerMultiplex);
+};
+
+
+// end of _2d group
+/// @}
+
+NS_CC_END

+ 208 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLight.cpp

@@ -0,0 +1,208 @@
+/****************************************************************************
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+ http://www.cocos2d-x.org
+ 
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#include "2d/CCLight.h"
+#include "2d/CCScene.h"
+
+NS_CC_BEGIN
+
+void BaseLight::setIntensity(float intensity)
+{
+    CC_ASSERT(intensity >= 0);
+    _intensity = intensity;
+}
+
+void BaseLight::onEnter()
+{
+    auto scene = getScene();
+    if (scene)
+    {
+        auto &lights = scene->_lights;
+        auto iter = std::find(lights.begin(), lights.end(), this);
+        if (iter == lights.end())
+            lights.push_back(this);
+    }
+    Node::onEnter();
+}
+void BaseLight::onExit()
+{
+    auto scene = getScene();
+    if (scene)
+    {
+        auto &lights = scene->_lights;
+        auto iter = std::find(lights.begin(), lights.end(), this);
+        if (iter != lights.end())
+            lights.erase(iter);
+    }
+    Node::onExit();
+}
+
+void BaseLight::setRotationFromDirection( const Vec3 &direction )
+{
+    float projLen = sqrt(direction.x * direction.x + direction.z * direction.z);
+    float rotY = CC_RADIANS_TO_DEGREES(atan2f(-direction.x, -direction.z));
+    float rotX = -CC_RADIANS_TO_DEGREES(atan2f(-direction.y, projLen));
+    setRotation3D(Vec3(rotX, rotY, 0.0f));
+}
+
+BaseLight::BaseLight()
+: _intensity(1.0f)
+, _lightFlag(LightFlag::LIGHT0)
+, _enabled(true)
+{
+    
+}
+BaseLight::~BaseLight()
+{
+    
+}
+
+
+////////////////////////////////////////////////////////////////////
+DirectionLight* DirectionLight::create(const Vec3 &direction, const Color3B &color)
+{
+    auto light = new (std::nothrow) DirectionLight();
+    light->setRotationFromDirection(direction);
+    light->setColor(color);
+    light->autorelease();
+    return light;
+}
+
+void DirectionLight::setDirection(const Vec3 &dir)
+{
+    setRotationFromDirection(dir);
+}
+Vec3 DirectionLight::getDirection() const
+{
+    Mat4 mat = getNodeToParentTransform();
+    return Vec3(-mat.m[8], -mat.m[9], -mat.m[10]);
+}
+Vec3 DirectionLight::getDirectionInWorld() const
+{
+    Mat4 mat = getNodeToWorldTransform();
+    return Vec3(-mat.m[8], -mat.m[9], -mat.m[10]);
+}
+DirectionLight::DirectionLight()
+{
+    
+}
+DirectionLight::~DirectionLight()
+{
+    
+}
+
+//////////////////////////////////////////////////////////////////
+PointLight* PointLight::create(const Vec3 &position, const Color3B &color, float range)
+{
+    auto light = new (std::nothrow) PointLight();
+    light->setPosition3D(position);
+    light->setColor(color);
+    light->_range = range;
+    light->autorelease();
+    return light;
+}
+
+PointLight::PointLight()
+{
+    
+}
+PointLight::~PointLight()
+{
+    
+}
+
+//////////////////////////////////////////////////////////////
+SpotLight* SpotLight::create(const Vec3 &direction, const Vec3 &position, const Color3B &color, float innerAngle, float outerAngle, float range)
+{
+    auto light = new (std::nothrow) SpotLight();
+    light->setRotationFromDirection(direction);
+    light->setPosition3D(position);
+    light->setColor(color);
+    light->setInnerAngle(innerAngle);
+    light->setOuterAngle(outerAngle);
+    light->_range = range;
+    light->autorelease();
+    return light;
+}
+
+void SpotLight::setDirection(const Vec3 &dir)
+{
+    setRotationFromDirection(dir);
+}
+
+Vec3 SpotLight::getDirection() const
+{
+    Mat4 mat = getNodeToParentTransform();
+    return Vec3(-mat.m[8], -mat.m[9], -mat.m[10]);
+}
+
+Vec3 SpotLight::getDirectionInWorld() const
+{
+    Mat4 mat = getNodeToWorldTransform();
+    return Vec3(-mat.m[8], -mat.m[9], -mat.m[10]);
+}
+
+void SpotLight::setInnerAngle(float angle)
+{
+    _innerAngle = angle;
+    _cosInnerAngle = cosf(angle);
+}
+
+void SpotLight::setOuterAngle(float angle)
+{
+    _outerAngle = angle;
+    _cosOuterAngle = cosf(angle);
+}
+
+SpotLight::SpotLight()
+{
+
+}
+
+SpotLight::~SpotLight()
+{
+
+}
+
+/////////////////////////////////////////////////////////////
+
+AmbientLight* AmbientLight::create( const Color3B &color )
+{
+    auto light = new (std::nothrow) AmbientLight();
+    light->setColor(color);
+    light->autorelease();
+    return light;
+}
+
+AmbientLight::AmbientLight()
+{
+
+}
+
+AmbientLight::~AmbientLight()
+{
+
+}
+
+NS_CC_END

+ 295 - 0
3rdparty/cocos2dx/include/cocos/2d/CCLight.h

@@ -0,0 +1,295 @@
+/****************************************************************************
+ Copyright (c) 2014-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#ifndef __CCLIGHT_H__
+#define __CCLIGHT_H__
+
+#include "2d/CCNode.h"
+
+NS_CC_BEGIN
+
+enum class LightType
+{
+    DIRECTIONAL = 0,
+    POINT = 1,
+    SPOT = 2,
+    AMBIENT = 3,
+};
+
+enum class LightFlag
+{
+    LIGHT0 = 1,
+    LIGHT1 = 1 << 1,
+    LIGHT2 = 1 << 2,
+    LIGHT3 = 1 << 3,
+    LIGHT4 = 1 << 4,
+    LIGHT5 = 1 << 5,
+    LIGHT6 = 1 << 6,
+    LIGHT7 = 1 << 7,
+    LIGHT8 = 1 << 8,
+    LIGHT9 = 1 << 9,
+    LIGHT10 = 1 << 10,
+    LIGHT11 = 1 << 11,
+    LIGHT12 = 1 << 12,
+    LIGHT13 = 1 << 13,
+    LIGHT14 = 1 << 14,
+    LIGHT15 = 1 << 15,
+};
+
+/**
+@js NA
+*/
+class CC_DLL BaseLight : public Node
+{
+public:
+    
+    /**
+     * Get the light type,light type MUST be one of LightType::DIRECTIONAL ,
+     * LightType::POINT, LightType::SPOT, LightType::AMBIENT.
+     */
+    virtual LightType getLightType() const = 0;
+    
+    /** intensity getter and setter */
+    float getIntensity() const { return _intensity; }
+    void setIntensity(float intensity);
+    
+    /**light flag getter and setter*/
+    LightFlag getLightFlag() const { return _lightFlag; }
+    void setLightFlag(LightFlag flag) { _lightFlag = flag; }
+    
+    /**
+     * light enabled getter and setter.
+     */
+    void setEnabled(bool enabled) { _enabled = enabled; }
+    bool isEnabled() const { return _enabled; }
+    
+    //override
+    virtual void onEnter() override;
+    virtual void onExit() override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    BaseLight();
+    virtual ~BaseLight();
+    
+protected:
+    void setRotationFromDirection( const Vec3 &direction );
+    
+protected:
+    float       _intensity;
+    LightFlag   _lightFlag;
+    bool        _enabled;
+};
+
+/**
+@js NA
+*/
+class CC_DLL DirectionLight : public BaseLight
+{
+public:
+    /**
+     * Creates a direction light.
+     * @param direction The light's direction
+     * @param color The light's color.
+     *
+     * @return The new direction light.
+     */
+    static DirectionLight* create(const Vec3 &direction, const Color3B &color);
+    
+    //get light type
+    virtual LightType getLightType() const override { return LightType::DIRECTIONAL; }
+    
+    /**
+     * Sets the Direction in parent.
+     *
+     * @param dir The Direction in parent.
+     */
+    void setDirection(const Vec3 &dir);
+    
+    /**
+     * Returns the Direction in parent.
+     */
+    Vec3 getDirection() const;
+    
+    /**
+     * Returns direction in world.
+     */
+    Vec3 getDirectionInWorld() const;
+    
+CC_CONSTRUCTOR_ACCESS:
+    DirectionLight();
+    virtual ~DirectionLight();
+    
+};
+
+/**
+@js NA
+*/
+class CC_DLL PointLight : public BaseLight
+{
+public:
+    /**
+     * Creates a point light.
+     * @param position The light's position
+     * @param color The light's color.
+     * @param range The light's range.
+     *
+     * @return The new point light.
+     */
+    static PointLight* create(const Vec3 &position, const Color3B &color, float range);
+    
+    //get light type
+    virtual LightType getLightType() const override { return LightType::POINT; }
+    
+    /** get or set range */
+    float getRange() const { return _range; }
+    void setRange(float range) { _range = range; }
+    
+CC_CONSTRUCTOR_ACCESS:
+    PointLight();
+    virtual ~PointLight();
+    
+protected:
+    float _range;
+};
+
+/**
+@js NA
+*/
+class CC_DLL SpotLight : public BaseLight
+{
+public:
+    /**
+     * Creates a spot light.
+     * @param direction The light's direction
+     * @param position The light's position
+     * @param color The light's color.
+     * @param innerAngle The light's inner angle (in radians).
+     * @param outerAngle The light's outer angle (in radians).
+     * @param range The light's range.
+     *
+     * @return The new spot light.
+     */
+    static SpotLight* create(const Vec3 &direction, const Vec3 &position, const Color3B &color, float innerAngle, float outerAngle, float range);
+    
+    //get light type
+    virtual LightType getLightType() const override { return LightType::SPOT; }
+    
+    /**
+     * Sets the Direction in parent.
+     *
+     * @param dir The Direction in parent.
+     */
+    void setDirection(const Vec3 &dir);
+    
+    /**
+     * Returns the Direction in parent.
+     */
+    Vec3 getDirection() const;
+    
+    /**
+     * Returns direction in world.
+     */
+    Vec3 getDirectionInWorld() const;
+    
+    /**
+     * Sets the range of point or spot light.
+     *
+     * @param range The range of point or spot light.
+     */
+    void setRange(float range) { _range = range; }
+    
+    /**
+     * Returns the range of point or spot light.
+     *
+     * @return The range of the point or spot light.
+     */
+    float getRange() const { return _range; }
+    /**
+     * Sets the inner angle of a spot light (in radians).
+     *
+     * @param angle The angle of spot light (in radians).
+     */
+    void setInnerAngle(float angle);
+    
+    /**
+     * Returns the inner angle the spot light (in radians).
+     */
+    float getInnerAngle() const { return _innerAngle; }
+    
+    /** get cos innerAngle */
+    float getCosInnerAngle() const { return _cosInnerAngle; }
+    
+    /**
+     * Sets the outer angle of a spot light (in radians).
+     *
+     * @param outerAngle The angle of spot light (in radians).
+     */
+    void setOuterAngle(float outerAngle);
+    
+    /**
+     * Returns the outer angle of the spot light (in radians).
+     */
+    float getOuterAngle() const { return _outerAngle; }
+    
+    /** get cos outAngle */
+    float getCosOuterAngle() const { return _cosOuterAngle; }
+    
+CC_CONSTRUCTOR_ACCESS:
+    SpotLight();
+    virtual ~SpotLight();
+    
+protected:
+    float _range;
+    float _innerAngle;
+    float _cosInnerAngle;
+    float _outerAngle;
+    float _cosOuterAngle;
+};
+
+/**
+@js NA
+*/
+class CC_DLL AmbientLight : public BaseLight
+{
+public:
+    /**
+     * Creates a ambient light.
+     * @param color The light's color.
+     *
+     * @return The new ambient light.
+     */
+    static AmbientLight* create(const Color3B &color);
+    
+    //get light type
+    virtual LightType getLightType() const override { return LightType::AMBIENT; }
+    
+CC_CONSTRUCTOR_ACCESS:
+    AmbientLight();
+    virtual ~AmbientLight();
+};
+
+NS_CC_END
+
+#endif

+ 575 - 0
3rdparty/cocos2dx/include/cocos/2d/CCMenu.cpp

@@ -0,0 +1,575 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCMenu.h"
+#include "2d/CCCamera.h"
+#include "base/CCDirector.h"
+#include "base/CCTouch.h"
+#include "base/CCEventListenerTouch.h"
+#include "base/CCEventDispatcher.h"
+#include "base/ccUTF8.h"
+#include "platform/CCStdC.h"
+
+#include <vector>
+
+using namespace std;
+
+NS_CC_BEGIN
+
+enum 
+{
+    kDefaultPadding =  5,
+};
+
+//
+//CCMenu
+//
+
+Menu::~Menu()
+{
+    CCLOGINFO("In the destructor of Menu. %p", this);
+}
+
+
+Menu* Menu::create()
+{
+    return Menu::create(nullptr, nullptr);
+}
+
+Menu * Menu::create(MenuItem* item, ...)
+{
+    va_list args;
+    va_start(args,item);
+    
+    Menu *ret = Menu::createWithItems(item, args);
+    
+    va_end(args);
+    
+    return ret;
+}
+
+Menu* Menu::createWithArray(const Vector<MenuItem*>& arrayOfItems)
+{
+    auto ret = new (std::nothrow) Menu();
+    if (ret && ret->initWithArray(arrayOfItems))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    
+    return ret;
+}
+
+Menu* Menu::createWithItems(MenuItem* item, va_list args)
+{
+    Vector<MenuItem*> items;
+    if( item )
+    {
+        items.pushBack(item);
+        MenuItem *i = va_arg(args, MenuItem*);
+        while(i)
+        {
+            items.pushBack(i);
+            i = va_arg(args, MenuItem*);
+        }
+    }
+    
+    return Menu::createWithArray(items);
+}
+
+Menu* Menu::createWithItem(MenuItem* item)
+{
+    return Menu::create(item, nullptr);
+}
+
+bool Menu::init()
+{
+    return initWithArray(Vector<MenuItem*>());
+}
+
+bool Menu::initWithArray(const Vector<MenuItem*>& arrayOfItems)
+{
+    if (Layer::init())
+    {
+        _enabled = true;
+        // menu in the center of the screen
+        Size s = Director::getInstance()->getWinSize();
+
+        this->setIgnoreAnchorPointForPosition(true);
+        setAnchorPoint(Vec2(0.5f, 0.5f));
+        this->setContentSize(s);
+
+        setPosition(s.width/2, s.height/2);
+        
+        int z=0;
+        
+        for (auto& item : arrayOfItems)
+        {
+            this->addChild(item, z);
+            z++;
+        }
+    
+        _selectedItem = nullptr;
+        _state = Menu::State::WAITING;
+        
+        // enable cascade color and opacity on menus
+        setCascadeColorEnabled(true);
+        setCascadeOpacityEnabled(true);
+        
+        
+        auto touchListener = EventListenerTouchOneByOne::create();
+        touchListener->setSwallowTouches(true);
+        
+        touchListener->onTouchBegan = CC_CALLBACK_2(Menu::onTouchBegan, this);
+        touchListener->onTouchMoved = CC_CALLBACK_2(Menu::onTouchMoved, this);
+        touchListener->onTouchEnded = CC_CALLBACK_2(Menu::onTouchEnded, this);
+        touchListener->onTouchCancelled = CC_CALLBACK_2(Menu::onTouchCancelled, this);
+        
+        _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
+        
+        return true;
+    }
+    return false;
+}
+
+/*
+* override add:
+*/
+void Menu::addChild(Node * child)
+{
+    Layer::addChild(child);
+}
+
+void Menu::addChild(Node * child, int zOrder)
+{
+    Layer::addChild(child, zOrder);
+}
+
+void Menu::addChild(Node * child, int zOrder, int tag)
+{
+    CCASSERT( dynamic_cast<MenuItem*>(child) != nullptr, "Menu only supports MenuItem objects as children");
+    Layer::addChild(child, zOrder, tag);
+}
+
+void Menu::addChild(Node * child, int zOrder, const std::string &name)
+{
+    CCASSERT( dynamic_cast<MenuItem*>(child) != nullptr, "Menu only supports MenuItem objects as children");
+    Layer::addChild(child, zOrder, name);
+}
+
+void Menu::onEnter()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnter))
+            return;
+    }
+#endif
+    
+    Layer::onEnter();
+}
+
+void Menu::onExit()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit))
+            return;
+    }
+#endif
+    
+    if (_state == Menu::State::TRACKING_TOUCH)
+    {
+        if (_selectedItem)
+        {
+            _selectedItem->unselected();
+            _selectedItem = nullptr;
+        }
+        
+        _state = Menu::State::WAITING;
+    }
+
+    Layer::onExit();
+}
+
+void Menu::removeChild(Node* child, bool cleanup)
+{
+    CCASSERT(dynamic_cast<MenuItem*>(child) != nullptr, "Menu only supports MenuItem objects as children");
+    
+    if (_selectedItem == child)
+    {
+        _selectedItem = nullptr;
+    }
+    
+    Node::removeChild(child, cleanup);
+}
+
+//Menu - Events
+
+bool Menu::onTouchBegan(Touch* touch, Event* /*event*/)
+{
+    auto camera = Camera::getVisitingCamera();
+    if (_state != Menu::State::WAITING || ! _visible || !_enabled || !camera)
+    {
+        return false;
+    }
+    
+    for (Node *c = this->_parent; c != nullptr; c = c->getParent())
+    {
+        if (c->isVisible() == false)
+        {
+            return false;
+        }
+    }
+    _selectedItem = this->getItemForTouch(touch, camera);
+
+    if (_selectedItem)
+    {
+        _state = Menu::State::TRACKING_TOUCH;
+        _selectedWithCamera = camera;
+        _selectedItem->selected();
+        
+        return true;
+    }
+    
+    return false;
+}
+
+void Menu::onTouchEnded(Touch* /*touch*/, Event* /*event*/)
+{
+    CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchEnded] -- invalid state");
+    this->retain();
+    if (_selectedItem)
+    {
+        _selectedItem->unselected();
+        _selectedItem->activate();
+    }
+    _state = Menu::State::WAITING;
+    _selectedWithCamera = nullptr;
+    this->release();
+}
+
+void Menu::onTouchCancelled(Touch* /*touch*/, Event* /*event*/)
+{
+    CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchCancelled] -- invalid state");
+    this->retain();
+    if (_selectedItem)
+    {
+        _selectedItem->unselected();
+    }
+    _state = Menu::State::WAITING;
+    this->release();
+}
+
+void Menu::onTouchMoved(Touch* touch, Event* /*event*/)
+{
+    CCASSERT(_state == Menu::State::TRACKING_TOUCH, "[Menu ccTouchMoved] -- invalid state");
+    MenuItem *currentItem = this->getItemForTouch(touch, _selectedWithCamera);
+    if (currentItem != _selectedItem)
+    {
+        if (_selectedItem)
+        {
+            _selectedItem->unselected();
+        }
+        _selectedItem = currentItem;
+        if (_selectedItem)
+        {
+            _selectedItem->selected();
+        }
+    }
+}
+
+//Menu - Alignment
+void Menu::alignItemsVertically()
+{
+    this->alignItemsVerticallyWithPadding(kDefaultPadding);
+}
+
+void Menu::alignItemsVerticallyWithPadding(float padding)
+{
+    float height = -padding;
+
+    for(const auto &child : _children)
+        height += child->getContentSize().height * child->getScaleY() + padding;
+
+    float y = height / 2.0f;
+    
+    for(const auto &child : _children) {
+        child->setPosition(0, y - child->getContentSize().height * child->getScaleY() / 2.0f);
+        y -= child->getContentSize().height * child->getScaleY() + padding;
+    }
+}
+
+void Menu::alignItemsHorizontally()
+{
+    this->alignItemsHorizontallyWithPadding(kDefaultPadding);
+}
+
+void Menu::alignItemsHorizontallyWithPadding(float padding)
+{
+    float width = -padding;
+    for(const auto &child : _children)
+        width += child->getContentSize().width * child->getScaleX() + padding;
+
+    float x = -width / 2.0f;
+    
+    for(const auto &child : _children) {
+        child->setPosition(x + child->getContentSize().width * child->getScaleX() / 2.0f, 0);
+        x += child->getContentSize().width * child->getScaleX() + padding;
+    }
+}
+
+void Menu::alignItemsInColumns(int columns, ...)
+{
+    va_list args;
+    va_start(args, columns);
+
+    this->alignItemsInColumns(columns, args);
+
+    va_end(args);
+}
+
+void Menu::alignItemsInColumns(int columns, va_list args)
+{
+    CCASSERT(columns >= 0, "Columns must be >= 0");
+    ValueVector rows;
+    while (columns)
+    {
+        rows.push_back(Value(columns));
+        columns = va_arg(args, int);
+    }
+    alignItemsInColumnsWithArray(rows);
+}
+
+void Menu::alignItemsInColumnsWithArray(const ValueVector& rows)
+{
+    int height = -5;
+    size_t row = 0;
+    int rowHeight = 0;
+    int columnsOccupied = 0;
+    int rowColumns = 0;
+
+    for(const auto &child : _children) {
+        CCASSERT(row < rows.size(), "row should less than rows.size()!");
+        
+        rowColumns = rows[row].asInt();
+        // can not have zero columns on a row
+        CCASSERT(rowColumns, "rowColumns can't be 0.");
+        
+        float tmp = child->getContentSize().height;
+        rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp);
+        
+        ++columnsOccupied;
+        if (columnsOccupied >= rowColumns)
+        {
+            height += rowHeight + 5;
+            
+            columnsOccupied = 0;
+            rowHeight = 0;
+            ++row;
+        }
+    }
+
+    // check if too many rows/columns for available menu items
+    CCASSERT(! columnsOccupied, "columnsOccupied should be 0.");
+
+    Size winSize = getContentSize();
+
+    row = 0;
+    rowHeight = 0;
+    rowColumns = 0;
+    float w = 0.0;
+    float x = 0.0;
+    float y = (float)(height / 2);
+
+    for(const auto &child : _children) {
+        if (rowColumns == 0)
+        {
+            rowColumns = rows[row].asInt();
+            w = winSize.width / (1 + rowColumns);
+            x = w;
+        }
+
+        float tmp = child->getContentSize().height;
+        rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp);
+
+        child->setPosition(x - winSize.width / 2,
+                               y - child->getContentSize().height / 2);
+
+        x += w;
+        ++columnsOccupied;
+
+        if (columnsOccupied >= rowColumns)
+        {
+            y -= rowHeight + 5;
+
+            columnsOccupied = 0;
+            rowColumns = 0;
+            rowHeight = 0;
+            ++row;
+        }
+    }
+}
+
+void Menu::alignItemsInRows(int rows, ...)
+{
+    va_list args;
+    va_start(args, rows);
+
+    this->alignItemsInRows(rows, args);
+
+    va_end(args);
+}
+
+void Menu::alignItemsInRows(int rows, va_list args)
+{
+    ValueVector array;
+    while (rows)
+    {
+        array.push_back(Value(rows));
+        rows = va_arg(args, int);
+    }
+    alignItemsInRowsWithArray(array);
+}
+
+void Menu::alignItemsInRowsWithArray(const ValueVector& columns)
+{
+    vector<int> columnWidths;
+    vector<int> columnHeights;
+
+    int width = -10;
+    int columnHeight = -5;
+    size_t column = 0;
+    int columnWidth = 0;
+    int rowsOccupied = 0;
+    int columnRows;
+
+    for(const auto &child : _children) {
+        // check if too many menu items for the amount of rows/columns
+        CCASSERT(column < columns.size(), "column should be less than columns.size().");
+
+        columnRows = columns[column].asInt();
+        // can't have zero rows on a column
+        CCASSERT(columnRows, "columnRows can't be 0.");
+
+        // columnWidth = fmaxf(columnWidth, [item contentSize].width);
+        float tmp = child->getContentSize().width;
+        columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);
+
+        columnHeight += (int)(child->getContentSize().height + 5);
+        ++rowsOccupied;
+
+        if (rowsOccupied >= columnRows)
+        {
+            columnWidths.push_back(columnWidth);
+            columnHeights.push_back(columnHeight);
+            width += columnWidth + 10;
+
+            rowsOccupied = 0;
+            columnWidth = 0;
+            columnHeight = -5;
+            ++column;
+        }
+    }
+
+    // check if too many rows/columns for available menu items.
+    CCASSERT(! rowsOccupied, "rowsOccupied should be 0.");
+
+    Size winSize = getContentSize();
+
+    column = 0;
+    columnWidth = 0;
+    columnRows = 0;
+    float x = (float)(-width / 2);
+    float y = 0.0;
+
+    for(const auto &child : _children) {
+        if (columnRows == 0)
+        {
+            columnRows = columns[column].asInt();
+            y = (float) columnHeights[column];
+        }
+
+        // columnWidth = fmaxf(columnWidth, [item contentSize].width);
+        float tmp = child->getContentSize().width;
+        columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);
+
+        child->setPosition(x + columnWidths[column] / 2,
+                               y - winSize.height / 2);
+
+        y -= child->getContentSize().height + 10;
+        ++rowsOccupied;
+
+        if (rowsOccupied >= columnRows)
+        {
+            x += columnWidth + 5;
+            rowsOccupied = 0;
+            columnRows = 0;
+            columnWidth = 0;
+            ++column;
+        }
+    }
+}
+
+MenuItem* Menu::getItemForTouch(Touch *touch, const Camera *camera)
+{
+    Vec2 touchLocation = touch->getLocation();
+    for (const auto &item: _children)
+    {
+        MenuItem* child = dynamic_cast<MenuItem*>(item);
+        if (nullptr == child || false == child->isVisible() || false == child->isEnabled())
+        {
+            continue;
+        }
+        Rect rect;
+        rect.size = child->getContentSize();
+        if (isScreenPointInRect(touchLocation, camera, child->getWorldToNodeTransform(), rect, nullptr))
+        {
+            return child;
+        }
+    }
+    return nullptr;
+}
+
+void Menu::setOpacityModifyRGB(bool /*value*/)
+{}
+
+bool Menu::isOpacityModifyRGB() const
+{
+    return false;
+}
+
+std::string Menu::getDescription() const
+{
+    return StringUtils::format("<Menu | Tag = %d>", _tag);
+}
+
+NS_CC_END

+ 195 - 0
3rdparty/cocos2dx/include/cocos/2d/CCMenu.h

@@ -0,0 +1,195 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __CCMENU_H_
+#define __CCMENU_H_
+
+#include "2d/CCMenuItem.h"
+#include "2d/CCLayer.h"
+#include "base/CCValue.h"
+
+NS_CC_BEGIN
+class Touch;
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+
+
+/** @brief A Menu for touch handling.
+* 
+* Features and Limitation:
+*  - You can add MenuItem objects in runtime using addChild.
+*  - But the only accepted children are MenuItem objects.
+*/
+class CC_DLL Menu : public Layer
+{
+public:
+    /**
+     * Menu state, it's used internally.
+     */
+    enum class State
+    {
+        WAITING,
+        TRACKING_TOUCH,
+    };
+    
+    /**
+     *@brief Creates an empty Menu.
+     */
+    static Menu* create();
+    
+    /** Creates a Menu with MenuItem objects. */
+    static Menu* create(MenuItem* item, ...) CC_REQUIRES_NULL_TERMINATION;
+
+    /** 
+     * Creates a Menu with a Array of MenuItem objects.
+     * @js NA
+     */
+    static Menu* createWithArray(const Vector<MenuItem*>& arrayOfItems);
+
+    /**
+     * Creates a Menu with it's item, then use addChild() to add 
+     * other items. It is used for script, it can't be initialized with undetermined
+     * number of variables.
+     * @js NA
+     */
+    static Menu* createWithItem(MenuItem* item);
+    
+    /** Creates a Menu with MenuItem objects.
+     * @js NA
+     */
+    static Menu* createWithItems(MenuItem *firstItem, va_list args);
+
+    /** Align items vertically. */
+    void alignItemsVertically();
+
+    /** Align items vertically with padding.
+    @since v0.7.2
+    */
+    void alignItemsVerticallyWithPadding(float padding);
+
+    /** Align items horizontally. */
+    void alignItemsHorizontally();
+    
+    /** Align items horizontally with padding.
+    @since v0.7.2
+    */
+    void alignItemsHorizontallyWithPadding(float padding);
+
+    /** Align items in rows of columns. */
+    void alignItemsInColumns(int columns, ...) CC_REQUIRES_NULL_TERMINATION;
+    
+    /** Align items in rows of columns. */
+    void alignItemsInColumns(int columns, va_list args);
+    
+    /** Align items in array of columns.
+     * @js NA
+     */
+    void alignItemsInColumnsWithArray(const ValueVector& rows);
+
+    /** Align items in columns of rows. */
+    void alignItemsInRows(int rows, ...) CC_REQUIRES_NULL_TERMINATION;
+    
+    /** Align items in columns of rows. */
+    void alignItemsInRows(int rows, va_list args);
+    
+    /** Align items in array of rows.
+     * @js NA
+     */
+    void alignItemsInRowsWithArray(const ValueVector& columns);
+
+    /**
+     * Determines if the menu is enabled.
+     * @see `setEnabled(bool)`.
+     * @return whether the menu is enabled or not.
+     */
+    virtual bool isEnabled() const { return _enabled; }
+
+    /**
+     * Set whether the menu is enabled. If set to false, interacting with the menu
+     * will have no effect.
+     * The default value is true, a menu is enabled by default.
+     * @param value true if menu is to be enabled, false if menu is to be disabled.
+     */
+    virtual void setEnabled(bool value) { _enabled = value; };
+
+    virtual bool onTouchBegan(Touch* touch, Event* event) override;
+    virtual void onTouchEnded(Touch* touch, Event* event) override;
+    virtual void onTouchCancelled(Touch* touch, Event* event) override;
+    virtual void onTouchMoved(Touch* touch, Event* event) override;
+    
+    // overrides
+    virtual void removeChild(Node* child, bool cleanup) override;
+    
+    virtual void addChild(Node * child) override;
+    virtual void addChild(Node * child, int zOrder) override;
+    virtual void addChild(Node * child, int zOrder, int tag) override;
+    virtual void addChild(Node * child, int zOrder, const std::string &name) override;
+    
+    virtual void onEnter() override;
+    virtual void onExit() override;
+    virtual void setOpacityModifyRGB(bool value) override;
+    virtual bool isOpacityModifyRGB() const override;
+
+    virtual std::string getDescription() const override;
+
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    Menu() : _selectedItem(nullptr), _selectedWithCamera(nullptr) {}
+    virtual ~Menu();
+
+    /** initializes an empty Menu */
+    bool init() override;
+
+    /** initializes a Menu with a NSArray of MenuItem objects */
+    bool initWithArray(const Vector<MenuItem*>& arrayOfItems);
+
+protected:
+
+
+
+    /** whether or not the menu will receive events */
+    bool _enabled;
+
+    virtual MenuItem* getItemForTouch(Touch * touch, const Camera *camera);
+    State _state;
+    MenuItem *_selectedItem;
+    const Camera *_selectedWithCamera;
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Menu);
+};
+
+// end of _2d group
+/// @}
+
+NS_CC_END
+
+#endif//__CCMENU_H_

+ 841 - 0
3rdparty/cocos2dx/include/cocos/2d/CCMenuItem.cpp

@@ -0,0 +1,841 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCMenuItem.h"
+#include "2d/CCActionInterval.h"
+#include "2d/CCSprite.h"
+#include "2d/CCLabelAtlas.h"
+#include "2d/CCLabel.h"
+#include "base/ccUTF8.h"
+#include <stdarg.h>
+
+NS_CC_BEGIN
+    
+static int _globalFontSize = kItemSize;
+static std::string _globalFontName = "Marker Felt";
+static bool _globalFontNameRelease = false;
+
+const unsigned int    kZoomActionTag = 0xc0c05002;
+
+//
+// MenuItem
+//
+
+MenuItem* MenuItem::create()
+{
+    return MenuItem::create((const ccMenuCallback&)nullptr);
+}
+
+MenuItem* MenuItem::create( const ccMenuCallback& callback)
+{
+    MenuItem *ret = new (std::nothrow) MenuItem();
+    ret->initWithCallback(callback);
+    ret->autorelease();
+    return ret;
+}
+
+bool MenuItem::initWithCallback(const ccMenuCallback& callback)
+{
+    setAnchorPoint(Vec2(0.5f, 0.5f));
+    _callback = callback;
+    _enabled = true;
+    _selected = false;
+    return true;
+}
+
+MenuItem::~MenuItem()
+{
+}
+
+void MenuItem::selected()
+{
+    _selected = true;
+}
+
+void MenuItem::unselected()
+{
+    _selected = false;
+}
+
+void MenuItem::activate()
+{
+    if (_enabled)
+    {
+        if( _callback )
+        {
+            _callback(this);
+        }
+#if CC_ENABLE_SCRIPT_BINDING
+        if (kScriptTypeLua == _scriptType)
+        {
+            BasicScriptData data(this);
+            ScriptEvent scriptEvent(kMenuClickedEvent, &data);
+            ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);
+        }
+#endif
+    }
+}
+
+void MenuItem::setEnabled(bool enabled)
+{
+    _enabled = enabled;
+}
+
+bool MenuItem::isEnabled() const
+{
+    return _enabled;
+}
+
+Rect MenuItem::rect() const
+{
+    return Rect( _position.x - _contentSize.width * _anchorPoint.x,
+                      _position.y - _contentSize.height * _anchorPoint.y,
+                      _contentSize.width, _contentSize.height);
+}
+
+bool MenuItem::isSelected() const
+{
+    return _selected;
+}
+
+void MenuItem::setCallback(const ccMenuCallback& callback)
+{
+    _callback = callback;
+}
+
+std::string MenuItem::getDescription() const
+{
+    return StringUtils::format("<MenuItem | tag = %d>", _tag);
+}
+
+//
+//CCMenuItemLabel
+//
+
+void MenuItemLabel::setLabel(Node* var)
+{
+    if (var)
+    {
+        var->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT);
+        setContentSize(var->getContentSize()); 
+        addChild(var);
+    }
+    
+    if (_label)
+    {
+        removeChild(_label, true);
+    }
+    
+    _label = var;
+}
+
+MenuItemLabel * MenuItemLabel::create(Node*label, const ccMenuCallback& callback)
+{
+    MenuItemLabel *ret = new (std::nothrow) MenuItemLabel();
+    ret->initWithLabel(label, callback);
+    ret->autorelease();
+    return ret;
+}
+
+MenuItemLabel* MenuItemLabel::create(Node *label)
+{
+    MenuItemLabel *ret = new (std::nothrow) MenuItemLabel();
+    ret->initWithLabel(label, (const ccMenuCallback&) nullptr);
+    ret->autorelease();
+    return ret;
+}
+
+bool MenuItemLabel::initWithLabel(Node* label, const ccMenuCallback& callback)
+{
+    MenuItem::initWithCallback(callback);
+    _originalScale = 1.0f;
+    _colorBackup = Color3B::WHITE;
+    setDisabledColor(Color3B(126,126,126));
+    this->setLabel(label);
+
+    setCascadeColorEnabled(true);
+    setCascadeOpacityEnabled(true);
+
+    return true;
+}
+
+
+MenuItemLabel::~MenuItemLabel()
+{
+}
+
+void MenuItemLabel::setString(const std::string& label)
+{
+    dynamic_cast<LabelProtocol*>(_label)->setString(label);
+    this->setContentSize(_label->getContentSize());
+}
+
+std::string MenuItemLabel::getString() const
+{
+    auto label = dynamic_cast<LabelProtocol*>(_label);
+    return label->getString();
+}
+
+void MenuItemLabel::activate()
+{
+    if(_enabled)
+    {
+        this->stopAllActions();
+        this->setScale( _originalScale );
+        MenuItem::activate();
+    }
+}
+
+void MenuItemLabel::selected()
+{
+    // subclass to change the default action
+    if(_enabled)
+    {
+        MenuItem::selected();
+        
+        Action *action = getActionByTag(kZoomActionTag);
+        if (action)
+        {
+            this->stopAction(action);
+        }
+        else
+        {
+            _originalScale = this->getScale();
+        }
+        
+        Action *zoomAction = ScaleTo::create(0.1f, _originalScale * 1.2f);
+        zoomAction->setTag(kZoomActionTag);
+        this->runAction(zoomAction);
+    }
+}
+
+void MenuItemLabel::unselected()
+{
+    // subclass to change the default action
+    if(_enabled)
+    {
+        MenuItem::unselected();
+        this->stopActionByTag(kZoomActionTag);
+        Action *zoomAction = ScaleTo::create(0.1f, _originalScale);
+        zoomAction->setTag(kZoomActionTag);
+        this->runAction(zoomAction);
+    }
+}
+
+void MenuItemLabel::setEnabled(bool enabled)
+{
+    if( _enabled != enabled ) 
+    {
+        if(enabled == false)
+        {
+            _colorBackup = this->getColor();
+            this->setColor(_disabledColor);
+        }
+        else
+        {
+            this->setColor(_colorBackup);
+        }
+    }
+    MenuItem::setEnabled(enabled);
+}
+
+//
+//CCMenuItemAtlasFont
+//
+
+MenuItemAtlasFont * MenuItemAtlasFont::create(const std::string& value, const std::string& charMapFile, int itemWidth, int itemHeight, char startCharMap)
+{
+    return MenuItemAtlasFont::create(value, charMapFile, itemWidth, itemHeight, startCharMap, (const ccMenuCallback&)nullptr);
+}
+
+MenuItemAtlasFont * MenuItemAtlasFont::create(const std::string& value, const std::string& charMapFile, int itemWidth, int itemHeight, char startCharMap, const ccMenuCallback& callback)
+{
+    MenuItemAtlasFont *ret = new (std::nothrow) MenuItemAtlasFont();
+    ret->initWithString(value, charMapFile, itemWidth, itemHeight, startCharMap, callback);
+    ret->autorelease();
+    return ret;
+}
+
+bool MenuItemAtlasFont::initWithString(const std::string& value, const std::string& charMapFile, int itemWidth, int itemHeight, char startCharMap, const ccMenuCallback& callback)
+{
+    CCASSERT( value.size() != 0, "value length must be greater than 0");
+    LabelAtlas *label = LabelAtlas::create();
+    label->initWithString(value, charMapFile, itemWidth, itemHeight, startCharMap);
+    if (MenuItemLabel::initWithLabel(label, callback))
+    {
+        // do something ?
+    }
+    return true;
+}
+
+//
+//CCMenuItemFont
+//
+
+void MenuItemFont::setFontSize(int s)
+{
+    _globalFontSize = s;
+}
+
+int MenuItemFont::getFontSize()
+{
+    return _globalFontSize;
+}
+
+void MenuItemFont::setFontName(const std::string& name)
+{
+    if (_globalFontNameRelease)
+    {
+        _globalFontName.clear();
+    }
+    _globalFontName = name;
+    _globalFontNameRelease = true;
+}
+
+const std::string& MenuItemFont::getFontName()
+{
+    return _globalFontName;
+}
+
+MenuItemFont * MenuItemFont::create(const std::string& value, const ccMenuCallback& callback)
+{
+    MenuItemFont *ret = new (std::nothrow) MenuItemFont();
+    ret->initWithString(value, callback);
+    ret->autorelease();
+    return ret;
+}
+
+
+MenuItemFont * MenuItemFont::create(const std::string& value)
+{
+    MenuItemFont *ret = new (std::nothrow) MenuItemFont();
+    ret->initWithString(value, (const ccMenuCallback&)nullptr);
+    ret->autorelease();
+    return ret;
+}
+
+MenuItemFont::MenuItemFont()
+ : _fontSize(0), _fontName("")
+{}
+
+MenuItemFont::~MenuItemFont()
+{
+    CCLOGINFO("In the destructor of MenuItemFont (%p).", this);
+}
+
+bool MenuItemFont::initWithString(const std::string& value, const ccMenuCallback& callback)
+{
+    CCASSERT( !value.empty(), "Value length must be greater than 0");
+
+    _fontName = _globalFontName;
+    _fontSize = _globalFontSize;
+
+    Label *label = Label::createWithSystemFont(value, _fontName, _fontSize);
+    if (MenuItemLabel::initWithLabel(label, callback))
+    {
+        // do something ?
+    }
+    return true;
+}
+
+void MenuItemFont::setFontSizeObj(int s)
+{
+    _fontSize = s;
+    dynamic_cast<Label*>(_label)->setSystemFontSize(_fontSize);
+    this->setContentSize(dynamic_cast<Label*>(_label)->getContentSize());
+}
+
+int MenuItemFont::getFontSizeObj() const
+{
+    return _fontSize;
+}
+
+void MenuItemFont::setFontNameObj(const std::string& name)
+{
+    _fontName = name;
+    dynamic_cast<Label*>(_label)->setSystemFontName(_fontName);
+    this->setContentSize(dynamic_cast<Label*>(_label)->getContentSize());
+}
+
+const std::string& MenuItemFont::getFontNameObj() const
+{
+    return _fontName;
+}
+
+//
+//CCMenuItemSprite
+//
+
+void MenuItemSprite::setNormalImage(Node* image)
+{
+    if (image != _normalImage)
+    {
+        if (image)
+        {
+            addChild(image);
+            image->setAnchorPoint(Vec2(0, 0));
+        }
+
+        if (_normalImage)
+        {
+            removeChild(_normalImage, true);
+        }
+
+        _normalImage = image;
+        if (_normalImage != nullptr)
+        {
+            this->setContentSize(_normalImage->getContentSize());
+        }
+        this->updateImagesVisibility();
+    }
+}
+
+void MenuItemSprite::setSelectedImage(Node* image)
+{
+    if (image != _normalImage)
+    {
+        if (image)
+        {
+            addChild(image);
+            image->setAnchorPoint(Vec2(0, 0));
+        }
+
+        if (_selectedImage)
+        {
+            removeChild(_selectedImage, true);
+        }
+
+        _selectedImage = image;
+        this->updateImagesVisibility();
+    }
+}
+
+void MenuItemSprite::setDisabledImage(Node* image)
+{
+    if (image != _normalImage)
+    {
+        if (image)
+        {
+            addChild(image);
+            image->setAnchorPoint(Vec2(0, 0));
+        }
+
+        if (_disabledImage)
+        {
+            removeChild(_disabledImage, true);
+        }
+
+        _disabledImage = image;
+        this->updateImagesVisibility();
+    }
+}
+
+//
+//CCMenuItemSprite
+//
+
+MenuItemSprite * MenuItemSprite::create(Node* normalSprite, Node* selectedSprite, Node* disabledSprite)
+{
+    return MenuItemSprite::create(normalSprite, selectedSprite, disabledSprite, (const ccMenuCallback&)nullptr);
+}
+
+MenuItemSprite * MenuItemSprite::create(Node* normalSprite, Node* selectedSprite, const ccMenuCallback& callback)
+{
+    return MenuItemSprite::create(normalSprite, selectedSprite, nullptr, callback);
+}
+
+MenuItemSprite * MenuItemSprite::create(Node *normalSprite, Node *selectedSprite, Node *disabledSprite, const ccMenuCallback& callback)
+{
+    MenuItemSprite *ret = new (std::nothrow) MenuItemSprite();
+    ret->initWithNormalSprite(normalSprite, selectedSprite, disabledSprite, callback);
+    ret->autorelease();
+    return ret;
+}
+
+bool MenuItemSprite::initWithNormalSprite(Node* normalSprite, Node* selectedSprite, Node* disabledSprite, const ccMenuCallback& callback)
+{
+    MenuItem::initWithCallback(callback);
+    setNormalImage(normalSprite);
+    setSelectedImage(selectedSprite);
+    setDisabledImage(disabledSprite);
+
+    if(_normalImage)
+    {
+        this->setContentSize(_normalImage->getContentSize());
+    }
+
+    setCascadeColorEnabled(true);
+    setCascadeOpacityEnabled(true);
+
+    return true;
+}
+
+/**
+ @since v0.99.5
+ */
+void MenuItemSprite::selected()
+{
+    MenuItem::selected();
+
+    if (_normalImage)
+    {
+        if (_disabledImage)
+        {
+            _disabledImage->setVisible(false);
+        }
+
+        if (_selectedImage)
+        {
+            _normalImage->setVisible(false);
+            _selectedImage->setVisible(true);
+        }
+        else
+        {
+            _normalImage->setVisible(true);
+        }
+    }
+}
+
+void MenuItemSprite::unselected()
+{
+    MenuItem::unselected();
+    this->updateImagesVisibility();
+}
+
+void MenuItemSprite::setEnabled(bool bEnabled)
+{
+    if( _enabled != bEnabled ) 
+    {
+        MenuItem::setEnabled(bEnabled);
+        this->updateImagesVisibility();
+    }
+}
+
+// Helper 
+void MenuItemSprite::updateImagesVisibility()
+{
+    if (_enabled)
+    {
+        if (_normalImage)   _normalImage->setVisible(true);
+        if (_selectedImage) _selectedImage->setVisible(false);
+        if (_disabledImage) _disabledImage->setVisible(false);
+    }
+    else
+    {
+        if (_disabledImage)
+        {
+            if (_normalImage)   _normalImage->setVisible(false);
+            if (_selectedImage) _selectedImage->setVisible(false);
+            if (_disabledImage) _disabledImage->setVisible(true);
+        }
+        else
+        {
+            if (_normalImage)   _normalImage->setVisible(true);
+            if (_selectedImage) _selectedImage->setVisible(false);
+            if (_disabledImage) _disabledImage->setVisible(false);
+        }
+    }
+}
+
+///
+/// MenuItemImage
+///
+
+MenuItemImage* MenuItemImage::create()
+{
+    MenuItemImage *ret = new (std::nothrow) MenuItemImage();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool MenuItemImage::init()
+{
+    return initWithNormalImage("", "", "", (const ccMenuCallback&)nullptr);
+}
+
+MenuItemImage * MenuItemImage::create(const std::string& normalImage, const std::string& selectedImage)
+{
+    return MenuItemImage::create(normalImage, selectedImage, "", (const ccMenuCallback&)nullptr);
+}
+
+MenuItemImage * MenuItemImage::create(const std::string& normalImage, const std::string& selectedImage, const ccMenuCallback& callback)
+{
+    return MenuItemImage::create(normalImage, selectedImage, "", callback);
+}
+
+MenuItemImage * MenuItemImage::create(const std::string& normalImage, const std::string& selectedImage, const std::string& disabledImage, const ccMenuCallback& callback)
+{
+    MenuItemImage *ret = new (std::nothrow) MenuItemImage();
+    if (ret && ret->initWithNormalImage(normalImage, selectedImage, disabledImage, callback))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+MenuItemImage * MenuItemImage::create(const std::string& normalImage, const std::string& selectedImage, const std::string& disabledImage)
+{
+    MenuItemImage *ret = new (std::nothrow) MenuItemImage();
+    if (ret && ret->initWithNormalImage(normalImage, selectedImage, disabledImage, (const ccMenuCallback&)nullptr))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool MenuItemImage::initWithNormalImage(const std::string& normalImage, const std::string& selectedImage, const std::string& disabledImage, const ccMenuCallback& callback)
+{
+    Node *normalSprite = nullptr;
+    Node *selectedSprite = nullptr;
+    Node *disabledSprite = nullptr;
+
+    if (normalImage.size() >0)
+    {
+        normalSprite = Sprite::create(normalImage);
+    }
+
+    if (selectedImage.size() >0)
+    {
+        selectedSprite = Sprite::create(selectedImage);
+    }
+
+    if(disabledImage.size() >0)
+    {
+        disabledSprite = Sprite::create(disabledImage);
+    }
+    return initWithNormalSprite(normalSprite, selectedSprite, disabledSprite, callback);
+}
+
+//
+// Setter of sprite frames
+//
+void MenuItemImage::setNormalSpriteFrame(SpriteFrame * frame)
+{
+    setNormalImage(Sprite::createWithSpriteFrame(frame));
+}
+
+void MenuItemImage::setSelectedSpriteFrame(SpriteFrame * frame)
+{
+    setSelectedImage(Sprite::createWithSpriteFrame(frame));
+}
+
+void MenuItemImage::setDisabledSpriteFrame(SpriteFrame * frame)
+{
+    setDisabledImage(Sprite::createWithSpriteFrame(frame));
+}
+
+//
+// MenuItemToggle
+//
+
+MenuItemToggle * MenuItemToggle::createWithCallback(const ccMenuCallback &callback, const Vector<MenuItem*>& menuItems)
+{
+    MenuItemToggle *ret = new (std::nothrow) MenuItemToggle();
+    ret->MenuItem::initWithCallback(callback);
+    ret->autorelease();
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    if (sEngine)
+    {
+        for (const auto &item : menuItems)
+        {
+            if (item)
+            {
+                sEngine->retainScriptObject(ret, item);
+            }
+        }
+    }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    ret->_subItems = menuItems;
+    ret->_selectedIndex = UINT_MAX;
+    ret->setSelectedIndex(0);
+    return ret;
+}
+
+MenuItemToggle * MenuItemToggle::createWithCallback(const ccMenuCallback &callback, MenuItem* item, ...)
+{
+    va_list args;
+    va_start(args, item);
+    MenuItemToggle *ret = new (std::nothrow) MenuItemToggle();
+    ret->initWithCallback(callback, item, args);
+    ret->autorelease();
+    va_end(args);
+    return ret;
+}
+
+MenuItemToggle * MenuItemToggle::create()
+{
+    MenuItemToggle *ret = new (std::nothrow) MenuItemToggle();
+    ret->initWithItem(nullptr);
+    ret->autorelease();
+    return ret;
+}
+
+bool MenuItemToggle::initWithCallback(const ccMenuCallback &callback, MenuItem *item, va_list args)
+{
+    MenuItem::initWithCallback(callback);
+
+    int z = 0;
+    MenuItem *i = item;
+    
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    
+    while(i)
+    {
+        z++;
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        if (sEngine)
+        {
+            sEngine->retainScriptObject(this, i);
+        }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        _subItems.pushBack(i);
+        i = va_arg(args, MenuItem*);
+    }
+    _selectedIndex = UINT_MAX;
+    this->setSelectedIndex(0);
+    return true;
+}
+
+MenuItemToggle* MenuItemToggle::create(MenuItem *item)
+{
+    MenuItemToggle *ret = new (std::nothrow) MenuItemToggle();
+    ret->initWithItem(item);
+    ret->autorelease();
+    return ret;
+}
+
+bool MenuItemToggle::initWithItem(MenuItem *item)
+{
+    MenuItem::initWithCallback((const ccMenuCallback&)nullptr);
+
+    if (item)
+    {
+        addSubItem(item);
+    }
+    _selectedIndex = UINT_MAX;
+    this->setSelectedIndex(0);
+    
+    setCascadeColorEnabled(true);
+    setCascadeOpacityEnabled(true);
+    
+    return true;
+}
+
+void MenuItemToggle::addSubItem(MenuItem *item)
+{
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    if (sEngine)
+    {
+        sEngine->retainScriptObject(this, item);
+    }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    _subItems.pushBack(item);
+}
+
+void MenuItemToggle::cleanup()
+{
+    for(const auto &item : _subItems) {
+#if defined(CC_NATIVE_CONTROL_SCRIPT) && !CC_NATIVE_CONTROL_SCRIPT
+        ScriptEngineManager::getInstance()->getScriptEngine()->releaseScriptObject(this, item);
+#endif
+        item->cleanup();
+    }
+    MenuItem::cleanup();
+}
+
+void MenuItemToggle::setSelectedIndex(unsigned int index)
+{
+    if( index != _selectedIndex && _subItems.size() > 0 )
+    {
+        _selectedIndex = index;
+        if (_selectedItem)
+        {
+            _selectedItem->removeFromParentAndCleanup(false);
+        }
+
+        _selectedItem = _subItems.at(_selectedIndex);
+        this->addChild(_selectedItem);
+        Size s = _selectedItem->getContentSize();
+        this->setContentSize(s);
+        _selectedItem->setPosition(s.width / 2, s.height / 2);
+    }
+}
+
+void MenuItemToggle::selected()
+{
+    MenuItem::selected();
+    _subItems.at(_selectedIndex)->selected();
+}
+
+void MenuItemToggle::unselected()
+{
+    MenuItem::unselected();
+    _subItems.at(_selectedIndex)->unselected();
+}
+
+void MenuItemToggle::activate()
+{
+    // update index
+    if( _enabled ) 
+    {
+        unsigned int newIndex = (_selectedIndex + 1) % _subItems.size();
+        this->setSelectedIndex(newIndex);
+    }
+    MenuItem::activate();
+}
+void MenuItemToggle::setEnabled(bool enabled)
+{
+    if (_enabled != enabled)
+    {
+        MenuItem::setEnabled(enabled);
+
+        for(const auto &item : _subItems) {
+            item->setEnabled(enabled);
+        }
+    }
+}
+
+MenuItem* MenuItemToggle::getSelectedItem()
+{
+    return _subItems.at(_selectedIndex);
+}
+
+NS_CC_END

+ 509 - 0
3rdparty/cocos2dx/include/cocos/2d/CCMenuItem.h

@@ -0,0 +1,509 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#ifndef __CCMENU_ITEM_H__
+#define __CCMENU_ITEM_H__
+
+// C++ includes
+#include <functional>
+
+// cocos2d includes
+#include "2d/CCNode.h"
+#include "base/CCProtocols.h"
+
+NS_CC_BEGIN
+
+typedef std::function<void(Ref*)> ccMenuCallback;
+
+class Label;
+class LabelAtlas;
+class Sprite;
+class SpriteFrame;
+#define kItemSize 32
+    
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** @brief MenuItem base class.
+ *
+ *  Subclass MenuItem (or any subclass) to create your custom MenuItem objects.
+ */
+class CC_DLL MenuItem : public Node
+{
+public:
+    /** Creates a MenuItem with no target/selector. */
+    static MenuItem* create();
+    /** Creates a MenuItem with a target/selector. */
+    static MenuItem* create(const ccMenuCallback& callback);
+
+    /** Returns the outside box. */
+    Rect rect() const;
+    /** Activate the item. */
+    virtual void activate();
+    /** The item was selected (not activated), similar to "mouse-over". */
+    virtual void selected();
+    /** The item was unselected. */
+    virtual void unselected();
+    /** Returns whether or not the item is enabled. */
+    virtual bool isEnabled() const;
+    /** Enables or disables the item. */
+    virtual void setEnabled(bool value);
+    /** Returns whether or not the item is selected. */
+    virtual bool isSelected() const;
+
+    /** Set the callback to the menu item.
+    * @code
+    * In js,can contain two params,the second param is jsptr.
+    * @endcode
+    * @lua NA
+    */
+    void setCallback(const ccMenuCallback& callback);
+
+    /**
+     * @js NA
+     */
+    virtual std::string getDescription() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    MenuItem()
+    : _selected(false)
+    , _enabled(false)
+	, _callback(nullptr)
+    {}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~MenuItem();
+    
+    /** Initializes a MenuItem with a target/selector.
+     * @lua NA
+     */
+    bool initWithCallback(const ccMenuCallback& callback);
+
+protected:
+    bool            _selected;
+    bool            _enabled;
+    // callback
+    ccMenuCallback _callback;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MenuItem);
+};
+
+/** @brief An abstract class for "label" MenuItemLabel items.
+ Any Node that supports the LabelProtocol protocol can be added.
+ Supported nodes:
+ - BitmapFontAtlas
+ - LabelAtlas
+ - LabelTTF
+ - Label
+ */
+class CC_DLL MenuItemLabel : public MenuItem
+{
+public:
+    /** Creates a MenuItemLabel with a Label and a callback. */
+    static MenuItemLabel * create(Node*label, const ccMenuCallback& callback);
+
+    /** Creates a MenuItemLabel with a Label. Target and selector will be nil. */
+    static MenuItemLabel* create(Node *label);
+
+    /** Sets a new string to the inner label. */
+    void setString(const std::string& label);
+
+    /** Get the inner string of the inner label. */
+    std::string getString() const;
+
+    /** Gets the color that will be used when the item is disabled. */
+    const Color3B& getDisabledColor() const { return _disabledColor; }
+
+    /** Sets the color that will be used when the item is disabled. */
+    void setDisabledColor(const Color3B& color) { _disabledColor = color; }
+    
+    /** Gets the label that is rendered. */
+    Node* getLabel() const { return _label; }
+    
+    /** Sets the label that is rendered. */
+    void setLabel(Node* node);
+    
+    // Overrides
+    virtual void activate() override;
+    virtual void selected() override;
+    virtual void unselected() override;
+    virtual void setEnabled(bool enabled) override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    MenuItemLabel()
+    : _originalScale(0.0)
+    , _label(nullptr)
+    {}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~MenuItemLabel();
+    
+    /** Initializes a MenuItemLabel with a Label, target and selector. */
+    bool initWithLabel(Node* label, const ccMenuCallback& callback);
+    
+protected:
+    Color3B    _colorBackup;
+    float      _originalScale;
+
+    /** The color that will be used to disable the item. */
+    Color3B _disabledColor;
+    /** Label that is rendered. It can be any Node that implements the LabelProtocol. */
+    Node* _label;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MenuItemLabel);
+};
+
+
+/** @brief A MenuItemAtlasFont.
+ Helper class that creates a MenuItemLabel class with a LabelAtlas.
+ */
+class CC_DLL MenuItemAtlasFont : public MenuItemLabel
+{
+public:
+    /** Creates a menu item from a string and atlas with a target/selector. */
+    static MenuItemAtlasFont* create(const std::string& value, const std::string& charMapFile, int itemWidth, int itemHeight, char startCharMap);
+    /** Creates a menu item from a string and atlas. Use it with MenuItemToggle. */
+    static MenuItemAtlasFont* create(const std::string& value, const std::string& charMapFile, int itemWidth, int itemHeight, char startCharMap, const ccMenuCallback& callback);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    MenuItemAtlasFont(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~MenuItemAtlasFont(){}
+    
+    /** Initializes a menu item from a string and atlas with a target/selector. */
+    bool initWithString(const std::string& value, const std::string& charMapFile, int itemWidth, int itemHeight, char startCharMap, const ccMenuCallback& callback);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MenuItemAtlasFont);
+};
+
+
+/** @brief A MenuItemFont.
+ Helper class that creates a MenuItemLabel class with a Label.
+ */
+class CC_DLL MenuItemFont : public MenuItemLabel
+{
+public:
+    /** Creates a menu item from a string without target/selector. To be used with MenuItemToggle. */
+    static MenuItemFont * create(const std::string& value = "");
+    /** Creates a menu item from a string with a target/selector. */
+    static MenuItemFont * create(const std::string& value, const ccMenuCallback& callback);
+
+    /** Set default font size. */
+    static void setFontSize(int size);
+    /** Get default font size. */
+    static int getFontSize();
+    /** Set the default font name. */
+    static void setFontName(const std::string& name);
+    /** Get the default font name. */
+    static const std::string& getFontName();
+    
+    /** Set font size.
+     * c++ can not overload static and non-static member functions with the same parameter types.
+     * so change the name to setFontSizeObj.
+     * @js setFontSize
+     * @js NA
+     */
+    void setFontSizeObj(int size);
+    
+    /** get font size .
+     * @js getFontSize
+     * @js NA
+     */
+    int getFontSizeObj() const;
+    
+    /**
+     * Set the font name .
+     * c++ can not overload static and non-static member functions with the same parameter types.
+     * so change the name to setFontNameObj.
+     * @js setFontName
+     * @js NA
+     */
+    void setFontNameObj(const std::string& name);
+
+    /** Returns the name of the Font.
+     * @js getFontNameObj
+     * @js NA
+     */
+    const std::string& getFontNameObj() const;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    MenuItemFont();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~MenuItemFont();
+    
+    /** Initializes a menu item from a string with a target/selector. */
+    bool initWithString(const std::string& value, const ccMenuCallback& callback);
+    
+protected:
+    int _fontSize;
+    std::string _fontName;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MenuItemFont);
+};
+
+
+/** @brief MenuItemSprite accepts Node<RGBAProtocol> objects as items.
+ The images has 3 different states:
+ - unselected image
+ - selected image
+ - disabled image
+ 
+ @since v0.8.0
+ */
+class CC_DLL MenuItemSprite : public MenuItem
+{
+public:
+    /** Creates a menu item with a normal, selected and disabled image.*/
+    static MenuItemSprite * create(Node* normalSprite, Node* selectedSprite, Node* disabledSprite = nullptr);
+    /** Creates a menu item with a normal and selected image with a callable object. */
+    static MenuItemSprite * create(Node* normalSprite, Node* selectedSprite, const ccMenuCallback& callback);
+    /** Creates a menu item with a normal,selected  and disabled image with target/selector. */
+    static MenuItemSprite * create(Node* normalSprite, Node* selectedSprite, Node* disabledSprite, const ccMenuCallback& callback);
+
+    /** Gets the image used when the item is not selected. */
+    Node* getNormalImage() const { return _normalImage; }
+    
+    /** Sets the image used when the item is not selected. */
+    void setNormalImage(Node* image);
+    
+    /** Gets the image used when the item is selected. */
+    Node* getSelectedImage() const { return _selectedImage; }
+    
+    /** Sets the image used when the item is selected. */
+    void setSelectedImage(Node* image);
+    
+    /** Gets the image used when the item is disabled. */
+    Node* getDisabledImage() const { return _disabledImage; }
+    
+    /** Sets the image used when the item is disabled. */
+    void setDisabledImage(Node* image);
+    
+    /**
+     * The item was selected (not activated), similar to "mouse-over".
+     @since v0.99.5
+     */
+    virtual void selected();
+
+    /** The item was unselected. */
+    virtual void unselected();
+
+    /** Enables or disables the item. */
+    virtual void setEnabled(bool bEnabled);
+    
+CC_CONSTRUCTOR_ACCESS:
+    MenuItemSprite()
+    :_normalImage(nullptr)
+    ,_selectedImage(nullptr)
+    ,_disabledImage(nullptr)
+    {}
+
+    /** Initializes a menu item with a normal, selected and disabled image with a callable object. */
+    bool initWithNormalSprite(Node* normalSprite, Node* selectedSprite, Node* disabledSprite, const ccMenuCallback& callback);
+    
+protected:
+    virtual void updateImagesVisibility();
+
+    /** The image used when the item is not selected. */
+    Node* _normalImage;
+    /** The image used when the item is selected. */
+    Node* _selectedImage;
+    /** The image used when the item is disabled. */
+    Node* _disabledImage;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MenuItemSprite);
+};
+
+
+/** @brief MenuItemImage accepts images as items.
+ The images has 3 different states:
+ - unselected image
+ - selected image
+ - disabled image
+ 
+ For best results try that all images are of the same size.
+ */
+class CC_DLL MenuItemImage : public MenuItemSprite
+{
+public:
+    /** Creates an MenuItemImage. */
+    static MenuItemImage* create();
+    /** Creates a menu item with a normal and selected image.*/
+    static MenuItemImage* create(const std::string& normalImage, const std::string& selectedImage);
+    /** Creates a menu item with a normal,selected  and disabled image.*/
+    static MenuItemImage* create(const std::string& normalImage, const std::string& selectedImage, const std::string& disabledImage);
+    /** Creates a menu item with a normal and selected image with a callable object. */
+    static MenuItemImage* create(const std::string&normalImage, const std::string&selectedImage, const ccMenuCallback& callback);
+    /** Creates a menu item with a normal,selected and disabled image with a callable object. */
+    static MenuItemImage* create(const std::string&normalImage, const std::string&selectedImage, const std::string&disabledImage, const ccMenuCallback& callback);
+
+    /** Sets the sprite frame for the normal image. */
+    void setNormalSpriteFrame(SpriteFrame* frame);
+    /** Sets the sprite frame for the selected image. */
+    void setSelectedSpriteFrame(SpriteFrame* frame);
+    /** Sets the sprite frame for the disabled image. */
+    void setDisabledSpriteFrame(SpriteFrame* frame);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    MenuItemImage(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~MenuItemImage(){}
+    
+    bool init();
+    
+    /** Initializes a menu item with a normal, selected and disabled image with a callable object. */
+    bool initWithNormalImage(const std::string& normalImage, const std::string& selectedImage, const std::string& disabledImage, const ccMenuCallback& callback);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MenuItemImage);
+};
+
+
+/** @brief A MenuItemToggle.
+ A simple container class that "toggles" it's inner items.
+ The inner items can be any MenuItem.
+ */
+class CC_DLL MenuItemToggle : public MenuItem
+{
+public:
+    /**
+     *@brief Creates a menu item from a Array with a callable object.
+     */
+    static MenuItemToggle * createWithCallback(const ccMenuCallback& callback, const Vector<MenuItem*>& menuItems);
+    /** Creates a menu item from a list of items with a callable object. */
+    static MenuItemToggle* createWithCallback(const ccMenuCallback& callback, MenuItem* item, ...) CC_REQUIRES_NULL_TERMINATION;
+    /** Creates a menu item with no target/selector and no items. */
+    static MenuItemToggle* create();
+    
+    /** Creates a menu item with a item. */
+    static MenuItemToggle* create(MenuItem *item);
+    
+    /** Add more menu item. */
+    void addSubItem(MenuItem *item);
+    
+    /** Return the selected item. */
+    MenuItem* getSelectedItem();
+
+    /** Gets the index of the selected item. */
+    unsigned int getSelectedIndex() const { return _selectedIndex; }
+    
+    /** Sets the index of the selected item. */
+    void setSelectedIndex(unsigned int index);
+    
+    /** Gets the array that contains the subitems.
+     *You can add/remove items in runtime, and you can replace the array with a new one.
+     * @since v0.7.2
+     * @js NA
+     * @lua NA
+     */
+    const Vector<MenuItem*>& getSubItems() const { return _subItems; }
+    Vector<MenuItem*>& getSubItems() { return _subItems; }
+
+    /** Sets the array that contains the subitems. */
+    void setSubItems(const Vector<MenuItem*>& items) {
+        _subItems = items;
+    }
+    
+    // Overrides
+    virtual void activate() override;
+    virtual void selected() override;
+    virtual void unselected() override;
+    virtual void setEnabled(bool var) override;
+    virtual void cleanup() override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    MenuItemToggle()
+    : _selectedIndex(0)
+    , _selectedItem(nullptr)
+    {}
+    
+    /** Initializes a menu item from a list of items with a callable object. */
+    bool initWithCallback(const ccMenuCallback& callback, MenuItem* item, va_list args);
+    
+    /** Initializes a menu item with a item. */
+    bool initWithItem(MenuItem *item);
+
+protected:
+    /** Returns the selected item. */
+    unsigned int _selectedIndex;
+    MenuItem* _selectedItem;
+
+    /** Array that contains the subitems. You can add/remove items in runtime, and you can replace the array with a new one.
+     @since v0.7.2
+     */
+    Vector<MenuItem*> _subItems;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MenuItemToggle);
+
+};
+
+
+// end of 2d group
+/// @}
+
+NS_CC_END
+
+#endif //__CCMENU_ITEM_H__

+ 417 - 0
3rdparty/cocos2dx/include/cocos/2d/CCMotionStreak.cpp

@@ -0,0 +1,417 @@
+/****************************************************************************
+Copyright (c) 2011      ForzeField Studios S.L.
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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 false 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.
+****************************************************************************/
+#include "2d/CCMotionStreak.h"
+#include "math/CCVertex.h"
+#include "base/CCDirector.h"
+#include "base/ccUtils.h"
+#include "renderer/CCTextureCache.h"
+#include "renderer/CCTexture2D.h"
+#include "renderer/CCRenderer.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/ProgramState.h"
+
+NS_CC_BEGIN
+
+MotionStreak::MotionStreak()
+{
+    _customCommand.setDrawType(CustomCommand::DrawType::ARRAY);
+    _customCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE_STRIP);
+
+    auto& pipelineDescriptor = _customCommand.getPipelineDescriptor();
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR);
+    _programState = new (std::nothrow) backend::ProgramState(program);
+    pipelineDescriptor.programState = _programState;
+    _mvpMatrixLocaiton = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix");
+    _textureLocation = pipelineDescriptor.programState->getUniformLocation("u_texture");
+    
+    auto vertexLayout = _programState->getVertexLayout();
+    const auto& attributeInfo = _programState->getProgram()->getActiveAttributes();
+    auto iter = attributeInfo.find("a_position");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT2, 0, false);
+    }
+    iter = attributeInfo.find("a_texCoord");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_texCoord", iter->second.location, backend::VertexFormat::FLOAT2, 2 * sizeof(float), false);
+    }
+    iter = attributeInfo.find("a_color");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_color", iter->second.location, backend::VertexFormat::UBYTE4, 4 * sizeof(float), true);
+    }
+    vertexLayout->setLayout(4 * sizeof(float) + 4 * sizeof(uint8_t));
+}
+
+MotionStreak::~MotionStreak()
+{
+    CC_SAFE_RELEASE(_texture);
+    CC_SAFE_FREE(_pointState);
+    CC_SAFE_FREE(_pointVertexes);
+    CC_SAFE_FREE(_vertices);
+    CC_SAFE_FREE(_colorPointer);
+    CC_SAFE_FREE(_texCoords);
+}
+
+MotionStreak* MotionStreak::create(float fade, float minSeg, float stroke, const Color3B& color, const std::string& path)
+{
+    MotionStreak *ret = new (std::nothrow) MotionStreak();
+    if (ret && ret->initWithFade(fade, minSeg, stroke, color, path))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+MotionStreak* MotionStreak::create(float fade, float minSeg, float stroke, const Color3B& color, Texture2D* texture)
+{
+    MotionStreak *ret = new (std::nothrow) MotionStreak();
+    if (ret && ret->initWithFade(fade, minSeg, stroke, color, texture))
+    {
+        ret->autorelease();
+        return ret;
+    }
+
+    CC_SAFE_DELETE(ret);
+    return nullptr;
+}
+
+bool MotionStreak::initWithFade(float fade, float minSeg, float stroke, const Color3B& color, const std::string& path)
+{
+    CCASSERT(!path.empty(), "Invalid filename");
+
+    Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(path);
+    return initWithFade(fade, minSeg, stroke, color, texture);
+}
+
+bool MotionStreak::initWithFade(float fade, float minSeg, float stroke, const Color3B& color, Texture2D* texture)
+{
+    Node::setPosition(Vec2::ZERO);
+    setAnchorPoint(Vec2::ZERO);
+    setIgnoreAnchorPointForPosition(true);
+    _startingPositionInitialized = false;
+
+    _positionR.setZero();
+    _fastMode = true;
+    _minSeg = (minSeg == -1.0f) ? stroke/ 5.0f : minSeg;
+    _minSeg *= _minSeg;
+
+    _stroke = stroke;
+    _fadeDelta = 1.0f / fade;
+    
+    double fps = 1 / Director::getInstance()->getAnimationInterval();
+    _maxPoints = (int)(fade * fps) + 2;
+    
+    _pointState = (float *)malloc(sizeof(float) * _maxPoints);
+    _pointVertexes = (Vec2*)malloc(sizeof(Vec2) * _maxPoints);
+
+    const size_t VERTEX_SIZE = sizeof(Vec2) + sizeof(Tex2F) + sizeof(uint8_t) * 4;
+
+    _vertexCount = _maxPoints * 2;
+    _vertices = (Vec2*)malloc(sizeof(Vec2) * _vertexCount);
+    _texCoords = (Tex2F*)malloc(sizeof(Tex2F) * _vertexCount);
+    _colorPointer =  (uint8_t*)malloc(sizeof(uint8_t) * 4 * _vertexCount);
+    _customCommand.createVertexBuffer(VERTEX_SIZE, _vertexCount, CustomCommand::BufferUsage::DYNAMIC);
+
+    std::vector<uint8_t> zeros;
+    zeros.resize(VERTEX_SIZE * _vertexCount);
+    std::fill(zeros.begin(), zeros.end(), 0);
+    _customCommand.updateVertexBuffer(zeros.data(), zeros.size());
+
+    setTexture(texture);
+    setColor(color);
+    scheduleUpdate();
+
+    return true;
+}
+
+void MotionStreak::setPosition(const Vec2& position)
+{
+    if (!_startingPositionInitialized) {
+        _startingPositionInitialized = true;
+    }
+    _positionR = position;
+}
+
+void MotionStreak::setPosition(float x, float y)
+{
+    if (!_startingPositionInitialized) {
+        _startingPositionInitialized = true;
+    }
+    _positionR.x = x;
+    _positionR.y = y;
+}
+
+const Vec2& MotionStreak::getPosition() const
+{
+    return _positionR;
+}
+
+void MotionStreak::getPosition(float* x, float* y) const
+{
+    *x = _positionR.x;
+    *y = _positionR.y;
+}
+
+float MotionStreak::getPositionX() const
+{
+    return _positionR.x;
+}
+
+Vec3 MotionStreak::getPosition3D() const
+{
+    return Vec3(_positionR.x, _positionR.y, getPositionZ());
+}
+
+void MotionStreak::setPositionX(float x)
+{
+    if (!_startingPositionInitialized) {
+        _startingPositionInitialized = true;
+    }
+    _positionR.x = x;
+}
+
+float MotionStreak::getPositionY() const
+{
+    return  _positionR.y;
+}
+
+void MotionStreak::setPositionY(float y)
+{
+    if (!_startingPositionInitialized) {
+        _startingPositionInitialized = true;
+    }
+    _positionR.y = y;
+}
+
+void MotionStreak::tintWithColor(const Color3B& colors)
+{
+    setColor(colors);
+
+    // Fast assignation
+    for(unsigned int i = 0; i<_nuPoints*2; i++) 
+    {
+        *((Color3B*) (_colorPointer+i*4)) = colors;
+    }
+}
+
+Texture2D* MotionStreak::getTexture() const
+{
+    return _texture;
+}
+
+void MotionStreak::setTexture(Texture2D *texture)
+{
+    if (_texture != texture)
+    {
+        CC_SAFE_RETAIN(texture);
+        CC_SAFE_RELEASE(_texture);
+        _texture = texture;
+    }
+}
+
+void MotionStreak::setBlendFunc(const BlendFunc &blendFunc)
+{
+    _blendFunc = blendFunc;
+}
+
+const BlendFunc& MotionStreak::getBlendFunc() const
+{
+    return _blendFunc;
+}
+
+void MotionStreak::setOpacity(uint8_t /*opacity*/)
+{
+    CCASSERT(false, "Set opacity no supported");
+}
+
+uint8_t MotionStreak::getOpacity() const
+{
+    CCASSERT(false, "Opacity no supported");
+    return 0;
+}
+
+void MotionStreak::setOpacityModifyRGB(bool /*bValue*/)
+{
+}
+
+bool MotionStreak::isOpacityModifyRGB() const
+{
+    return false;
+}
+
+void MotionStreak::update(float delta)
+{
+    if (!_startingPositionInitialized)
+        return;
+
+    delta *= _fadeDelta;
+
+    unsigned int newIdx, newIdx2, i, i2;
+    unsigned int mov = 0;
+
+    // Update current points
+    for (i = 0; i < _nuPoints; i++)
+    {
+        _pointState[i] -= delta;
+
+        if (_pointState[i] <= 0)
+            mov++;
+        else
+        {
+            newIdx = i - mov;
+
+            if (mov > 0)
+            {
+                // Move data
+                _pointState[newIdx] = _pointState[i];
+
+                // Move point
+                _pointVertexes[newIdx] = _pointVertexes[i];
+
+                // Move vertices
+                i2 = i * 2;
+                newIdx2 = newIdx * 2;
+                _vertices[newIdx2] = _vertices[i2];
+                _vertices[newIdx2+1] = _vertices[i2+1];
+
+                // Move color
+                i2 *= 4;
+                newIdx2 *= 4;
+                _colorPointer[newIdx2+0] = _colorPointer[i2+0];
+                _colorPointer[newIdx2+1] = _colorPointer[i2+1];
+                _colorPointer[newIdx2+2] = _colorPointer[i2+2];
+                _colorPointer[newIdx2+4] = _colorPointer[i2+4];
+                _colorPointer[newIdx2+5] = _colorPointer[i2+5];
+                _colorPointer[newIdx2+6] = _colorPointer[i2+6];
+            }else
+                newIdx2 = newIdx * 8;
+
+            const uint8_t op = (uint8_t)(_pointState[newIdx] * 255.0f);
+            _colorPointer[newIdx2+3] = op;
+            _colorPointer[newIdx2+7] = op;
+        }
+    }
+    _nuPoints -= mov;
+
+    // Append new point
+    bool appendNewPoint = true;
+    if (_nuPoints >= _maxPoints)
+        appendNewPoint = false;
+    else if (_nuPoints > 0)
+    {
+        bool a1 = _pointVertexes[_nuPoints-1].getDistanceSq(_positionR) < _minSeg;
+        bool a2 = (_nuPoints == 1) ? false : (_pointVertexes[_nuPoints-2].getDistanceSq(_positionR) < (_minSeg * 2.0f));
+        if (a1 || a2)
+            appendNewPoint = false;
+    }
+
+    if (appendNewPoint)
+    {
+        _pointVertexes[_nuPoints] = _positionR;
+        _pointState[_nuPoints] = 1.0f;
+
+        // Color assignment
+        const unsigned int offset = _nuPoints * 8;
+        *((Color3B*)(_colorPointer + offset)) = _displayedColor;
+        *((Color3B*)(_colorPointer + offset+4)) = _displayedColor;
+
+        // Opacity
+        _colorPointer[offset+3] = 255;
+        _colorPointer[offset+7] = 255;
+
+        // Generate polygon
+        if (_nuPoints > 0 && _fastMode )
+        {
+            if(_nuPoints > 1)
+            {
+                ccVertexLineToPolygon(_pointVertexes, _stroke, _vertices, _nuPoints, 1);
+            }
+            else
+            {
+                ccVertexLineToPolygon(_pointVertexes, _stroke, _vertices, 0, 2);
+            }
+        }
+
+        _nuPoints++;
+    }
+
+
+    if (! _fastMode)
+        ccVertexLineToPolygon(_pointVertexes, _stroke, _vertices, 0, _nuPoints);
+
+    // Updated Tex Coords only if they are different than previous step
+    if (_nuPoints  && _previousNuPoints != _nuPoints) {
+        float texDelta = 1.0f / _nuPoints;
+        for (i = 0; i < _nuPoints; i++) {
+            _texCoords[i*2] = Tex2F(0, texDelta*i);
+            _texCoords[i*2+1] = Tex2F(1, texDelta*i);
+        }
+
+        _previousNuPoints = _nuPoints;
+    }
+}
+
+void MotionStreak::reset()
+{
+    _nuPoints = 0;
+}
+
+void MotionStreak::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
+{
+    if(_nuPoints <= 1)
+        return;
+
+    auto drawCount = _nuPoints * 2;
+        
+    _customCommand.init(_globalZOrder, _blendFunc);
+    _customCommand.setVertexDrawInfo(0, drawCount);
+    renderer->addCommand(&_customCommand);
+
+    auto programState = _customCommand.getPipelineDescriptor().programState;
+    programState->setTexture(_textureLocation, 0, _texture->getBackendTexture());
+
+    const auto& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    Mat4 finalMat = projectionMat * transform;
+    programState->setUniform(_mvpMatrixLocaiton, finalMat.m, sizeof(Mat4));
+
+    unsigned int offset = 0;
+    unsigned int vertexSize = sizeof(Vec2) + sizeof(Vec2) + sizeof(uint8_t) * 4;
+    for (unsigned int i = 0; i < drawCount; ++i)
+    {
+        offset = i * vertexSize;
+        _customCommand.updateVertexBuffer(&_vertices[i], offset, sizeof(_vertices[0]));
+        _customCommand.updateVertexBuffer(&_texCoords[i], offset + sizeof(_vertices[0]), sizeof(_texCoords[0]) );
+        _customCommand.updateVertexBuffer(&_colorPointer[i * 4], offset + sizeof(_vertices[0]) + sizeof(_texCoords[0]), 4 * sizeof(uint8_t));
+    }
+}
+
+NS_CC_END

+ 197 - 0
3rdparty/cocos2dx/include/cocos/2d/CCMotionStreak.h

@@ -0,0 +1,197 @@
+/****************************************************************************
+Copyright (c) 2011      ForzeField Studios S.L.
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#pragma once
+
+#include "base/CCProtocols.h"
+#include "2d/CCNode.h"
+#include "renderer/CCCustomCommand.h"
+
+NS_CC_BEGIN
+
+class Texture2D;
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** @class MotionStreak.
+ * @brief Creates a trailing path.
+ */
+class CC_DLL MotionStreak : public Node, public TextureProtocol
+{
+public:
+    /** Creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture filename.
+     *
+     * @param timeToFade The fade time, in seconds.
+     * @param minSeg The minimum segments.
+     * @param strokeWidth The width of stroke.
+     * @param strokeColor The color of stroke.
+     * @param imagePath The texture file name of stoke.
+     * @return An autoreleased MotionStreak object.
+     */
+    static MotionStreak* create(float timeToFade, float minSeg, float strokeWidth, const Color3B& strokeColor, const std::string& imagePath);
+    /** Creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture.
+     * 
+     * @param timeToFade The fade time, in seconds.
+     * @param minSeg The minimum segments.
+     * @param strokeWidth The width of stroke.
+     * @param strokeColor The color of stroke.
+     * @param texture The texture name of stoke.
+     * @return An autoreleased MotionStreak object.
+     */
+    static MotionStreak* create(float timeToFade, float minSeg, float strokeWidth, const Color3B& strokeColor, Texture2D* texture);
+
+    // Overrides
+    virtual void setPosition(const Vec2& position) override;
+    virtual void setPosition(float x, float y) override;
+    virtual const Vec2& getPosition() const override;
+    virtual void getPosition(float* x, float* y) const override;
+    virtual void setPositionX(float x) override;
+    virtual void setPositionY(float y) override;
+    virtual float getPositionX() const override;
+    virtual float getPositionY() const override;
+    virtual Vec3 getPosition3D() const override;
+    /**
+    * @js NA
+    * @lua NA
+    */
+    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
+    /**
+    * @lua NA
+    */
+    virtual void update(float delta) override;
+    virtual Texture2D* getTexture() const override;
+    virtual void setTexture(Texture2D *texture) override;
+    /**
+    * @js NA
+    * @lua NA
+    */
+    virtual void setBlendFunc(const BlendFunc &blendFunc) override;
+    /**
+    * @js NA
+    * @lua NA
+    */
+    virtual const BlendFunc& getBlendFunc() const override;
+    virtual uint8_t getOpacity() const override;
+    virtual void setOpacity(uint8_t opacity) override;
+    virtual void setOpacityModifyRGB(bool value) override;
+    virtual bool isOpacityModifyRGB() const override;
+
+    /** Color used for the tint.
+     *
+     * @param colors The color used for the tint.
+     */
+    void tintWithColor(const Color3B& colors);
+
+    /** Remove all living segments of the ribbon.
+     */
+    void reset();
+
+    /** When fast mode is enabled, new points are added faster but with lower precision. 
+     * 
+     * @return True if fast mode is enabled.
+     */
+    bool isFastMode() const { return _fastMode; }
+    /** Sets fast mode or not.
+     *
+     * @param bFastMode True if enabled fast mode.
+     */
+    void setFastMode(bool bFastMode) { _fastMode = bFastMode; }
+    /** Get stroke.
+     *
+     * @return float stroke.
+     */
+    float getStroke() const { return _stroke; }
+    /** Set stroke.
+     *
+     * @param stroke The width of stroke.
+     */
+    void setStroke(float stroke) { _stroke = stroke; }
+
+    /** Is the starting position initialized or not.
+     *
+     * @return True if the starting position is initialized.
+     */
+    bool isStartingPositionInitialized() const { return _startingPositionInitialized; }
+    /** Sets the starting position initialized or not.
+     *
+     * @param bStartingPositionInitialized True if initialized the starting position.
+     */
+    void setStartingPositionInitialized(bool bStartingPositionInitialized)
+    {
+        _startingPositionInitialized = bStartingPositionInitialized; 
+    }
+    
+CC_CONSTRUCTOR_ACCESS:
+    MotionStreak();
+    virtual ~MotionStreak();
+    
+    /** initializes a motion streak with fade in seconds, minimum segments, stroke's width, color and texture filename */
+    bool initWithFade(float fade, float minSeg, float stroke, const Color3B& color, const std::string& path);
+    
+    /** initializes a motion streak with fade in seconds, minimum segments, stroke's width, color and texture  */
+    bool initWithFade(float fade, float minSeg, float stroke, const Color3B& color, Texture2D* texture);
+
+protected:
+    bool _fastMode = false;
+    bool _startingPositionInitialized = false;
+
+    /** texture used for the motion streak */
+    Texture2D* _texture = nullptr;
+    BlendFunc _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
+    Vec2 _positionR;
+
+    float _stroke = 0.f;
+    float _fadeDelta = 0.f;
+    float _minSeg = 0.f;
+
+    unsigned int _maxPoints = 0;
+    unsigned int _nuPoints = 0;
+    unsigned int _previousNuPoints = 0;
+
+    /** Pointers */
+    Vec2* _pointVertexes = nullptr;
+    float* _pointState = nullptr;
+
+    Vec2* _vertices = nullptr;
+    uint8_t* _colorPointer = nullptr;
+    Tex2F* _texCoords = nullptr;
+    unsigned int _vertexCount = 0;
+    
+    CustomCommand _customCommand;
+    
+    backend::UniformLocation _mvpMatrixLocaiton;
+    backend::UniformLocation _textureLocation;
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(MotionStreak);
+};
+
+// end of _2d group
+/// @}
+
+NS_CC_END

+ 2199 - 0
3rdparty/cocos2dx/include/cocos/2d/CCNode.cpp

@@ -0,0 +1,2199 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2009      Valentin Milea
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCNode.h"
+
+#include <algorithm>
+#include <string>
+#include <regex>
+
+#include "base/CCDirector.h"
+#include "base/CCScheduler.h"
+#include "base/CCEventDispatcher.h"
+#include "base/ccUTF8.h"
+#include "2d/CCCamera.h"
+#include "2d/CCActionManager.h"
+#include "2d/CCScene.h"
+#include "2d/CCComponent.h"
+#include "renderer/CCMaterial.h"
+#include "math/TransformUtils.h"
+
+
+#if CC_NODE_RENDER_SUBPIXEL
+#define RENDER_IN_SUBPIXEL
+#else
+#define RENDER_IN_SUBPIXEL(__ARGS__) (ceil(__ARGS__))
+#endif
+
+
+NS_CC_BEGIN
+
+// FIXME:: Yes, nodes might have a sort problem once every 30 days if the game runs at 60 FPS and each frame sprites are reordered.
+std::uint32_t Node::s_globalOrderOfArrival = 0;
+int Node::__attachedNodeCount = 0;
+
+// MARK: Constructor, Destructor, Init
+
+Node::Node()
+: _rotationX(0.0f)
+, _rotationY(0.0f)
+, _rotationZ_X(0.0f)
+, _rotationZ_Y(0.0f)
+, _scaleX(1.0f)
+, _scaleY(1.0f)
+, _scaleZ(1.0f)
+, _positionZ(0.0f)
+, _usingNormalizedPosition(false)
+, _normalizedPositionDirty(false)
+, _skewX(0.0f)
+, _skewY(0.0f)
+, _anchorPoint(0, 0)
+, _contentSize(Size::ZERO)
+, _contentSizeDirty(true)
+, _transformDirty(true)
+, _inverseDirty(true)
+, _additionalTransform(nullptr)
+, _additionalTransformDirty(false)
+, _transformUpdated(true)
+// children (lazy allocs)
+// lazy alloc
+, _localZOrder$Arrival(0LL)
+, _globalZOrder(0)
+, _parent(nullptr)
+// "whole screen" objects. like Scenes and Layers, should set _ignoreAnchorPointForPosition to true
+, _tag(Node::INVALID_TAG)
+, _name("")
+, _hashOfName(0)
+// userData is always inited as nil
+, _userData(nullptr)
+, _userObject(nullptr)
+, _running(false)
+, _visible(true)
+, _ignoreAnchorPointForPosition(false)
+, _reorderChildDirty(false)
+, _isTransitionFinished(false)
+#if CC_ENABLE_SCRIPT_BINDING
+, _updateScriptHandler(0)
+#endif
+, _componentContainer(nullptr)
+, _displayedOpacity(255)
+, _realOpacity(255)
+, _displayedColor(Color3B::WHITE)
+, _realColor(Color3B::WHITE)
+, _cascadeColorEnabled(false)
+, _cascadeOpacityEnabled(false)
+, _cameraMask(1)
+, _onEnterCallback(nullptr)
+, _onExitCallback(nullptr)
+, _onEnterTransitionDidFinishCallback(nullptr)
+, _onExitTransitionDidStartCallback(nullptr)
+#if CC_USE_PHYSICS
+, _physicsBody(nullptr)
+#endif
+{
+    // set default scheduler and actionManager
+    _director = Director::getInstance();
+    _actionManager = _director->getActionManager();
+    _actionManager->retain();
+    _scheduler = _director->getScheduler();
+    _scheduler->retain();
+    _eventDispatcher = _director->getEventDispatcher();
+    _eventDispatcher->retain();
+    
+#if CC_ENABLE_SCRIPT_BINDING
+    ScriptEngineProtocol* engine = ScriptEngineManager::getInstance()->getScriptEngine();
+    _scriptType = engine != nullptr ? engine->getScriptType() : kScriptTypeNone;
+#endif
+    _transform = _inverse = Mat4::IDENTITY;
+}
+
+Node * Node::create()
+{
+    Node * ret = new (std::nothrow) Node();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+Node::~Node()
+{
+    CCLOGINFO( "deallocing Node: %p - tag: %i", this, _tag );
+    
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_updateScriptHandler)
+    {
+        ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptHandler(_updateScriptHandler);
+    }
+#endif
+
+    // User object has to be released before others, since userObject may have a weak reference of this node
+    // It may invoke `node->stopAllActions();` while `_actionManager` is null if the next line is after `CC_SAFE_RELEASE_NULL(_actionManager)`.
+    CC_SAFE_RELEASE_NULL(_userObject);
+
+    for (auto& child : _children)
+    {
+        child->_parent = nullptr;
+    }
+
+    removeAllComponents();
+    
+    CC_SAFE_DELETE(_componentContainer);
+    
+    stopAllActions();
+    unscheduleAllCallbacks();
+    CC_SAFE_RELEASE_NULL(_actionManager);
+    CC_SAFE_RELEASE_NULL(_scheduler);
+    
+    _eventDispatcher->removeEventListenersForTarget(this);
+    
+#if CC_NODE_DEBUG_VERIFY_EVENT_LISTENERS && COCOS2D_DEBUG > 0
+    _eventDispatcher->debugCheckNodeHasNoEventListenersOnDestruction(this);
+#endif
+
+    CCASSERT(!_running, "Node still marked as running on node destruction! Was base class onExit() called in derived class onExit() implementations?");
+    CC_SAFE_RELEASE(_eventDispatcher);
+
+    delete[] _additionalTransform;
+    CC_SAFE_RELEASE(_programState);
+}
+
+bool Node::init()
+{
+    return true;
+}
+
+void Node::cleanup()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnCleanup))
+            return;
+    }
+    else if (_scriptType == kScriptTypeLua)
+    {
+        ScriptEngineManager::sendNodeEventToLua(this, kNodeOnCleanup);
+    }
+#endif // #if CC_ENABLE_SCRIPT_BINDING
+    
+    // actions
+    this->stopAllActions();
+    // timers
+    this->unscheduleAllCallbacks();
+
+    // NOTE: Although it was correct that removing event listeners associated with current node in Node::cleanup.
+    // But it broke the compatibility to the versions before v3.16 .
+    // User code may call `node->removeFromParent(true)` which will trigger node's cleanup method, when the node 
+    // is added to scene again, event listeners like EventListenerTouchOneByOne will be lost. 
+    // In fact, user's code should use `node->removeFromParent(false)` in order not to do a cleanup and just remove node
+    // from its parent. For more discussion about why we revert this change is at https://github.com/cocos2d/cocos2d-x/issues/18104.
+    // We need to consider more before we want to correct the old and wrong logic code.
+    // For now, compatiblity is the most important for our users.
+//    _eventDispatcher->removeEventListenersForTarget(this);
+    
+    for( const auto &child: _children)
+        child->cleanup();
+}
+
+std::string Node::getDescription() const
+{
+    return StringUtils::format("<Node | Tag = %d", _tag);
+}
+
+// MARK: getters / setters
+
+float Node::getSkewX() const
+{
+    return _skewX;
+}
+
+void Node::setSkewX(float skewX)
+{
+    if (_skewX == skewX)
+        return;
+    
+    _skewX = skewX;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+float Node::getSkewY() const
+{
+    return _skewY;
+}
+
+void Node::setSkewY(float skewY)
+{
+    if (_skewY == skewY)
+        return;
+    
+    _skewY = skewY;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+void Node::setLocalZOrder(std::int32_t z)
+{
+    if (getLocalZOrder() == z)
+        return;
+    
+    _setLocalZOrder(z);
+    if (_parent)
+    {
+        _parent->reorderChild(this, z);
+    }
+
+    _eventDispatcher->setDirtyForNode(this);
+}
+
+/// zOrder setter : private method
+/// used internally to alter the zOrder variable. DON'T call this method manually
+void Node::_setLocalZOrder(std::int32_t z)
+{
+    _localZOrder = z;
+}
+
+void Node::updateOrderOfArrival()
+{
+    _orderOfArrival = (++s_globalOrderOfArrival);
+}
+
+void Node::setGlobalZOrder(float globalZOrder)
+{
+    if (_globalZOrder != globalZOrder)
+    {
+        _globalZOrder = globalZOrder;
+        _eventDispatcher->setDirtyForNode(this);
+    }
+}
+
+/// rotation getter
+float Node::getRotation() const
+{
+    CCASSERT(_rotationZ_X == _rotationZ_Y, "CCNode#rotation. RotationX != RotationY. Don't know which one to return");
+    return _rotationZ_X;
+}
+
+/// rotation setter
+void Node::setRotation(float rotation)
+{
+    if (_rotationZ_X == rotation)
+        return;
+    
+    _rotationZ_X = _rotationZ_Y = rotation;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+    
+    updateRotationQuat();
+}
+
+float Node::getRotationSkewX() const
+{
+    return _rotationZ_X;
+}
+
+void Node::setRotation3D(const Vec3& rotation)
+{
+    if (_rotationX == rotation.x &&
+        _rotationY == rotation.y &&
+        _rotationZ_X == rotation.z)
+        return;
+    
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+
+    _rotationX = rotation.x;
+    _rotationY = rotation.y;
+
+    // rotation Z is decomposed in 2 to simulate Skew for Flash animations
+    _rotationZ_Y = _rotationZ_X = rotation.z;
+    
+    updateRotationQuat();
+}
+
+Vec3 Node::getRotation3D() const
+{
+    // rotation Z is decomposed in 2 to simulate Skew for Flash animations
+    CCASSERT(_rotationZ_X == _rotationZ_Y, "_rotationZ_X != _rotationZ_Y");
+
+    return Vec3(_rotationX,_rotationY,_rotationZ_X);
+}
+
+void Node::updateRotationQuat()
+{
+    // convert Euler angle to quaternion
+    // when _rotationZ_X == _rotationZ_Y, _rotationQuat = RotationZ_X * RotationY * RotationX
+    // when _rotationZ_X != _rotationZ_Y, _rotationQuat = RotationY * RotationX
+    float halfRadx = CC_DEGREES_TO_RADIANS(_rotationX / 2.f), halfRady = CC_DEGREES_TO_RADIANS(_rotationY / 2.f), halfRadz = _rotationZ_X == _rotationZ_Y ? -CC_DEGREES_TO_RADIANS(_rotationZ_X / 2.f) : 0;
+    float coshalfRadx = cosf(halfRadx), sinhalfRadx = sinf(halfRadx), coshalfRady = cosf(halfRady), sinhalfRady = sinf(halfRady), coshalfRadz = cosf(halfRadz), sinhalfRadz = sinf(halfRadz);
+    _rotationQuat.x = sinhalfRadx * coshalfRady * coshalfRadz - coshalfRadx * sinhalfRady * sinhalfRadz;
+    _rotationQuat.y = coshalfRadx * sinhalfRady * coshalfRadz + sinhalfRadx * coshalfRady * sinhalfRadz;
+    _rotationQuat.z = coshalfRadx * coshalfRady * sinhalfRadz - sinhalfRadx * sinhalfRady * coshalfRadz;
+    _rotationQuat.w = coshalfRadx * coshalfRady * coshalfRadz + sinhalfRadx * sinhalfRady * sinhalfRadz;
+}
+
+void Node::updateRotation3D()
+{
+    //convert quaternion to Euler angle
+    float x = _rotationQuat.x, y = _rotationQuat.y, z = _rotationQuat.z, w = _rotationQuat.w;
+    _rotationX = atan2f(2.f * (w * x + y * z), 1.f - 2.f * (x * x + y * y));
+    float sy = 2.f * (w * y - z * x);
+    sy = clampf(sy, -1, 1);
+    _rotationY = asinf(sy);
+    _rotationZ_X = atan2f(2.f * (w * z + x * y), 1.f - 2.f * (y * y + z * z));
+    
+    _rotationX = CC_RADIANS_TO_DEGREES(_rotationX);
+    _rotationY = CC_RADIANS_TO_DEGREES(_rotationY);
+    _rotationZ_X = _rotationZ_Y = -CC_RADIANS_TO_DEGREES(_rotationZ_X);
+}
+
+void Node::setRotationQuat(const Quaternion& quat)
+{
+    _rotationQuat = quat;
+    updateRotation3D();
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+Quaternion Node::getRotationQuat() const
+{
+    return _rotationQuat;
+}
+
+void Node::setRotationSkewX(float rotationX)
+{
+    if (_rotationZ_X == rotationX)
+        return;
+    
+    _rotationZ_X = rotationX;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+    
+    updateRotationQuat();
+}
+
+float Node::getRotationSkewY() const
+{
+    return _rotationZ_Y;
+}
+
+void Node::setRotationSkewY(float rotationY)
+{
+    if (_rotationZ_Y == rotationY)
+        return;
+    
+    _rotationZ_Y = rotationY;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+    
+    updateRotationQuat();
+}
+
+/// scale getter
+float Node::getScale() const
+{
+    CCASSERT( _scaleX == _scaleY, "CCNode#scale. ScaleX != ScaleY. Don't know which one to return");
+    return _scaleX;
+}
+
+/// scale setter
+void Node::setScale(float scale)
+{
+    if (_scaleX == scale && _scaleY == scale && _scaleZ == scale)
+        return;
+    
+    _scaleX = _scaleY = _scaleZ = scale;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+/// scaleX getter
+float Node::getScaleX() const
+{
+    return _scaleX;
+}
+
+/// scale setter
+void Node::setScale(float scaleX,float scaleY)
+{
+    if (_scaleX == scaleX && _scaleY == scaleY)
+        return;
+    
+    _scaleX = scaleX;
+    _scaleY = scaleY;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+/// scaleX setter
+void Node::setScaleX(float scaleX)
+{
+    if (_scaleX == scaleX)
+        return;
+    
+    _scaleX = scaleX;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+/// scaleY getter
+float Node::getScaleY() const
+{
+    return _scaleY;
+}
+
+/// scaleY setter
+void Node::setScaleZ(float scaleZ)
+{
+    if (_scaleZ == scaleZ)
+        return;
+    
+    _scaleZ = scaleZ;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+/// scaleY getter
+float Node::getScaleZ() const
+{
+    return _scaleZ;
+}
+
+/// scaleY setter
+void Node::setScaleY(float scaleY)
+{
+    if (_scaleY == scaleY)
+        return;
+    
+    _scaleY = scaleY;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+
+/// position getter
+const Vec2& Node::getPosition() const
+{
+    return _position;
+}
+
+/// position setter
+void Node::setPosition(const Vec2& position)
+{
+    setPosition(position.x, position.y);
+}
+
+void Node::getPosition(float* x, float* y) const
+{
+    *x = _position.x;
+    *y = _position.y;
+}
+
+void Node::setPosition(float x, float y)
+{
+    if (_position.x == x && _position.y == y)
+        return;
+    
+    _position.x = x;
+    _position.y = y;
+    
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+    _usingNormalizedPosition = false;
+}
+
+void Node::setPosition3D(const Vec3& position)
+{
+    setPositionZ(position.z);
+    setPosition(position.x, position.y);
+}
+
+Vec3 Node::getPosition3D() const
+{
+    return Vec3(_position.x, _position.y, _positionZ);
+}
+
+float Node::getPositionX() const
+{
+    return _position.x;
+}
+
+void Node::setPositionX(float x)
+{
+    setPosition(x, _position.y);
+}
+
+float Node::getPositionY() const
+{
+    return  _position.y;
+}
+
+void Node::setPositionY(float y)
+{
+    setPosition(_position.x, y);
+}
+
+float Node::getPositionZ() const
+{
+    return _positionZ;
+}
+
+void Node::setPositionZ(float positionZ)
+{
+    if (_positionZ == positionZ)
+        return;
+    
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+
+    _positionZ = positionZ;
+}
+
+/// position getter
+const Vec2& Node::getPositionNormalized() const
+{
+    return _normalizedPosition;
+}
+
+/// position setter
+void Node::setPositionNormalized(const Vec2& position)
+{
+    if (_normalizedPosition.equals(position))
+        return;
+
+    _normalizedPosition = position;
+    _usingNormalizedPosition = true;
+    _normalizedPositionDirty = true;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+ssize_t Node::getChildrenCount() const
+{
+    return _children.size();
+}
+
+/// isVisible getter
+bool Node::isVisible() const
+{
+    return _visible;
+}
+
+/// isVisible setter
+void Node::setVisible(bool visible)
+{
+    if(visible != _visible)
+    {
+        _visible = visible;
+        if(_visible)
+            _transformUpdated = _transformDirty = _inverseDirty = true;
+    }
+}
+
+const Vec2& Node::getAnchorPointInPoints() const
+{
+    return _anchorPointInPoints;
+}
+
+/// anchorPoint getter
+const Vec2& Node::getAnchorPoint() const
+{
+    return _anchorPoint;
+}
+
+void Node::setAnchorPoint(const Vec2& point)
+{
+    if (! point.equals(_anchorPoint))
+    {
+        _anchorPoint = point;
+        _anchorPointInPoints.set(_contentSize.width * _anchorPoint.x, _contentSize.height * _anchorPoint.y);
+        _transformUpdated = _transformDirty = _inverseDirty = true;
+    }
+}
+
+/// contentSize getter
+const Size& Node::getContentSize() const
+{
+    return _contentSize;
+}
+
+void Node::setContentSize(const Size & size)
+{
+    if (! size.equals(_contentSize))
+    {
+        _contentSize = size;
+
+        _anchorPointInPoints.set(_contentSize.width * _anchorPoint.x, _contentSize.height * _anchorPoint.y);
+        _transformUpdated = _transformDirty = _inverseDirty = _contentSizeDirty = true;
+    }
+}
+
+// isRunning getter
+bool Node::isRunning() const
+{
+    return _running;
+}
+
+/// parent setter
+void Node::setParent(Node * parent)
+{
+    _parent = parent;
+    _transformUpdated = _transformDirty = _inverseDirty = true;
+}
+
+/// isRelativeAnchorPoint getter
+bool Node::isIgnoreAnchorPointForPosition() const
+{
+    return _ignoreAnchorPointForPosition;
+}
+
+/// isRelativeAnchorPoint setter
+void Node::setIgnoreAnchorPointForPosition(bool newValue)
+{
+    if (newValue != _ignoreAnchorPointForPosition) 
+    {
+        _ignoreAnchorPointForPosition = newValue;
+        _transformUpdated = _transformDirty = _inverseDirty = true;
+    }
+}
+
+/// tag getter
+int Node::getTag() const
+{
+    return _tag;
+}
+
+/// tag setter
+void Node::setTag(int tag)
+{
+    _tag = tag ;
+}
+
+const std::string& Node::getName() const
+{
+    return _name;
+}
+
+void Node::setName(const std::string& name)
+{
+    _name = name;
+    std::hash<std::string> h;
+    _hashOfName = h(name);
+}
+
+/// userData setter
+void Node::setUserData(void *userData)
+{
+    _userData = userData;
+}
+
+void Node::setUserObject(Ref* userObject)
+{
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    if (sEngine)
+    {
+        if (userObject)
+            sEngine->retainScriptObject(this, userObject);
+        if (_userObject)
+            sEngine->releaseScriptObject(this, _userObject);
+    }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    CC_SAFE_RETAIN(userObject);
+    CC_SAFE_RELEASE(_userObject);
+    _userObject = userObject;
+}
+
+Scene* Node::getScene() const
+{
+    if (!_parent)
+        return nullptr;
+    
+    auto sceneNode = _parent;
+    while (sceneNode->_parent)
+    {
+        sceneNode = sceneNode->_parent;
+    }
+
+    return dynamic_cast<Scene*>(sceneNode);
+}
+
+Rect Node::getBoundingBox() const
+{
+    Rect rect(0, 0, _contentSize.width, _contentSize.height);
+    return RectApplyAffineTransform(rect, getNodeToParentAffineTransform());
+}
+
+// MARK: Children logic
+
+// lazy allocs
+void Node::childrenAlloc()
+{
+    _children.reserve(4);
+}
+
+Node* Node::getChildByTag(int tag) const
+{
+    CCASSERT(tag != Node::INVALID_TAG, "Invalid tag");
+
+    for (const auto child : _children)
+    {
+        if(child && child->_tag == tag)
+            return child;
+    }
+    return nullptr;
+}
+
+Node* Node::getChildByName(const std::string& name) const
+{
+    CCASSERT(!name.empty(), "Invalid name");
+    
+    std::hash<std::string> h;
+    size_t hash = h(name);
+    
+    for (const auto& child : _children)
+    {
+        // Different strings may have the same hash code, but can use it to compare first for speed
+        if(child->_hashOfName == hash && child->_name.compare(name) == 0)
+            return child;
+    }
+    return nullptr;
+}
+
+void Node::enumerateChildren(const std::string &name, std::function<bool (Node *)> callback) const
+{
+    CCASSERT(!name.empty(), "Invalid name");
+    CCASSERT(callback != nullptr, "Invalid callback function");
+    
+    size_t length = name.length();
+    
+    size_t subStrStartPos = 0;  // sub string start index
+    size_t subStrlength = length; // sub string length
+    
+    // Starts with '//'?
+    bool searchRecursively = false;
+    if (length > 2 && name[0] == '/' && name[1] == '/')
+    {
+        searchRecursively = true;
+        subStrStartPos = 2;
+        subStrlength -= 2;
+    }
+    
+    // End with '/..'?
+    bool searchFromParent = false;
+    if (length > 3 &&
+        name[length-3] == '/' &&
+        name[length-2] == '.' &&
+        name[length-1] == '.')
+    {
+        searchFromParent = true;
+        subStrlength -= 3;
+    }
+    
+    // Remove '//', '/..' if exist
+    std::string newName = name.substr(subStrStartPos, subStrlength);
+    
+    const Node* target = this;
+    
+    if (searchFromParent)
+    {
+        if (nullptr == _parent)
+        {
+            return;
+        }
+        target = _parent;
+    }
+    
+    if (searchRecursively)
+    {
+        // name is '//xxx'
+        target->doEnumerateRecursive(target, newName, callback);
+    }
+    else
+    {
+        // name is xxx
+        target->doEnumerate(newName, callback);
+    }
+}
+
+bool Node::doEnumerateRecursive(const Node* node, const std::string &name, std::function<bool (Node *)> callback) const
+{
+    bool ret =false;
+    
+    if (node->doEnumerate(name, callback))
+    {
+        // search itself
+        ret = true;
+    }
+    else
+    {
+        // search its children
+        for (const auto& child : node->getChildren())
+        {
+            if (doEnumerateRecursive(child, name, callback))
+            {
+                ret = true;
+                break;
+            }
+        }
+    }
+    
+    return ret;
+}
+
+bool Node::doEnumerate(std::string name, std::function<bool (Node *)> callback) const
+{
+    // name may be xxx/yyy, should find its parent
+    size_t pos = name.find('/');
+    std::string searchName = name;
+    bool needRecursive = false;
+    if (pos != name.npos)
+    {
+        searchName = name.substr(0, pos);
+        name.erase(0, pos+1);
+        needRecursive = true;
+    }
+    
+    bool ret = false;
+    for (const auto& child : getChildren())
+    {
+        if (std::regex_match(child->_name, std::regex(searchName)))
+        {
+            if (!needRecursive)
+            {
+                // terminate enumeration if callback return true
+                if (callback(child))
+                {
+                    ret = true;
+                    break;
+                }
+            }
+            else
+            {
+                ret = child->doEnumerate(name, callback);
+                if (ret)
+                    break;
+            }
+        }
+    }
+    
+    return ret;
+}
+
+/* "add" logic MUST only be on this method
+* If a class want's to extend the 'addChild' behavior it only needs
+* to override this method
+*/
+void Node::addChild(Node *child, int localZOrder, int tag)
+{    
+    CCASSERT( child != nullptr, "Argument must be non-nil");
+    CCASSERT( child->_parent == nullptr, "child already added. It can't be added again");
+
+    addChildHelper(child, localZOrder, tag, "", true);
+}
+
+void Node::addChild(Node* child, int localZOrder, const std::string &name)
+{
+    CCASSERT(child != nullptr, "Argument must be non-nil");
+    CCASSERT(child->_parent == nullptr, "child already added. It can't be added again");
+    
+    addChildHelper(child, localZOrder, INVALID_TAG, name, false);
+}
+
+void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)
+{
+    auto assertNotSelfChild
+        ( [ this, child ]() -> bool
+          {
+              for ( Node* parent( getParent() ); parent != nullptr;
+                    parent = parent->getParent() )
+                  if ( parent == child )
+                      return false;
+              
+              return true;
+          } );
+    (void)assertNotSelfChild;
+    
+    CCASSERT( assertNotSelfChild(),
+              "A node cannot be the child of his own children" );
+    
+    if (_children.empty())
+    {
+        this->childrenAlloc();
+    }
+    
+    this->insertChild(child, localZOrder);
+    
+    if (setTag)
+        child->setTag(tag);
+    else
+        child->setName(name);
+    
+    child->setParent(this);
+
+    child->updateOrderOfArrival();
+
+    if( _running )
+    {
+        child->onEnter();
+        // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter
+        if (_isTransitionFinished)
+        {
+            child->onEnterTransitionDidFinish();
+        }
+    }
+    
+    if (_cascadeColorEnabled)
+    {
+        updateCascadeColor();
+    }
+    
+    if (_cascadeOpacityEnabled)
+    {
+        updateCascadeOpacity();
+    }
+}
+
+void Node::addChild(Node *child, int zOrder)
+{
+    CCASSERT( child != nullptr, "Argument must be non-nil");
+    this->addChild(child, zOrder, child->_name);
+}
+
+void Node::addChild(Node *child)
+{
+    CCASSERT( child != nullptr, "Argument must be non-nil");
+    this->addChild(child, child->getLocalZOrder(), child->_name);
+}
+
+void Node::removeFromParent()
+{
+    this->removeFromParentAndCleanup(true);
+}
+
+void Node::removeFromParentAndCleanup(bool cleanup)
+{
+    if (_parent != nullptr)
+    {
+        _parent->removeChild(this,cleanup);
+    } 
+}
+
+/* "remove" logic MUST only be on this method
+* If a class want's to extend the 'removeChild' behavior it only needs
+* to override this method
+*/
+void Node::removeChild(Node* child, bool cleanup /* = true */)
+{
+    // explicit nil handling
+    if (_children.empty())
+    {
+        return;
+    }
+
+    ssize_t index = _children.getIndex(child);
+    if( index != CC_INVALID_INDEX )
+        this->detachChild( child, index, cleanup );
+}
+
+void Node::removeChildByTag(int tag, bool cleanup/* = true */)
+{
+    CCASSERT( tag != Node::INVALID_TAG, "Invalid tag");
+
+    Node *child = this->getChildByTag(tag);
+
+    if (child == nullptr)
+    {
+        CCLOG("cocos2d: removeChildByTag(tag = %d): child not found!", tag);
+    }
+    else
+    {
+        this->removeChild(child, cleanup);
+    }
+}
+
+void Node::removeChildByName(const std::string &name, bool cleanup)
+{
+    CCASSERT(!name.empty(), "Invalid name");
+    
+    Node *child = this->getChildByName(name);
+    
+    if (child == nullptr)
+    {
+        CCLOG("cocos2d: removeChildByName(name = %s): child not found!", name.c_str());
+    }
+    else
+    {
+        this->removeChild(child, cleanup);
+    }
+}
+
+void Node::removeAllChildren()
+{
+    this->removeAllChildrenWithCleanup(true);
+}
+
+void Node::removeAllChildrenWithCleanup(bool cleanup)
+{
+    // not using detachChild improves speed here
+    for (const auto& child : _children)
+    {
+        // IMPORTANT:
+        //  -1st do onExit
+        //  -2nd cleanup
+        if(_running)
+        {
+            child->onExitTransitionDidStart();
+            child->onExit();
+        }
+
+        if (cleanup)
+        {
+            child->cleanup();
+        }
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+        if (sEngine)
+        {
+            sEngine->releaseScriptObject(this, child);
+        }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        // set parent nil at the end
+        child->setParent(nullptr);
+    }
+    
+    _children.clear();
+}
+
+void Node::detachChild(Node *child, ssize_t childIndex, bool doCleanup)
+{
+    // IMPORTANT:
+    //  -1st do onExit
+    //  -2nd cleanup
+    if (_running)
+    {
+        child->onExitTransitionDidStart();
+        child->onExit();
+    }
+
+    // If you don't do cleanup, the child's actions will not get removed and the
+    // its scheduledSelectors_ dict will not get released!
+    if (doCleanup)
+    {
+        child->cleanup();
+    }
+    
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    if (sEngine)
+    {
+        sEngine->releaseScriptObject(this, child);
+    }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    // set parent nil at the end
+    child->setParent(nullptr);
+
+    _children.erase(childIndex);
+}
+
+
+// helper used by reorderChild & add
+void Node::insertChild(Node* child, int z)
+{
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    if (sEngine)
+    {
+        sEngine->retainScriptObject(this, child);
+    }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    _transformUpdated = true;
+    _reorderChildDirty = true;
+    _children.pushBack(child);
+    child->_setLocalZOrder(z);
+}
+
+void Node::reorderChild(Node *child, int zOrder)
+{
+    CCASSERT( child != nullptr, "Child must be non-nil");
+    _reorderChildDirty = true;
+    child->updateOrderOfArrival();
+    child->_setLocalZOrder(zOrder);
+}
+
+void Node::sortAllChildren()
+{
+    if (_reorderChildDirty)
+    {
+        sortNodes(_children);
+        _reorderChildDirty = false;
+        _eventDispatcher->setDirtyForNode(this);
+    }
+}
+
+// MARK: draw / visit
+
+void Node::draw()
+{
+    auto renderer = _director->getRenderer();
+    draw(renderer, _modelViewTransform, FLAGS_TRANSFORM_DIRTY);
+}
+
+void Node::draw(Renderer* /*renderer*/, const Mat4 & /*transform*/, uint32_t /*flags*/)
+{
+}
+
+void Node::visit()
+{
+    auto renderer = _director->getRenderer();
+    auto& parentTransform = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+    visit(renderer, parentTransform, FLAGS_TRANSFORM_DIRTY);
+}
+
+uint32_t Node::processParentFlags(const Mat4& parentTransform, uint32_t parentFlags)
+{
+    if(_usingNormalizedPosition)
+    {
+        CCASSERT(_parent, "setPositionNormalized() doesn't work with orphan nodes");
+        if ((parentFlags & FLAGS_CONTENT_SIZE_DIRTY) || _normalizedPositionDirty)
+        {
+            auto& s = _parent->getContentSize();
+            _position.x = _normalizedPosition.x * s.width;
+            _position.y = _normalizedPosition.y * s.height;
+            _transformUpdated = _transformDirty = _inverseDirty = true;
+            _normalizedPositionDirty = false;
+        }
+    }
+
+    // Fixes Github issue #16100. Basically when having two cameras, one camera might set as dirty the
+    // node that is not visited by it, and might affect certain calculations. Besides, it is faster to do this.
+    if (!isVisitableByVisitingCamera())
+        return parentFlags;
+
+    uint32_t flags = parentFlags;
+    flags |= (_transformUpdated ? FLAGS_TRANSFORM_DIRTY : 0);
+    flags |= (_contentSizeDirty ? FLAGS_CONTENT_SIZE_DIRTY : 0);
+    
+
+    if(flags & FLAGS_DIRTY_MASK)
+        _modelViewTransform = this->transform(parentTransform);
+    
+    _transformUpdated = false;
+    _contentSizeDirty = false;
+
+    return flags;
+}
+
+bool Node::isVisitableByVisitingCamera() const
+{
+    auto camera = Camera::getVisitingCamera();
+    bool visibleByCamera = camera ? ((unsigned short)camera->getCameraFlag() & _cameraMask) != 0 : true;
+    return visibleByCamera;
+}
+
+void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
+{
+    // quick return if not visible. children won't be drawn.
+    if (!_visible)
+    {
+        return;
+    }
+
+    uint32_t flags = processParentFlags(parentTransform, parentFlags);
+
+    // IMPORTANT:
+    // To ease the migration to v3.0, we still support the Mat4 stack,
+    // but it is deprecated and your code should not rely on it
+    _director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+    _director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
+    
+    bool visibleByCamera = isVisitableByVisitingCamera();
+
+    int i = 0;
+
+    if(!_children.empty())
+    {
+        sortAllChildren();
+        // draw children zOrder < 0
+        for(auto size = _children.size(); i < size; ++i)
+        {
+            auto node = _children.at(i);
+
+            if (node && node->_localZOrder < 0)
+                node->visit(renderer, _modelViewTransform, flags);
+            else
+                break;
+        }
+        // self draw
+        if (visibleByCamera)
+            this->draw(renderer, _modelViewTransform, flags);
+
+        for(auto it=_children.cbegin()+i, itCend = _children.cend(); it != itCend; ++it)
+            (*it)->visit(renderer, _modelViewTransform, flags);
+    }
+    else if (visibleByCamera)
+    {
+        this->draw(renderer, _modelViewTransform, flags);
+    }
+
+    _director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+    
+    // FIX ME: Why need to set _orderOfArrival to 0??
+    // Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
+    // reset for next frame
+    // _orderOfArrival = 0;
+}
+
+Mat4 Node::transform(const Mat4& parentTransform)
+{
+    return parentTransform * this->getNodeToParentTransform();
+}
+
+// MARK: events
+
+void Node::onEnter()
+{
+    if (!_running)
+    {
+        ++__attachedNodeCount;
+    }
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnEnter))
+            return;
+    }
+#endif
+    
+    if (_onEnterCallback)
+        _onEnterCallback();
+
+    if (_componentContainer && !_componentContainer->isEmpty())
+    {
+        _componentContainer->onEnter();
+    }
+    
+    _isTransitionFinished = false;
+    
+    for( const auto &child: _children)
+        child->onEnter();
+    
+    this->resume();
+    
+    _running = true;
+    
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeLua)
+    {
+        ScriptEngineManager::sendNodeEventToLua(this, kNodeOnEnter);
+    }
+#endif
+}
+
+void Node::onEnterTransitionDidFinish()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnEnterTransitionDidFinish))
+            return;
+    }
+#endif
+    
+    if (_onEnterTransitionDidFinishCallback)
+        _onEnterTransitionDidFinishCallback();
+
+    _isTransitionFinished = true;
+    for( const auto &child: _children)
+        child->onEnterTransitionDidFinish();
+    
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeLua)
+    {
+        ScriptEngineManager::sendNodeEventToLua(this, kNodeOnEnterTransitionDidFinish);
+    }
+#endif
+}
+
+void Node::onExitTransitionDidStart()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnExitTransitionDidStart))
+            return;
+    }
+#endif
+    
+    if (_onExitTransitionDidStartCallback)
+        _onExitTransitionDidStartCallback();
+    
+    for( const auto &child: _children)
+        child->onExitTransitionDidStart();
+    
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeLua)
+    {
+        ScriptEngineManager::sendNodeEventToLua(this, kNodeOnExitTransitionDidStart);
+    }
+#endif
+}
+
+void Node::onExit()
+{
+    if (_running)
+    {
+        --__attachedNodeCount;
+    }
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnExit))
+            return;
+    }
+#endif
+    
+    if (_onExitCallback)
+        _onExitCallback();
+    
+    if (_componentContainer && !_componentContainer->isEmpty())
+    {
+        _componentContainer->onExit();
+    }
+    
+    this->pause();
+    
+    _running = false;
+    
+    for( const auto &child: _children)
+        child->onExit();
+    
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeLua)
+    {
+        ScriptEngineManager::sendNodeEventToLua(this, kNodeOnExit);
+    }
+#endif
+}
+
+void Node::setEventDispatcher(EventDispatcher* dispatcher)
+{
+    if (dispatcher != _eventDispatcher)
+    {
+        _eventDispatcher->removeEventListenersForTarget(this);
+        CC_SAFE_RETAIN(dispatcher);
+        CC_SAFE_RELEASE(_eventDispatcher);
+        _eventDispatcher = dispatcher;
+    }
+}
+
+void Node::setActionManager(ActionManager* actionManager)
+{
+    if( actionManager != _actionManager )
+    {
+        this->stopAllActions();
+        CC_SAFE_RETAIN(actionManager);
+        CC_SAFE_RELEASE(_actionManager);
+        _actionManager = actionManager;
+    }
+}
+
+// MARK: actions
+
+Action * Node::runAction(Action* action)
+{
+    CCASSERT( action != nullptr, "Argument must be non-nil");
+    _actionManager->addAction(action, this, !_running);
+    return action;
+}
+
+void Node::stopAllActions()
+{
+    _actionManager->removeAllActionsFromTarget(this);
+}
+
+void Node::stopAction(Action* action)
+{
+    _actionManager->removeAction(action);
+}
+
+void Node::stopActionByTag(int tag)
+{
+    CCASSERT( tag != Action::INVALID_TAG, "Invalid tag");
+    _actionManager->removeActionByTag(tag, this);
+}
+
+void Node::stopAllActionsByTag(int tag)
+{
+    CCASSERT( tag != Action::INVALID_TAG, "Invalid tag");
+    _actionManager->removeAllActionsByTag(tag, this);
+}
+
+void Node::stopActionsByFlags(unsigned int flags)
+{
+    if (flags > 0)
+    {
+        _actionManager->removeActionsByFlags(flags, this);
+    }
+}
+
+Action * Node::getActionByTag(int tag)
+{
+    CCASSERT( tag != Action::INVALID_TAG, "Invalid tag");
+    return _actionManager->getActionByTag(tag, this);
+}
+
+ssize_t Node::getNumberOfRunningActions() const
+{
+    return _actionManager->getNumberOfRunningActionsInTarget(this);
+}
+
+ssize_t Node::getNumberOfRunningActionsByTag(int tag) const
+{
+    return _actionManager->getNumberOfRunningActionsInTargetByTag(this, tag);
+}
+
+
+// MARK: Callbacks
+
+void Node::setScheduler(Scheduler* scheduler)
+{
+    if( scheduler != _scheduler )
+    {
+        this->unscheduleAllCallbacks();
+        CC_SAFE_RETAIN(scheduler);
+        CC_SAFE_RELEASE(_scheduler);
+        _scheduler = scheduler;
+    }
+}
+
+bool Node::isScheduled(SEL_SCHEDULE selector) const
+{
+    return _scheduler->isScheduled(selector, this);
+}
+
+bool Node::isScheduled(const std::string &key) const
+{
+    return _scheduler->isScheduled(key, this);
+}
+
+void Node::scheduleUpdate()
+{
+    scheduleUpdateWithPriority(0);
+}
+
+void Node::scheduleUpdateWithPriority(int priority)
+{
+    _scheduler->scheduleUpdate(this, priority, !_running);
+}
+
+void Node::scheduleUpdateWithPriorityLua(int nHandler, int priority)
+{
+    unscheduleUpdate();
+    
+#if CC_ENABLE_SCRIPT_BINDING
+    _updateScriptHandler = nHandler;
+#endif
+    
+    _scheduler->scheduleUpdate(this, priority, !_running);
+}
+
+void Node::unscheduleUpdate()
+{
+    _scheduler->unscheduleUpdate(this);
+    
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_updateScriptHandler)
+    {
+        ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptHandler(_updateScriptHandler);
+        _updateScriptHandler = 0;
+    }
+#endif
+}
+
+void Node::schedule(SEL_SCHEDULE selector)
+{
+    this->schedule(selector, 0.0f, CC_REPEAT_FOREVER, 0.0f);
+}
+
+void Node::schedule(SEL_SCHEDULE selector, float interval)
+{
+    this->schedule(selector, interval, CC_REPEAT_FOREVER, 0.0f);
+}
+
+void Node::schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay)
+{
+    CCASSERT( selector, "Argument must be non-nil");
+    CCASSERT( interval >=0, "Argument must be positive");
+
+    _scheduler->schedule(selector, this, interval , repeat, delay, !_running);
+}
+
+void Node::schedule(const std::function<void(float)> &callback, const std::string &key)
+{
+    _scheduler->schedule(callback, this, 0, !_running, key);
+}
+
+void Node::schedule(const std::function<void(float)> &callback, float interval, const std::string &key)
+{
+    _scheduler->schedule(callback, this, interval, !_running, key);
+}
+
+void Node::schedule(const std::function<void(float)>& callback, float interval, unsigned int repeat, float delay, const std::string &key)
+{
+    _scheduler->schedule(callback, this, interval, repeat, delay, !_running, key);
+}
+
+void Node::scheduleOnce(SEL_SCHEDULE selector, float delay)
+{
+    this->schedule(selector, 0.0f, 0, delay);
+}
+
+void Node::scheduleOnce(const std::function<void(float)> &callback, float delay, const std::string &key)
+{
+    _scheduler->schedule(callback, this, 0, 0, delay, !_running, key);
+}
+
+void Node::unschedule(SEL_SCHEDULE selector)
+{
+    // explicit null handling
+    if (selector == nullptr)
+        return;
+    
+    _scheduler->unschedule(selector, this);
+}
+
+void Node::unschedule(const std::string &key)
+{
+    _scheduler->unschedule(key, this);
+}
+
+void Node::unscheduleAllCallbacks()
+{
+    _scheduler->unscheduleAllForTarget(this);
+}
+
+void Node::resume()
+{
+    _scheduler->resumeTarget(this);
+    _actionManager->resumeTarget(this);
+    _eventDispatcher->resumeEventListenersForTarget(this);
+}
+
+void Node::pause()
+{
+    _scheduler->pauseTarget(this);
+    _actionManager->pauseTarget(this);
+    _eventDispatcher->pauseEventListenersForTarget(this);
+}
+
+// override me
+void Node::update(float fDelta)
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (0 != _updateScriptHandler)
+    {
+        //only lua use
+        SchedulerScriptData data(_updateScriptHandler,fDelta);
+        ScriptEvent event(kScheduleEvent,&data);
+        ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&event);
+    }
+#endif
+    
+    if (_componentContainer && !_componentContainer->isEmpty())
+    {
+        _componentContainer->visit(fDelta);
+    }
+}
+
+// MARK: coordinates
+
+AffineTransform Node::getNodeToParentAffineTransform() const
+{
+    AffineTransform ret;
+    GLToCGAffine(getNodeToParentTransform().m, &ret);
+
+    return ret;
+}
+
+
+Mat4 Node::getNodeToParentTransform(Node* ancestor) const
+{
+    Mat4 t(this->getNodeToParentTransform());
+
+    for (Node *p = _parent;  p != nullptr && p != ancestor ; p = p->getParent())
+    {
+        t = p->getNodeToParentTransform() * t;
+    }
+
+    return t;
+}
+
+AffineTransform Node::getNodeToParentAffineTransform(Node* ancestor) const
+{
+    AffineTransform t(this->getNodeToParentAffineTransform());
+
+    for (Node *p = _parent; p != nullptr && p != ancestor; p = p->getParent())
+        t = AffineTransformConcat(t, p->getNodeToParentAffineTransform());
+
+    return t;
+}
+const Mat4& Node::getNodeToParentTransform() const
+{
+    if (_transformDirty)
+    {
+        // Translate values
+        float x = _position.x;
+        float y = _position.y;
+        float z = _positionZ;
+        
+        if (_ignoreAnchorPointForPosition)
+        {
+            x += _anchorPointInPoints.x;
+            y += _anchorPointInPoints.y;
+        }
+        
+        bool needsSkewMatrix = ( _skewX || _skewY );
+
+        // Build Transform Matrix = translation * rotation * scale
+        Mat4 translation;
+        //move to anchor point first, then rotate
+        Mat4::createTranslation(x, y, z, &translation);
+        
+        Mat4::createRotation(_rotationQuat, &_transform);
+        
+        if (_rotationZ_X != _rotationZ_Y)
+        {
+            // Rotation values
+            // Change rotation code to handle X and Y
+            // If we skew with the exact same value for both x and y then we're simply just rotating
+            float radiansX = -CC_DEGREES_TO_RADIANS(_rotationZ_X);
+            float radiansY = -CC_DEGREES_TO_RADIANS(_rotationZ_Y);
+            float cx = cosf(radiansX);
+            float sx = sinf(radiansX);
+            float cy = cosf(radiansY);
+            float sy = sinf(radiansY);
+            
+            float m0 = _transform.m[0], m1 = _transform.m[1], m4 = _transform.m[4], m5 = _transform.m[5], m8 = _transform.m[8], m9 = _transform.m[9];
+            _transform.m[0] = cy * m0 - sx * m1;
+            _transform.m[4] = cy * m4 - sx * m5;
+            _transform.m[8] = cy * m8 - sx * m9;
+            _transform.m[1] = sy * m0 + cx * m1;
+            _transform.m[5] = sy * m4 + cx * m5;
+            _transform.m[9] = sy * m8 + cx * m9;
+        }
+        _transform = translation * _transform;
+
+        if (_scaleX != 1.f)
+        {
+            _transform.m[0] *= _scaleX;
+            _transform.m[1] *= _scaleX;
+             _transform.m[2] *= _scaleX;
+        }
+        if (_scaleY != 1.f)
+        {
+            _transform.m[4] *= _scaleY;
+            _transform.m[5] *= _scaleY;
+            _transform.m[6] *= _scaleY;
+        }
+        if (_scaleZ != 1.f)
+        {
+            _transform.m[8] *= _scaleZ;
+            _transform.m[9] *= _scaleZ;
+            _transform.m[10] *= _scaleZ;
+        }
+        
+        // FIXME:: Try to inline skew
+        // If skew is needed, apply skew and then anchor point
+        if (needsSkewMatrix)
+        {
+            float skewMatArray[16] =
+            {
+                1, (float)tanf(CC_DEGREES_TO_RADIANS(_skewY)), 0, 0,
+                (float)tanf(CC_DEGREES_TO_RADIANS(_skewX)), 1, 0, 0,
+                0,  0,  1, 0,
+                0,  0,  0, 1
+            };
+            Mat4 skewMatrix(skewMatArray);
+            
+            _transform = _transform * skewMatrix;
+        }
+
+        // adjust anchor point
+        if (!_anchorPointInPoints.isZero())
+        {
+            // FIXME:: Argh, Mat4 needs a "translate" method.
+            // FIXME:: Although this is faster than multiplying a vec4 * mat4
+            _transform.m[12] += _transform.m[0] * -_anchorPointInPoints.x + _transform.m[4] * -_anchorPointInPoints.y;
+            _transform.m[13] += _transform.m[1] * -_anchorPointInPoints.x + _transform.m[5] * -_anchorPointInPoints.y;
+            _transform.m[14] += _transform.m[2] * -_anchorPointInPoints.x + _transform.m[6] * -_anchorPointInPoints.y;
+        }
+    }
+
+    if (_additionalTransform)
+    {
+        // This is needed to support both Node::setNodeToParentTransform() and Node::setAdditionalTransform()
+        // at the same time. The scenario is this:
+        // at some point setNodeToParentTransform() is called.
+        // and later setAdditionalTransform() is called every time. And since _transform
+        // is being overwritten everyframe, _additionalTransform[1] is used to have a copy
+        // of the last "_transform without _additionalTransform"
+        if (_transformDirty)
+            _additionalTransform[1] = _transform;
+
+        if (_transformUpdated)
+            _transform = _additionalTransform[1] * _additionalTransform[0];
+    }
+
+    _transformDirty = _additionalTransformDirty = false;
+
+    return _transform;
+}
+
+void Node::setNodeToParentTransform(const Mat4& transform)
+{
+    _transform = transform;
+    _transformDirty = false;
+    _transformUpdated = true;
+
+    if (_additionalTransform)
+        // _additionalTransform[1] has a copy of lastest transform
+        _additionalTransform[1] = transform;
+}
+
+void Node::setAdditionalTransform(const AffineTransform& additionalTransform)
+{
+    Mat4 tmp;
+    CGAffineToGL(additionalTransform, tmp.m);
+    setAdditionalTransform(&tmp);
+}
+
+void Node::setAdditionalTransform(const Mat4* additionalTransform)
+{
+    if (additionalTransform == nullptr)
+    {
+        if(_additionalTransform)  _transform = _additionalTransform[1];
+        delete[] _additionalTransform;
+        _additionalTransform = nullptr;
+    }
+    else
+    {
+        if (!_additionalTransform) {
+            _additionalTransform = new Mat4[2];
+
+            // _additionalTransform[1] is used as a backup for _transform
+            _additionalTransform[1] = _transform;
+        }
+
+        _additionalTransform[0] = *additionalTransform;
+    }
+    _transformUpdated = _additionalTransformDirty = _inverseDirty = true;
+}
+
+void Node::setAdditionalTransform(const Mat4& additionalTransform)
+{
+    setAdditionalTransform(&additionalTransform);
+}
+
+AffineTransform Node::getParentToNodeAffineTransform() const
+{
+    AffineTransform ret;
+
+    GLToCGAffine(getParentToNodeTransform().m,&ret);
+    return ret;
+}
+
+const Mat4& Node::getParentToNodeTransform() const
+{
+    if ( _inverseDirty )
+    {
+        _inverse = getNodeToParentTransform().getInversed();
+        _inverseDirty = false;
+    }
+
+    return _inverse;
+}
+
+
+AffineTransform Node::getNodeToWorldAffineTransform() const
+{
+    return this->getNodeToParentAffineTransform(nullptr);
+}
+
+Mat4 Node::getNodeToWorldTransform() const
+{
+    return this->getNodeToParentTransform(nullptr);
+}
+
+AffineTransform Node::getWorldToNodeAffineTransform() const
+{
+    return AffineTransformInvert(this->getNodeToWorldAffineTransform());
+}
+
+Mat4 Node::getWorldToNodeTransform() const
+{
+    return getNodeToWorldTransform().getInversed();
+}
+
+
+Vec2 Node::convertToNodeSpace(const Vec2& worldPoint) const
+{
+    Mat4 tmp = getWorldToNodeTransform();
+    Vec3 vec3(worldPoint.x, worldPoint.y, 0);
+    Vec3 ret;
+    tmp.transformPoint(vec3,&ret);
+    return Vec2(ret.x, ret.y);
+}
+
+Vec2 Node::convertToWorldSpace(const Vec2& nodePoint) const
+{
+    Mat4 tmp = getNodeToWorldTransform();
+    Vec3 vec3(nodePoint.x, nodePoint.y, 0);
+    Vec3 ret;
+    tmp.transformPoint(vec3,&ret);
+    return Vec2(ret.x, ret.y);
+
+}
+
+Vec2 Node::convertToNodeSpaceAR(const Vec2& worldPoint) const
+{
+    Vec2 nodePoint(convertToNodeSpace(worldPoint));
+    return nodePoint - _anchorPointInPoints;
+}
+
+Vec2 Node::convertToWorldSpaceAR(const Vec2& nodePoint) const
+{
+    return convertToWorldSpace(nodePoint + _anchorPointInPoints);
+}
+
+Vec2 Node::convertToWindowSpace(const Vec2& nodePoint) const
+{
+    Vec2 worldPoint(this->convertToWorldSpace(nodePoint));
+    return _director->convertToUI(worldPoint);
+}
+
+// convenience methods which take a Touch instead of Vec2
+Vec2 Node::convertTouchToNodeSpace(Touch *touch) const
+{
+    return this->convertToNodeSpace(touch->getLocation());
+}
+
+Vec2 Node::convertTouchToNodeSpaceAR(Touch *touch) const
+{
+    Vec2 point = touch->getLocation();
+    return this->convertToNodeSpaceAR(point);
+}
+
+void Node::updateTransform()
+{
+    // Recursively iterate over children
+    for( const auto &child: _children)
+        child->updateTransform();
+}
+
+// MARK: components
+
+Component* Node::getComponent(const std::string& name)
+{
+    if (_componentContainer)
+        return _componentContainer->get(name);
+    
+    return nullptr;
+}
+
+bool Node::addComponent(Component *component)
+{
+    // lazy alloc
+    if (!_componentContainer)
+        _componentContainer = new (std::nothrow) ComponentContainer(this);
+    
+    // should enable schedule update, then all components can receive this call back
+    scheduleUpdate();
+    
+    return _componentContainer->add(component);
+}
+
+bool Node::removeComponent(const std::string& name)
+{
+    if (_componentContainer)
+        return _componentContainer->remove(name);
+    
+    return false;
+}
+
+bool Node::removeComponent(Component *component)
+{
+    if (_componentContainer)
+    {
+        return _componentContainer->remove(component);
+    }
+    
+    return false;
+}
+
+void Node::removeAllComponents()
+{
+    if (_componentContainer)
+        _componentContainer->removeAll();
+}
+
+// MARK: Opacity and Color
+
+uint8_t Node::getOpacity() const
+{
+    return _realOpacity;
+}
+
+uint8_t Node::getDisplayedOpacity() const
+{
+    return _displayedOpacity;
+}
+
+void Node::setOpacity(uint8_t opacity)
+{
+    _displayedOpacity = _realOpacity = opacity;
+    
+    updateCascadeOpacity();
+}
+
+void Node::updateDisplayedOpacity(uint8_t parentOpacity)
+{
+    _displayedOpacity = _realOpacity * parentOpacity/255.0;
+    updateColor();
+    
+    if (_cascadeOpacityEnabled)
+    {
+        for(const auto& child : _children)
+        {
+            child->updateDisplayedOpacity(_displayedOpacity);
+        }
+    }
+}
+
+bool Node::isCascadeOpacityEnabled() const
+{
+    return _cascadeOpacityEnabled;
+}
+
+void Node::setCascadeOpacityEnabled(bool cascadeOpacityEnabled)
+{
+    if (_cascadeOpacityEnabled == cascadeOpacityEnabled)
+    {
+        return;
+    }
+    
+    _cascadeOpacityEnabled = cascadeOpacityEnabled;
+    
+    if (cascadeOpacityEnabled)
+    {
+        updateCascadeOpacity();
+    }
+    else
+    {
+        disableCascadeOpacity();
+    }
+}
+
+void Node::updateCascadeOpacity()
+{
+    uint8_t parentOpacity = 255;
+    
+    if (_parent != nullptr && _parent->isCascadeOpacityEnabled())
+    {
+        parentOpacity = _parent->getDisplayedOpacity();
+    }
+    
+    updateDisplayedOpacity(parentOpacity);
+}
+
+void Node::disableCascadeOpacity()
+{
+    _displayedOpacity = _realOpacity;
+    
+    for(const auto& child : _children)
+    {
+        child->updateDisplayedOpacity(255);
+    }
+}
+
+void Node::setOpacityModifyRGB(bool /*value*/)
+{}
+
+bool Node::isOpacityModifyRGB() const
+{
+    return false;
+}
+
+const Color3B& Node::getColor() const
+{
+    return _realColor;
+}
+
+const Color3B& Node::getDisplayedColor() const
+{
+    return _displayedColor;
+}
+
+void Node::setColor(const Color3B& color)
+{
+    _displayedColor = _realColor = color;
+    
+    updateCascadeColor();
+}
+
+void Node::updateDisplayedColor(const Color3B& parentColor)
+{
+    _displayedColor.r = _realColor.r * parentColor.r/255.0;
+    _displayedColor.g = _realColor.g * parentColor.g/255.0;
+    _displayedColor.b = _realColor.b * parentColor.b/255.0;
+    updateColor();
+    
+    if (_cascadeColorEnabled)
+    {
+        for(const auto &child : _children)
+        {
+            child->updateDisplayedColor(_displayedColor);
+        }
+    }
+}
+
+bool Node::isCascadeColorEnabled() const
+{
+    return _cascadeColorEnabled;
+}
+
+void Node::setCascadeColorEnabled(bool cascadeColorEnabled)
+{
+    if (_cascadeColorEnabled == cascadeColorEnabled)
+    {
+        return;
+    }
+    
+    _cascadeColorEnabled = cascadeColorEnabled;
+    
+    if (_cascadeColorEnabled)
+    {
+        updateCascadeColor();
+    }
+    else
+    {
+        disableCascadeColor();
+    }
+}
+
+void Node::updateCascadeColor()
+{
+    Color3B parentColor = Color3B::WHITE;
+    if (_parent && _parent->isCascadeColorEnabled())
+    {
+        parentColor = _parent->getDisplayedColor();
+    }
+    
+    updateDisplayedColor(parentColor);
+}
+
+void Node::disableCascadeColor()
+{
+    for(const auto& child : _children)
+    {
+        child->updateDisplayedColor(Color3B::WHITE);
+    }
+}
+
+bool isScreenPointInRect(const Vec2 &pt, const Camera* camera, const Mat4& w2l, const Rect& rect, Vec3 *p)
+{
+    if (nullptr == camera || rect.size.width <= 0 || rect.size.height <= 0)
+    {
+        return false;
+    }
+    
+    // first, convert pt to near/far plane, get Pn and Pf
+    Vec3 Pn(pt.x, pt.y, -1), Pf(pt.x, pt.y, 1);
+    Pn = camera->unprojectGL(Pn);
+    Pf = camera->unprojectGL(Pf);
+    
+    //  then convert Pn and Pf to node space
+    w2l.transformPoint(&Pn);
+    w2l.transformPoint(&Pf);
+
+    // Pn and Pf define a line Q(t) = D + t * E which D = Pn
+    auto E = Pf - Pn;
+    
+    // second, get three points which define content plane
+    //  these points define a plane P(u, w) = A + uB + wC
+    Vec3 A = Vec3(rect.origin.x, rect.origin.y, 0);
+    Vec3 B(rect.origin.x + rect.size.width, rect.origin.y, 0);
+    Vec3 C(rect.origin.x, rect.origin.y + rect.size.height, 0);
+    B = B - A;
+    C = C - A;
+    
+    //  the line Q(t) intercept with plane P(u, w)
+    //  calculate the intercept point P = Q(t)
+    //      (BxC).A - (BxC).D
+    //  t = -----------------
+    //          (BxC).E
+    Vec3 BxC;
+    Vec3::cross(B, C, &BxC);
+    auto BxCdotE = BxC.dot(E);
+    if (BxCdotE == 0) {
+        return false;
+    }
+    auto t = (BxC.dot(A) - BxC.dot(Pn)) / BxCdotE;
+    Vec3 P = Pn + t * E;
+    if (p) {
+        *p = P;
+    }
+    return rect.containsPoint(Vec2(P.x, P.y));
+}
+
+// MARK: Camera
+void Node::setCameraMask(unsigned short mask, bool applyChildren)
+{
+    _cameraMask = mask;
+    if (applyChildren)
+    {
+        for (const auto& child : _children)
+        {
+            child->setCameraMask(mask, applyChildren);
+        }
+    }
+}
+
+int Node::getAttachedNodeCount()
+{
+    return __attachedNodeCount;
+}
+
+void Node::setProgramState(backend::ProgramState* programState)
+{
+    if (_programState != programState)
+    {
+        CC_SAFE_RELEASE(_programState);
+        _programState = programState;
+        CC_SAFE_RETAIN(programState);
+    }
+}
+
+backend::ProgramState* Node::getProgramState() const
+{
+    return _programState;
+}
+
+NS_CC_END

+ 1978 - 0
3rdparty/cocos2dx/include/cocos/2d/CCNode.h

@@ -0,0 +1,1978 @@
+/****************************************************************************
+ Copyright (c) 2008-2010 Ricardo Quesada
+ Copyright (c) 2009      Valentin Milea
+ Copyright (c) 2010-2012 cocos2d-x.org
+ Copyright (c) 2011      Zynga Inc.
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+
+#ifndef __CCNODE_H__
+#define __CCNODE_H__
+
+#include <cstdint>
+#include "base/ccMacros.h"
+#include "base/CCVector.h"
+#include "base/CCProtocols.h"
+#include "base/CCScriptSupport.h"
+#include "math/CCAffineTransform.h"
+#include "math/CCMath.h"
+#include "2d/CCComponentContainer.h"
+#include "2d/CCComponent.h"
+
+#if CC_USE_PHYSICS
+#include "physics/CCPhysicsBody.h"
+#endif
+
+NS_CC_BEGIN
+
+class GridBase;
+class Touch;
+class Action;
+class LabelProtocol;
+class Scheduler;
+class ActionManager;
+class Component;
+class ComponentContainer;
+class EventDispatcher;
+class Scene;
+class Renderer;
+class Director;
+class Material;
+class Camera;
+class PhysicsBody;
+
+namespace backend{
+    class ProgramState;
+}
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+enum {
+    kNodeOnEnter,
+    kNodeOnExit,
+    kNodeOnEnterTransitionDidFinish,
+    kNodeOnExitTransitionDidStart,
+    kNodeOnCleanup
+};
+
+class EventListener;
+
+/** @class Node
+* @brief Node is the base element of the Scene Graph. Elements of the Scene Graph must be Node objects or subclasses of it.
+ The most common Node objects are: Scene, Layer, Sprite, Menu, Label.
+
+ The main features of a Node are:
+ - They can contain other Node objects (`addChild`, `getChildByTag`, `removeChild`, etc)
+ - They can schedule periodic callback (`schedule`, `unschedule`, etc)
+ - They can execute actions (`runAction`, `stopAction`, etc)
+
+ Subclassing a Node usually means (one/all) of:
+ - overriding init to initialize resources and schedule callbacks
+ - create callbacks to handle the advancement of time
+ - overriding `draw` to render the node
+
+ Properties of Node:
+ - position (default: x=0, y=0)
+ - scale (default: x=1, y=1)
+ - rotation (in degrees, clockwise) (default: 0)
+ - anchor point (default: x=0, y=0)
+ - contentSize (default: width=0, height=0)
+ - visible (default: true)
+
+ Limitations:
+ - A Node is a "void" object. If you want to draw something on the screen, you should use a Sprite instead. Or subclass Node and override `draw`.
+
+ */
+
+class CC_DLL Node : public Ref
+{
+public:
+    /** Default tag used for all the nodes */
+    static const int INVALID_TAG = -1;
+
+    enum {
+        FLAGS_TRANSFORM_DIRTY = (1 << 0),
+        FLAGS_CONTENT_SIZE_DIRTY = (1 << 1),
+        FLAGS_RENDER_AS_3D = (1 << 3),
+
+        FLAGS_DIRTY_MASK = (FLAGS_TRANSFORM_DIRTY | FLAGS_CONTENT_SIZE_DIRTY),
+    };
+    /// @{
+    /// @name Constructor, Destructor and Initializers
+
+    /**
+     * Allocates and initializes a node.
+     * @return A initialized node which is marked as "autorelease".
+     */
+    static Node * create();
+
+    /**
+     * Gets count of nodes those are attached to scene graph.
+     */
+    static int getAttachedNodeCount();
+public:
+    
+    /**
+     * Gets the description string. It makes debugging easier.
+     * @return A string
+     * @js NA
+     * @lua NA
+     */
+    virtual std::string getDescription() const;
+
+    /// @} end of initializers
+
+
+
+    /// @{
+    /// @name Setters & Getters for Graphic Properties
+
+    /**
+     LocalZOrder is the 'key' used to sort the node relative to its siblings.
+
+     The Node's parent will sort all its children based on the LocalZOrder value.
+     If two nodes have the same LocalZOrder, then the node that was added first to the children's array will be in front of the other node in the array.
+     
+     Also, the Scene Graph is traversed using the "In-Order" tree traversal algorithm ( http://en.wikipedia.org/wiki/Tree_traversal#In-order )
+     And Nodes that have LocalZOrder values < 0 are the "left" subtree
+     While Nodes with LocalZOrder >=0 are the "right" subtree.
+     
+     @see `setGlobalZOrder`
+     @see `setVertexZ`
+     *
+     * @param localZOrder The local Z order value.
+     */
+    virtual void setLocalZOrder(std::int32_t localZOrder);
+    
+    /* 
+     Helper function used by `setLocalZOrder`. Don't use it unless you know what you are doing.
+     @js NA
+     */
+    virtual void _setLocalZOrder(std::int32_t z);
+
+    /** !!! ONLY FOR INTERNAL USE
+    * Sets the arrival order when this node has a same ZOrder with other children.
+    *
+    * A node which called addChild subsequently will take a larger arrival order,
+    * If two children have the same Z order, the child with larger arrival order will be drawn later.
+    *
+    * @warning This method is used internally for localZOrder sorting, don't change this manually
+    *
+    * @param orderOfArrival   The arrival order.
+    */
+    void updateOrderOfArrival();
+
+    /**
+     * Gets the local Z order of this node.
+     *
+     * @see `setLocalZOrder(int)`
+     *
+     * @return The local (relative to its siblings) Z order.
+     */
+
+    virtual std::int32_t getLocalZOrder() const { return _localZOrder; }
+
+    /**
+     Defines the order in which the nodes are renderer.
+     Nodes that have a Global Z Order lower, are renderer first.
+     
+     In case two or more nodes have the same Global Z Order, the order is not guaranteed.
+     The only exception if the Nodes have a Global Z Order == 0. In that case, the Scene Graph order is used.
+     
+     By default, all nodes have a Global Z Order = 0. That means that by default, the Scene Graph order is used to render the nodes.
+     
+     Global Z Order is useful when you need to render nodes in an order different than the Scene Graph order.
+     
+     Limitations: Global Z Order can't be used by Nodes that have SpriteBatchNode as one of their ancestors.
+     And if ClippingNode is one of the ancestors, then "global Z order" will be relative to the ClippingNode.
+
+     @see `setLocalZOrder()`
+     @see `setVertexZ()`
+
+     @since v3.0
+     *
+     * @param globalZOrder The global Z order value.
+     */
+    virtual void setGlobalZOrder(float globalZOrder);
+    /**
+     * Returns the Node's Global Z Order.
+     *
+     * @see `setGlobalZOrder(int)`
+     *
+     * @return The node's global Z order
+     */
+    virtual float getGlobalZOrder() const { return _globalZOrder; }
+
+    /**
+     * Sets the scale (x) of the node.
+     *
+     * It is a scaling factor that multiplies the width of the node and its children.
+     *
+     * @param scaleX   The scale factor on X axis.
+     *
+     * @warning The physics body doesn't support this.
+     */
+    virtual void setScaleX(float scaleX);
+    /**
+     * Returns the scale factor on X axis of this node
+     *
+     * @see setScaleX(float)
+     *
+     * @return The scale factor on X axis.
+     */
+    virtual float getScaleX() const;
+
+
+    /**
+     * Sets the scale (y) of the node.
+     *
+     * It is a scaling factor that multiplies the height of the node and its children.
+     *
+     * @param scaleY   The scale factor on Y axis.
+     *
+     * @warning The physics body doesn't support this.
+     */
+    virtual void setScaleY(float scaleY);
+    /**
+     * Returns the scale factor on Y axis of this node
+     *
+     * @see `setScaleY(float)`
+     *
+     * @return The scale factor on Y axis.
+     */
+    virtual float getScaleY() const;
+
+    /**
+     * Changes the scale factor on Z axis of this node
+     *
+     * The Default value is 1.0 if you haven't changed it before.
+     *
+     * @param scaleZ   The scale factor on Z axis.
+     *
+     * @warning The physics body doesn't support this.
+     */
+    virtual void setScaleZ(float scaleZ);
+    /**
+     * Returns the scale factor on Z axis of this node
+     *
+     * @see `setScaleZ(float)`
+     *
+     * @return The scale factor on Z axis.
+     */
+    virtual float getScaleZ() const;
+
+
+    /**
+     * Sets the scale (x,y,z) of the node.
+     *
+     * It is a scaling factor that multiplies the width, height and depth of the node and its children.
+     *
+     * @param scale     The scale factor for both X and Y axis.
+     *
+     * @warning The physics body doesn't support this.
+     */
+    virtual void setScale(float scale);
+    /**
+     * Gets the scale factor of the node,  when X and Y have the same scale factor.
+     *
+     * @warning Assert when `_scaleX != _scaleY`
+     * @see setScale(float)
+     *
+     * @return The scale factor of the node.
+     */
+    virtual float getScale() const;
+
+     /**
+     * Sets the scale (x,y) of the node.
+     *
+     * It is a scaling factor that multiplies the width and height of the node and its children.
+     *
+     * @param scaleX     The scale factor on X axis.
+     * @param scaleY     The scale factor on Y axis.
+     *
+     * @warning The physics body doesn't support this.
+     */
+    virtual void setScale(float scaleX, float scaleY);
+
+    /**
+     * Sets the position (x,y) of the node in its parent's coordinate system.
+     *
+     * Usually we use `Vec2(x,y)` to compose Vec2 object.
+     * This code snippet sets the node in the center of screen.
+     @code
+     Size size = Director::getInstance()->getWinSize();
+     node->setPosition(size.width/2, size.height/2);
+     @endcode
+     *
+     * @param position  The position (x,y) of the node in OpenGL coordinates.
+     */
+    virtual void setPosition(const Vec2 &position);
+
+    /** Sets the position (x,y) using values between 0 and 1.
+     The positions in pixels is calculated like the following:
+     @code
+     // pseudo code
+     void setNormalizedPosition(Vec2 pos) {
+       Size s = getParent()->getContentSize();
+       _position = pos * s;
+     }
+     @endcode
+     *
+     * @param position The normalized position (x,y) of the node, using value between 0 and 1.
+     */
+    virtual void setPositionNormalized(const Vec2 &position);
+    // FIXME: should get deprecated in v4.0
+    virtual void setNormalizedPosition(const Vec2 &position) { setPositionNormalized(position); }
+
+    /**
+     * Gets the position (x,y) of the node in its parent's coordinate system.
+     *
+     * @see setPosition(const Vec2&)
+     *
+     * @return The position (x,y) of the node in OpenGL coordinates.
+     * @code
+     * In js and lua return value is table which contains x,y.
+     * @endcode
+     */
+    virtual const Vec2& getPosition() const;
+
+    /** Returns the normalized position.
+     * 
+     * @return The normalized position.
+     */
+    virtual const Vec2& getPositionNormalized() const;
+    // FIXME: should get deprecated in v4.0
+    virtual const Vec2& getNormalizedPosition() const { return getPositionNormalized(); }
+
+    /**
+     * Sets the position (x,y) of the node in its parent's coordinate system.
+     *
+     * Passing two numbers (x,y) is much efficient than passing Vec2 object.
+     * This method is bound to Lua and JavaScript.
+     * Passing a number is 10 times faster than passing a object from Lua to c++.
+     *
+     @code
+     // sample code in Lua
+     local pos  = node::getPosition()  -- returns Vec2 object from C++.
+     node:setPosition(x, y)            -- pass x, y coordinate to C++.
+     @endcode
+     *
+     * @param x     X coordinate for position.
+     * @param y     Y coordinate for position.
+     */
+    virtual void setPosition(float x, float y);
+    /**
+     * Gets position in a more efficient way, returns two number instead of a Vec2 object.
+     *
+     * @see `setPosition(float, float)`
+     * In js,out value not return.
+     *
+     * @param x To receive x coordinate for position.
+     * @param y To receive y coordinate for position.
+     */
+    virtual void getPosition(float* x, float* y) const;
+    /**
+     * Gets/Sets x or y coordinate individually for position.
+     * These methods are used in Lua and Javascript Bindings
+     */
+    /** Sets the x coordinate of the node in its parent's coordinate system.
+     *
+     * @param x The x coordinate of the node.
+     */
+    virtual void  setPositionX(float x);
+    /** Gets the x coordinate of the node in its parent's coordinate system.
+     *
+     * @return The x coordinate of the node.
+     */
+    virtual float getPositionX() const;
+    /** Sets the y coordinate of the node in its parent's coordinate system.
+     *
+     * @param y The y coordinate of the node.
+     */
+    virtual void  setPositionY(float y);
+    /** Gets the y coordinate of the node in its parent's coordinate system.
+     *
+     * @return The y coordinate of the node.
+     */
+    virtual float getPositionY() const;
+
+    /**
+     * Sets the position (X, Y, and Z) in its parent's coordinate system.
+     * 
+     * @param position The position (X, Y, and Z) in its parent's coordinate system.
+     * @js NA
+     */
+    virtual void setPosition3D(const Vec3& position);
+    /**
+     * Returns the position (X,Y,Z) in its parent's coordinate system.
+     *
+     * @return The position (X, Y, and Z) in its parent's coordinate system.
+     * @js NA
+     */
+    virtual Vec3 getPosition3D() const;
+
+    /**
+     * Sets the 'z' coordinate in the position. It is the OpenGL Z vertex value.
+     *
+     * The OpenGL depth buffer and depth testing are disabled by default. You need to turn them on.
+     * In order to use this property correctly.
+     *
+     * `setPositionZ()` also sets the `setGlobalZValue()` with the positionZ as value.
+     *
+     * @see `setGlobalZValue()`
+     *
+     * @param positionZ  OpenGL Z vertex of this node.
+     * @js setVertexZ
+     */
+    virtual void setPositionZ(float positionZ);
+
+    /**
+     * Gets position Z coordinate of this node.
+     *
+     * @see setPositionZ(float)
+     *
+     * @return The position Z coordinate of this node.
+     * @js getVertexZ
+     */
+    virtual float getPositionZ() const;
+
+    /**
+     * Changes the X skew angle of the node in degrees.
+     *
+     * The difference between `setRotationalSkew()` and `setSkew()` is that the first one simulate Flash's skew functionality
+     * while the second one uses the real skew function.
+     *
+     * This angle describes the shear distortion in the X direction.
+     * Thus, it is the angle between the Y coordinate and the left edge of the shape
+     * The default skewX angle is 0. Positive values distort the node in a CW direction.
+     *
+     * @param skewX The X skew angle of the node in degrees.
+     *
+     * @warning The physics body doesn't support this.
+     */
+    virtual void setSkewX(float skewX);
+    /**
+     * Returns the X skew angle of the node in degrees.
+     *
+     * @see `setSkewX(float)`
+     *
+     * @return The X skew angle of the node in degrees.
+     */
+    virtual float getSkewX() const;
+
+
+    /**
+     * Changes the Y skew angle of the node in degrees.
+     *
+     * The difference between `setRotationalSkew()` and `setSkew()` is that the first one simulate Flash's skew functionality
+     * while the second one uses the real skew function.
+     *
+     * This angle describes the shear distortion in the Y direction.
+     * Thus, it is the angle between the X coordinate and the bottom edge of the shape.
+     * The default skewY angle is 0. Positive values distort the node in a CCW direction.
+     *
+     * @param skewY    The Y skew angle of the node in degrees.
+     *
+     * @warning The physics body doesn't support this.
+     */
+    virtual void setSkewY(float skewY);
+    /**
+     * Returns the Y skew angle of the node in degrees.
+     *
+     * @see `setSkewY(float)`
+     *
+     * @return The Y skew angle of the node in degrees.
+     */
+    virtual float getSkewY() const;
+
+
+    /**
+     * Sets the anchor point in percent.
+     *
+     * anchorPoint is the point around which all transformations and positioning manipulations take place.
+     * It's like a pin in the node where it is "attached" to its parent.
+     * The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner.
+     * But you can use values higher than (1,1) and lower than (0,0) too.
+     * The default anchorPoint is (0,0), so it starts in the lower left corner of the node.
+     * @note If node has a physics body, the anchor must be in the middle, you can't change this to other value.
+     *
+     * @param anchorPoint   The anchor point of node.
+     */
+    virtual void setAnchorPoint(const Vec2& anchorPoint);
+    /**
+     * Returns the anchor point in percent.
+     *
+     * @see `setAnchorPoint(const Vec2&)`
+     *
+     * @return The anchor point of node.
+     */
+    virtual const Vec2& getAnchorPoint() const;
+    /**
+     * Returns the anchorPoint in absolute pixels.
+     *
+     * @warning You can only read it. If you wish to modify it, use anchorPoint instead.
+     * @see `getAnchorPoint()`
+     *
+     * @return The anchor point in absolute pixels.
+     */
+    virtual const Vec2& getAnchorPointInPoints() const;
+
+
+    /**
+     * Sets the untransformed size of the node.
+     *
+     * The contentSize remains the same no matter the node is scaled or rotated.
+     * All nodes has a size. Layer and Scene has the same size of the screen.
+     *
+     * @param contentSize   The untransformed size of the node.
+     */
+    virtual void setContentSize(const Size& contentSize);
+    /**
+     * Returns the untransformed size of the node.
+     *
+     * @see `setContentSize(const Size&)`
+     *
+     * @return The untransformed size of the node.
+     */
+    virtual const Size& getContentSize() const;
+
+
+    /**
+     * Sets whether the node is visible.
+     *
+     * The default value is true, a node is default to visible.
+     *
+     * @param visible   true if the node is visible, false if the node is hidden.
+     */
+    virtual void setVisible(bool visible);
+    /**
+     * Determines if the node is visible.
+     *
+     * @see `setVisible(bool)`
+     *
+     * @return true if the node is visible, false if the node is hidden.
+     */
+    virtual bool isVisible() const;
+
+
+    /**
+     * Sets the rotation (angle) of the node in degrees.
+     *
+     * 0 is the default rotation angle.
+     * Positive values rotate node clockwise, and negative values for anti-clockwise.
+     *
+     * @param rotation     The rotation of the node in degrees.
+     */
+    virtual void setRotation(float rotation);
+    /**
+     * Returns the rotation of the node in degrees.
+     *
+     * @see `setRotation(float)`
+     *
+     * @return The rotation of the node in degrees.
+     */
+    virtual float getRotation() const;
+
+    /**
+     * Sets the rotation (X,Y,Z) in degrees.
+     * Useful for 3d rotations.
+     *
+     * @warning The physics body doesn't support this.
+     *
+     * @param rotation The rotation of the node in 3d.
+     * @js NA
+     */
+    virtual void setRotation3D(const Vec3& rotation);
+    /**
+     * Returns the rotation (X,Y,Z) in degrees.
+     * 
+     * @return The rotation of the node in 3d.
+     * @js NA
+     */
+    virtual Vec3 getRotation3D() const;
+    
+    /**
+     * Set rotation by quaternion. You should make sure the quaternion is normalized.
+     *
+     * @param quat The rotation in quaternion, note that the quat must be normalized.
+     * @js NA
+     */
+    virtual void setRotationQuat(const Quaternion& quat);
+    
+    /**
+     * Return the rotation by quaternion, Note that when _rotationZ_X == _rotationZ_Y, the returned quaternion equals to RotationZ_X * RotationY * RotationX,
+     * it equals to RotationY * RotationX otherwise.
+     *
+     * @return The rotation in quaternion.
+     * @js NA
+     */
+    virtual Quaternion getRotationQuat() const;
+
+    /**
+     * Sets the X rotation (angle) of the node in degrees which performs a horizontal rotational skew.
+     *
+     * The difference between `setRotationalSkew()` and `setSkew()` is that the first one simulate Flash's skew functionality,
+     * while the second one uses the real skew function.
+     *
+     * 0 is the default rotation angle.
+     * Positive values rotate node clockwise, and negative values for anti-clockwise.
+     *
+     * @param rotationX    The X rotation in degrees which performs a horizontal rotational skew.
+     *
+     * @warning The physics body doesn't support this.
+     * @js setRotationX
+     */
+    virtual void setRotationSkewX(float rotationX);
+
+    /**
+     * Gets the X rotation (angle) of the node in degrees which performs a horizontal rotation skew.
+     *
+     * @see `setRotationSkewX(float)`
+     *
+     * @return The X rotation in degrees.
+     * @js getRotationX 
+     */
+    virtual float getRotationSkewX() const;
+
+    /**
+     * Sets the Y rotation (angle) of the node in degrees which performs a vertical rotational skew.
+     *
+     * The difference between `setRotationalSkew()` and `setSkew()` is that the first one simulate Flash's skew functionality,
+     * while the second one uses the real skew function.
+     *
+     * 0 is the default rotation angle.
+     * Positive values rotate node clockwise, and negative values for anti-clockwise.
+     *
+     * @param rotationY    The Y rotation in degrees.
+     *
+     * @warning The physics body doesn't support this.
+     * @js setRotationY
+     */
+    virtual void setRotationSkewY(float rotationY);
+
+    /**
+     * Gets the Y rotation (angle) of the node in degrees which performs a vertical rotational skew.
+     *
+     * @see `setRotationSkewY(float)`
+     *
+     * @return The Y rotation in degrees.
+     * @js getRotationY
+     */
+    virtual float getRotationSkewY() const;
+
+    /**
+     * Sets whether the anchor point will be (0,0) when you position this node.
+     *
+     * This is an internal method, only used by Layer and Scene. Don't call it outside framework.
+     * The default value is false, while in Layer and Scene are true.
+     *
+     * @param ignore    true if anchor point will be (0,0) when you position this node.
+     */
+    virtual void setIgnoreAnchorPointForPosition(bool ignore);
+    
+    /**
+     * Gets whether the anchor point will be (0,0) when you position this node.
+     *
+     * @see `setIgnoreAnchorPointForPosition(bool)`
+     *
+     * @return true if the anchor point will be (0,0) when you position this node.
+     */
+    virtual bool isIgnoreAnchorPointForPosition() const;
+
+    /// @}  end of Setters & Getters for Graphic Properties
+
+
+    /// @{
+    /// @name Children and Parent
+
+    /**
+     * Adds a child to the container with z-order as 0.
+     *
+     * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+     *
+     * @param child A child node.
+     */
+    virtual void addChild(Node * child);
+    /**
+     * Adds a child to the container with a local z-order.
+     *
+     * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+     *
+     * @param child     A child node.
+     * @param localZOrder    Z order for drawing priority. Please refer to `setLocalZOrder(int)`.
+     */
+    virtual void addChild(Node * child, int localZOrder);
+    /**
+     * Adds a child to the container with z order and tag.
+     *
+     * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+     *
+     * @param child         A child node.
+     * @param localZOrder   Z order for drawing priority. Please refer to `setLocalZOrder(int)`.
+     * @param tag           An integer to identify the node easily. Please refer to `setTag(int)`.
+     * 
+     * Please use `addChild(Node* child, int localZOrder, const std::string &name)` instead.
+     */
+     virtual void addChild(Node* child, int localZOrder, int tag);
+    /**
+     * Adds a child to the container with z order and tag
+     *
+     * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+     *
+     * @param child     A child node.
+     * @param localZOrder    Z order for drawing priority. Please refer to `setLocalZOrder(int)`.
+     * @param name      A string to identify the node easily. Please refer to `setName(int)`.
+     *
+     */
+    virtual void addChild(Node* child, int localZOrder, const std::string &name);
+    /**
+     * Gets a child from the container with its tag.
+     *
+     * @param tag   An identifier to find the child node.
+     *
+     * @return a Node object whose tag equals to the input parameter.
+     *
+     * Please use `getChildByName()` instead.
+     */
+     virtual Node * getChildByTag(int tag) const;
+    
+     /**
+     * Gets a child from the container with its tag that can be cast to Type T.
+     *
+     * @param tag   An identifier to find the child node.
+     *
+     * @return a Node with the given tag that can be cast to Type T.
+    */
+    template <typename T>
+    T getChildByTag(int tag) const { return static_cast<T>(getChildByTag(tag)); }
+    
+    /**
+     * Gets a child from the container with its name.
+     *
+     * @param name   An identifier to find the child node.
+     *
+     * @return a Node object whose name equals to the input parameter.
+     *
+     * @since v3.2
+     */
+    virtual Node* getChildByName(const std::string& name) const;
+    /**
+     * Gets a child from the container with its name that can be cast to Type T.
+     *
+     * @param name   An identifier to find the child node.
+     *
+     * @return a Node with the given name that can be cast to Type T.
+    */
+    template <typename T>
+    T getChildByName(const std::string& name) const { return static_cast<T>(getChildByName(name)); }
+    /** Search the children of the receiving node to perform processing for nodes which share a name.
+     *
+     * @param name The name to search for, supports c++11 regular expression.
+     * Search syntax options:
+     * `//`: Can only be placed at the begin of the search string. This indicates that it will search recursively.
+     * `/..`: The search should move up to the node's parent. Can only be placed at the end of string.
+     * `/` : When placed anywhere but the start of the search string, this indicates that the search should move to the node's children.
+     *
+     * @code
+     * enumerateChildren("//MyName", ...): This searches the children recursively and matches any node with the name `MyName`.
+     * enumerateChildren("[[:alnum:]]+", ...): This search string matches every node of its children.
+     * enumerateChildren("A[[:digit:]]", ...): This searches the node's children and returns any child named `A0`, `A1`, ..., `A9`.
+     * enumerateChildren("Abby/Normal", ...): This searches the node's grandchildren and returns any node whose name is `Normal`
+     * and whose parent is named `Abby`.
+     * enumerateChildren("//Abby/Normal", ...): This searches recursively and returns any node whose name is `Normal` and whose
+     * parent is named `Abby`.
+     * @endcode
+     *
+     * @warning Only support alpha or number for name, and not support unicode.
+     *
+     * @param callback A callback function to execute on nodes that match the `name` parameter. The function takes the following arguments:
+     *  `node` 
+     *      A node that matches the name
+     *  And returns a boolean result. Your callback can return `true` to terminate the enumeration.
+     *
+     * @since v3.2
+     */
+    virtual void enumerateChildren(const std::string &name, std::function<bool(Node* node)> callback) const;
+    /**
+     * Returns the array of the node's children.
+     *
+     * @return the array the node's children.
+     */
+    virtual Vector<Node*>& getChildren() { return _children; }
+    virtual const Vector<Node*>& getChildren() const { return _children; }
+    
+    /** 
+     * Returns the amount of children.
+     *
+     * @return The amount of children.
+     */
+    virtual ssize_t getChildrenCount() const;
+
+    /**
+     * Sets the parent node.
+     *
+     * @param parent    A pointer to the parent node.
+     */
+    virtual void setParent(Node* parent);
+    /**
+     * Returns a pointer to the parent node.
+     *
+     * @see `setParent(Node*)`
+     *
+     * @returns A pointer to the parent node.
+     */
+    virtual Node* getParent() { return _parent; }
+    virtual const Node* getParent() const { return _parent; }
+
+
+    ////// REMOVES //////
+
+    /**
+     * Removes this node itself from its parent node with a cleanup.
+     * If the node orphan, then nothing happens.
+     * @see `removeFromParentAndCleanup(bool)`
+     */
+    virtual void removeFromParent();
+    /**
+     * Removes this node itself from its parent node.
+     * If the node orphan, then nothing happens.
+     * @param cleanup   true if all actions and callbacks on this node should be removed, false otherwise.
+     * @js removeFromParent
+     * @lua removeFromParent
+     */
+    virtual void removeFromParentAndCleanup(bool cleanup);
+
+    /**
+     * Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter.
+     *
+     * @param child     The child node which will be removed.
+     * @param cleanup   True if all running actions and callbacks on the child node will be cleanup, false otherwise.
+     */
+    virtual void removeChild(Node* child, bool cleanup = true);
+
+    /**
+     * Removes a child from the container by tag value. It will also cleanup all running actions depending on the cleanup parameter.
+     *
+     * @param tag       An integer number that identifies a child node.
+     * @param cleanup   True if all running actions and callbacks on the child node will be cleanup, false otherwise.
+     *
+     * Please use `removeChildByName` instead.
+     */
+     virtual void removeChildByTag(int tag, bool cleanup = true);
+    /**
+     * Removes a child from the container by tag value. It will also cleanup all running actions depending on the cleanup parameter.
+     *
+     * @param name       A string that identifies a child node.
+     * @param cleanup   True if all running actions and callbacks on the child node will be cleanup, false otherwise.
+     */
+    virtual void removeChildByName(const std::string &name, bool cleanup = true);
+    /**
+     * Removes all children from the container with a cleanup.
+     *
+     * @see `removeAllChildrenWithCleanup(bool)`
+     */
+    virtual void removeAllChildren();
+    /**
+     * Removes all children from the container, and do a cleanup to all running actions depending on the cleanup parameter.
+     *
+     * @param cleanup   True if all running actions on all children nodes should be cleanup, false otherwise.
+     * @js removeAllChildren
+     * @lua removeAllChildren
+     */
+    virtual void removeAllChildrenWithCleanup(bool cleanup);
+
+    /**
+     * Reorders a child according to a new z value.
+     *
+     * @param child     An already added child node. It MUST be already added.
+     * @param localZOrder Z order for drawing priority. Please refer to setLocalZOrder(int).
+     */
+    virtual void reorderChild(Node * child, int localZOrder);
+
+    /**
+     * Sorts the children array once before drawing, instead of every time when a child is added or reordered.
+     * This approach can improve the performance massively.
+     * @note Don't call this manually unless a child added needs to be removed in the same frame.
+     */
+    virtual void sortAllChildren();
+
+    /**
+    * Sorts helper function
+    *
+    */
+    template<typename _T> inline
+    static void sortNodes(cocos2d::Vector<_T*>& nodes)
+    {
+        static_assert(std::is_base_of<Node, _T>::value, "Node::sortNodes: Only accept derived of Node!");
+#if CC_64BITS
+        std::sort(std::begin(nodes), std::end(nodes), [](_T* n1, _T* n2) {
+            return (n1->_localZOrder$Arrival < n2->_localZOrder$Arrival);
+        });
+#else
+        std::sort(std::begin(nodes), std::end(nodes), [](_T* n1, _T* n2) {
+            return (n1->_localZOrder == n2->_localZOrder && n1->_orderOfArrival < n2->_orderOfArrival) || n1->_localZOrder < n2->_localZOrder;
+        });
+#endif
+    }
+
+    /// @} end of Children and Parent
+    
+    /// @{
+    /// @name Tag & User data
+
+    /**
+     * Returns a tag that is used to identify the node easily.
+     *
+     * @return An integer that identifies the node.
+     *
+     * Please use `getTag()` instead.
+     */
+     virtual int getTag() const;
+    /**
+     * Changes the tag that is used to identify the node easily.
+     *
+     * Please refer to getTag for the sample code.
+     *
+     * @param tag   A integer that identifies the node.
+     *
+     * Please use `setName()` instead.
+     */
+     virtual void setTag(int tag);
+    
+    /** Returns a string that is used to identify the node.
+     * @return A string that identifies the node.
+     * 
+     * @since v3.2
+     */
+    virtual const std::string& getName() const;
+    /** Changes the name that is used to identify the node easily.
+     * @param name A string that identifies the node.
+     *
+     * @since v3.2
+     */
+    virtual void setName(const std::string& name);
+
+    
+    /**
+     * Returns a custom user data pointer.
+     *
+     * You can set everything in UserData pointer, a data block, a structure or an object.
+     *
+     * @return A custom user data pointer.
+     * @lua NA
+     */
+    virtual void* getUserData() { return _userData; }
+    /**
+    * @lua NA
+    */
+    virtual const void* getUserData() const { return _userData; }
+
+    /**
+     * Sets a custom user data pointer.
+     *
+     * You can set everything in UserData pointer, a data block, a structure or an object, etc.
+     * @warning Don't forget to release the memory manually,
+     *          especially before you change this data pointer, and before this node is autoreleased.
+     *
+     * @param userData  A custom user data pointer.
+     * @lua NA
+     */
+    virtual void setUserData(void *userData);
+
+    /**
+     * Returns a user assigned Object.
+     *
+     * Similar to userData, but instead of holding a void* it holds an object.
+     *
+     * @return A user assigned Object.
+     * @lua NA
+     */
+    virtual Ref* getUserObject() { return _userObject; }
+    /**
+    * @lua NA
+    */
+    virtual const Ref* getUserObject() const { return _userObject; }
+
+    /**
+     * Returns a user assigned Object.
+     *
+     * Similar to UserData, but instead of holding a void* it holds an object.
+     * The UserObject will be retained once in this method,
+     * and the previous UserObject (if existed) will be released.
+     * The UserObject will be released in Node's destructor.
+     *
+     * @param userObject    A user assigned Object.
+     */
+    virtual void setUserObject(Ref *userObject);
+
+    /// @} end of Tag & User Data
+
+    /**
+     * Returns whether or not the node is "running".
+     *
+     * If the node is running it will accept event callbacks like onEnter(), onExit(), update().
+     *
+     * @return Whether or not the node is running.
+     */
+    virtual bool isRunning() const;
+
+    /**
+     * Schedules for lua script.
+     * @js NA
+     *
+     * @param handler The key to search lua function.
+     * @param priority A given priority value.
+     */
+    void scheduleUpdateWithPriorityLua(int handler, int priority);
+
+    /// @}  end Script Bindings
+
+
+    /// @{
+    /// @name Event Callbacks
+
+    /**
+     * Event callback that is invoked every time when Node enters the 'stage'.
+     * If the Node enters the 'stage' with a transition, this event is called when the transition starts.
+     * During onEnter you can't access a "sister/brother" node.
+     * If you override onEnter, you shall call its parent's one, e.g., Node::onEnter().
+     * @lua NA
+     */
+    virtual void onEnter();
+
+    /** Event callback that is invoked when the Node enters in the 'stage'.
+     * If the Node enters the 'stage' with a transition, this event is called when the transition finishes.
+     * If you override onEnterTransitionDidFinish, you shall call its parent's one, e.g. Node::onEnterTransitionDidFinish()
+     * @lua NA
+     */
+    virtual void onEnterTransitionDidFinish();
+
+    /**
+     * Event callback that is invoked every time the Node leaves the 'stage'.
+     * If the Node leaves the 'stage' with a transition, this event is called when the transition finishes.
+     * During onExit you can't access a sibling node.
+     * If you override onExit, you shall call its parent's one, e.g., Node::onExit().
+     * @lua NA
+     */
+    virtual void onExit();
+
+    /**
+     * Event callback that is called every time the Node leaves the 'stage'.
+     * If the Node leaves the 'stage' with a transition, this callback is called when the transition starts.
+     * @lua NA
+     */
+    virtual void onExitTransitionDidStart();
+
+    /// @} end of event callbacks.
+
+
+    /**
+     * Stops all running actions and schedulers
+     */
+    virtual void cleanup();
+
+    /**
+     * Override this method to draw your own node.
+     * The following GL states will be enabled by default:
+     * - `glEnableClientState(GL_VERTEX_ARRAY);`
+     * - `glEnableClientState(GL_COLOR_ARRAY);`
+     * - `glEnableClientState(GL_TEXTURE_COORD_ARRAY);`
+     * - `glEnable(GL_TEXTURE_2D);`
+     * AND YOU SHOULD NOT DISABLE THEM AFTER DRAWING YOUR NODE
+     * But if you enable any other GL state, you should disable it after drawing your node.
+     * 
+     * @param renderer A given renderer.
+     * @param transform A transform matrix.
+     * @param flags Renderer flag.
+     */
+    virtual void draw(Renderer *renderer, const Mat4& transform, uint32_t flags);
+    virtual void draw() final;
+
+    /**
+     * Visits this node's children and draw them recursively.
+     *
+     * @param renderer A given renderer.
+     * @param parentTransform A transform matrix.
+     * @param parentFlags Renderer flag.
+     */
+    virtual void visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags);
+    virtual void visit() final;
+
+
+    /** Returns the Scene that contains the Node.
+     It returns `nullptr` if the node doesn't belong to any Scene.
+     This function recursively calls parent->getScene() until parent is a Scene object. The results are not cached. It is that the user caches the results in case this functions is being used inside a loop.
+     *
+     * @return The Scene that contains the node.
+     */
+    virtual Scene* getScene() const;
+
+    /**
+     * Returns an AABB (axis-aligned bounding-box) in its parent's coordinate system.
+     *
+     * @return An AABB (axis-aligned bounding-box) in its parent's coordinate system
+     */
+    virtual Rect getBoundingBox() const;
+
+    /** Set event dispatcher for scene.
+     *
+     * @param dispatcher The event dispatcher of scene.
+     */
+    virtual void setEventDispatcher(EventDispatcher* dispatcher);
+    /** Get the event dispatcher of scene.
+     *
+     * @return The event dispatcher of scene.
+     */
+    virtual EventDispatcher* getEventDispatcher() const { return _eventDispatcher; };
+
+    /// @{
+    /// @name Actions
+
+    /**
+     * Sets the ActionManager object that is used by all actions.
+     *
+     * @warning If you set a new ActionManager, then previously created actions will be removed.
+     *
+     * @param actionManager     A ActionManager object that is used by all actions.
+     */
+    virtual void setActionManager(ActionManager* actionManager);
+    /**
+     * Gets the ActionManager object that is used by all actions.
+     * @see setActionManager(ActionManager*)
+     * @return A ActionManager object.
+     */
+    virtual ActionManager* getActionManager() { return _actionManager; }
+    virtual const ActionManager* getActionManager() const { return _actionManager; }
+
+    /**
+     * Executes an action, and returns the action that is executed.
+     *
+     * This node becomes the action's target. Refer to Action::getTarget().
+     * @warning Actions don't retain their target.
+     *
+     * @param action An Action pointer.
+     */
+    virtual Action* runAction(Action* action);
+
+    /**
+     * Stops and removes all actions from the running action list .
+     */
+    void stopAllActions();
+
+    /**
+     * Stops and removes an action from the running action list.
+     *
+     * @param action    The action object to be removed.
+     */
+    void stopAction(Action* action);
+
+    /**
+     * Removes an action from the running action list by its tag.
+     *
+     * @param tag   A tag that indicates the action to be removed.
+     */
+    void stopActionByTag(int tag);
+    
+    /**
+     * Removes all actions from the running action list by its tag.
+     *
+     * @param tag   A tag that indicates the action to be removed.
+     */
+    void stopAllActionsByTag(int tag);
+
+    /**
+     * Removes all actions from the running action list by its flags.
+     *
+     * @param flags   A flag field that removes actions based on bitwise AND.
+     */
+    void stopActionsByFlags(unsigned int flags);
+
+    /**
+     * Gets an action from the running action list by its tag.
+     *
+     * @see `setTag(int)`, `getTag()`.
+     *
+     * @return The action object with the given tag.
+     */
+    Action* getActionByTag(int tag);
+
+    /**
+     * Returns the numbers of actions that are running plus the ones that are schedule to run (actions in actionsToAdd and actions arrays).
+     *
+     * Composable actions are counted as 1 action. Example:
+     *    If you are running 1 Sequence of 7 actions, it will return 1.
+     *    If you are running 7 Sequences of 2 actions, it will return 7.
+     *
+     * @return The number of actions that are running plus the ones that are schedule to run.
+     */
+    ssize_t getNumberOfRunningActions() const;
+
+    /**
+     * Returns the numbers of actions that are running plus the ones that are
+     * schedule to run (actions in actionsToAdd and actions arrays) with a
+     * specific tag.
+     *
+     * Composable actions are counted as 1 action. Example:
+     *    If you are running 1 Sequence of 7 actions, it will return 1.
+     *    If you are running 7 Sequences of 2 actions, it will return 7.
+     *
+     * @param  tag The tag that will be searched.
+     *
+     * @return The number of actions that are running plus the
+     *         ones that are schedule to run with specific tag.
+     */
+    ssize_t getNumberOfRunningActionsByTag(int tag) const;
+
+    /// @} end of Actions
+
+
+    /// @{
+    /// @name Scheduler and Timer
+
+    /**
+     * Sets a Scheduler object that is used to schedule all "updates" and timers.
+     *
+     * @warning If you set a new Scheduler, then previously created timers/update are going to be removed.
+     * @param scheduler     A Scheduler object that is used to schedule all "update" and timers.
+     */
+    virtual void setScheduler(Scheduler* scheduler);
+    /**
+     * Gets a Scheduler object.
+     *
+     * @see setScheduler(Scheduler*)
+     * @return A Scheduler object.
+     */
+    virtual Scheduler* getScheduler() { return _scheduler; }
+    virtual const Scheduler* getScheduler() const { return _scheduler; }
+
+
+    /**
+     * Checks whether a selector is scheduled.
+     *
+     * @param selector      A function selector
+     * @return Whether the function selector is scheduled.
+     * @js NA
+     * @lua NA
+     */
+    bool isScheduled(SEL_SCHEDULE selector) const;
+
+    /**
+     * Checks whether a lambda function is scheduled.
+     *
+     * @param key      key of the callback
+     * @return Whether the lambda function selector is scheduled.
+     * @js NA
+     * @lua NA
+     */
+    bool isScheduled(const std::string &key) const;
+
+    /**
+     * Schedules the "update" method.
+     *
+     * It will use the order number 0. This method will be called every frame.
+     * Scheduled methods with a lower order value will be called before the ones that have a higher order value.
+     * Only one "update" method could be scheduled per node.
+     * @lua NA
+     */
+    void scheduleUpdate();
+
+    /**
+     * Schedules the "update" method with a custom priority.
+     *
+     * This selector will be called every frame.
+     * Scheduled methods with a lower priority will be called before the ones that have a higher value.
+     * Only one "update" selector could be scheduled per node (You can't have 2 'update' selectors).
+     * @lua NA
+     *
+     * @param priority A given priority value.
+     */
+    void scheduleUpdateWithPriority(int priority);
+
+    /*
+     * Unschedules the "update" method.
+     * @see scheduleUpdate();
+     */
+    void unscheduleUpdate();
+
+    /**
+     * Schedules a custom selector.
+     *
+     * If the selector is already scheduled, then the interval parameter will be updated without scheduling it again.
+     @code
+     // firstly, implement a schedule function
+     void MyNode::TickMe(float dt);
+     // wrap this function into a selector via schedule_selector macro.
+     this->schedule(CC_SCHEDULE_SELECTOR(MyNode::TickMe), 0, 0, 0);
+     @endcode
+     *
+     * @param selector  The SEL_SCHEDULE selector to be scheduled.
+     * @param interval  Tick interval in seconds. 0 means tick every frame. If interval = 0, it's recommended to use scheduleUpdate() instead.
+     * @param repeat    The selector will be executed (repeat + 1) times, you can use CC_REPEAT_FOREVER for tick infinitely.
+     * @param delay     The amount of time that the first tick will wait before execution.
+     * @lua NA
+     */
+    void schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay);
+
+    /**
+     * Schedules a custom selector with an interval time in seconds.
+     * @see `schedule(SEL_SCHEDULE, float, unsigned int, float)`
+     *
+     * @param selector      The SEL_SCHEDULE selector to be scheduled.
+     * @param interval      Callback interval time in seconds. 0 means tick every frame,
+     * @lua NA
+     */
+    void schedule(SEL_SCHEDULE selector, float interval);
+
+    /**
+     * Schedules a selector that runs only once, with a delay of 0 or larger
+     * @see `schedule(SEL_SCHEDULE, float, unsigned int, float)`
+     *
+     * @param selector      The SEL_SCHEDULE selector to be scheduled.
+     * @param delay         The amount of time that the first tick will wait before execution.
+     * @lua NA
+     */
+    void scheduleOnce(SEL_SCHEDULE selector, float delay);
+
+    /**
+     * Schedules a lambda function that runs only once, with a delay of 0 or larger
+     *
+     * @param callback      The lambda function to be scheduled.
+     * @param delay         The amount of time that the first tick will wait before execution.
+     * @param key           The key of the lambda function. To be used if you want to unschedule it.
+     * @lua NA
+     */
+    void scheduleOnce(const std::function<void(float)>& callback, float delay, const std::string &key);
+
+    /**
+     * Schedules a custom selector, the scheduled selector will be ticked every frame.
+     * @see schedule(SEL_SCHEDULE, float, unsigned int, float)
+     *
+     * @param selector      A function wrapped as a selector
+     * @lua NA
+     */
+    void schedule(SEL_SCHEDULE selector);
+
+    /**
+     * Schedules a lambda function. The scheduled lambda function will be called every frame.
+     *
+     * @param callback      The lambda function to be scheduled.
+     * @param key           The key of the lambda function. To be used if you want to unschedule it.
+     * @lua NA
+     */
+    void schedule(const std::function<void(float)>& callback, const std::string &key);
+
+    /**
+     * Schedules a lambda function. The scheduled lambda function will be called every "interval" seconds
+     *
+     * @param callback      The lambda function to be scheduled
+     * @param interval      Callback interval time in seconds. 0 means every frame,
+     * @param key           The key of the lambda function. To be used if you want to unschedule it
+     * @lua NA
+     */
+    void schedule(const std::function<void(float)>& callback, float interval, const std::string &key);
+
+    /**
+     * Schedules a lambda function.
+     *
+     * @param callback  The lambda function to be schedule.
+     * @param interval  Tick interval in seconds. 0 means tick every frame.
+     * @param repeat    The selector will be executed (repeat + 1) times, you can use CC_REPEAT_FOREVER for tick infinitely.
+     * @param delay     The amount of time that the first tick will wait before execution.
+     * @param key       The key of the lambda function. To be used if you want to unschedule it.
+     * @lua NA
+     */
+    void schedule(const std::function<void(float)>& callback, float interval, unsigned int repeat, float delay, const std::string &key);
+
+    /**
+     * Unschedules a custom selector.
+     * @see `schedule(SEL_SCHEDULE, float, unsigned int, float)`
+     *
+     * @param selector      A function wrapped as a selector.
+     * @lua NA
+     */
+    void unschedule(SEL_SCHEDULE selector);
+
+    /**
+     * Unschedules a lambda function.
+     *
+     * @param key      The key of the lambda function to be unscheduled.
+     * @lua NA
+     */
+    void unschedule(const std::string &key);
+
+    /**
+     * Unschedule all scheduled selectors and lambda functions: custom selectors, and the 'update' selector and lambda functions.
+     * Actions are not affected by this method.
+     * @lua NA
+     */
+    void unscheduleAllCallbacks();
+
+    /**
+     * Resumes all scheduled selectors, actions and event listeners.
+     * This method is called internally by onEnter.
+     */
+    virtual void resume();
+    /**
+     * Pauses all scheduled selectors, actions and event listeners.
+     * This method is called internally by onExit.
+     */
+    virtual void pause();
+
+    /**
+     * Update method will be called automatically every frame if "scheduleUpdate" is called, and the node is "live".
+     * @param delta In seconds.
+     */
+    virtual void update(float delta);
+
+    /// @} end of Scheduler and Timer
+
+    /// @{
+    /// @name Transformations
+
+    /**
+     * Calls children's updateTransform() method recursively.
+     *
+     * This method is moved from Sprite, so it's no longer specific to Sprite.
+     * As the result, you apply SpriteBatchNode's optimization on your customed Node.
+     * e.g., `batchNode->addChild(myCustomNode)`, while you can only addChild(sprite) before.
+     */
+    virtual void updateTransform();
+
+    /**
+     * Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.
+     * The matrix is in Pixels.
+     *
+     * @return The transformation matrix.
+     */
+    virtual const Mat4& getNodeToParentTransform() const;
+    virtual AffineTransform getNodeToParentAffineTransform() const;
+
+    /**
+     * Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.
+     * The matrix is in Pixels.
+     * Note: If ancestor is not a valid ancestor of the node, the API would return the same value as @see getNodeToWorldTransform
+     *
+     * @param ancestor The parent's node pointer.
+     * @since v3.7
+     * @return The transformation matrix.
+     */
+    virtual Mat4 getNodeToParentTransform(Node* ancestor) const;
+
+    /**
+     * Returns the affine transform matrix that transform the node's (local) space coordinates into the parent's space coordinates.
+     * The matrix is in Pixels.
+     *
+     * Note: If ancestor is not a valid ancestor of the node, the API would return the same value as @see getNodeToWorldAffineTransform
+     *
+     * @param ancestor The parent's node pointer.
+     * @since v3.7
+     * @return The affine transformation matrix.
+     */
+    virtual AffineTransform getNodeToParentAffineTransform(Node* ancestor) const;
+
+    /** 
+     * Sets the transformation matrix manually.
+     *
+     * @param transform A given transformation matrix.
+     */
+    virtual void setNodeToParentTransform(const Mat4& transform);
+
+    /**
+     * Returns the matrix that transform parent's space coordinates to the node's (local) space coordinates.
+     * The matrix is in Pixels.
+     *
+     * @return The transformation matrix.
+     */
+    virtual const Mat4& getParentToNodeTransform() const;
+    virtual AffineTransform getParentToNodeAffineTransform() const;
+
+    /**
+     * Returns the world affine transform matrix. The matrix is in Pixels.
+     *
+     * @return transformation matrix, in pixels.
+     */
+    virtual Mat4 getNodeToWorldTransform() const;
+    virtual AffineTransform getNodeToWorldAffineTransform() const;
+
+    /**
+     * Returns the inverse world affine transform matrix. The matrix is in Pixels.
+     *
+     * @return The transformation matrix.
+     */
+    virtual Mat4 getWorldToNodeTransform() const;
+    virtual AffineTransform getWorldToNodeAffineTransform() const;
+
+    /// @} end of Transformations
+
+
+    /// @{
+    /// @name Coordinate Converters
+
+    /**
+     * Converts a Vec2 to node (local) space coordinates. The result is in Points.
+     *
+     * @param worldPoint A given coordinate.
+     * @return A point in node (local) space coordinates.
+     */
+    Vec2 convertToNodeSpace(const Vec2& worldPoint) const;
+
+    /**
+     * Converts a Vec2 to world space coordinates. The result is in Points.
+     *
+     * @param nodePoint A given coordinate.
+     * @return A point in world space coordinates.
+     */
+    Vec2 convertToWorldSpace(const Vec2& nodePoint) const;
+
+    /**
+     * Converts a Vec2 to node (local) space coordinates. The result is in Points.
+     * treating the returned/received node point as anchor relative.
+     *
+     * @param worldPoint A given coordinate.
+     * @return A point in node (local) space coordinates, anchor relative.
+     */
+    Vec2 convertToNodeSpaceAR(const Vec2& worldPoint) const;
+
+    /**
+     * Converts a local Vec2 to world space coordinates.The result is in Points.
+     * treating the returned/received node point as anchor relative.
+     *
+     * @param nodePoint A given coordinate.
+     * @return A point in world space coordinates, anchor relative.
+     */
+    Vec2 convertToWorldSpaceAR(const Vec2& nodePoint) const;
+
+    /**
+     * convenience methods which take a Touch instead of Vec2.
+     *
+     * @param touch A given touch.
+     * @return A point in world space coordinates.
+     */
+    Vec2 convertTouchToNodeSpace(Touch * touch) const;
+
+    /**
+     * converts a Touch (world coordinates) into a local coordinate. This method is AR (Anchor Relative).
+     *
+     * @param touch A given touch.
+     * @return A point in world space coordinates, anchor relative.
+     */
+    Vec2 convertTouchToNodeSpaceAR(Touch * touch) const;
+
+    /**
+     *  Sets an additional transform matrix to the node.
+     *
+     *  In order to remove it, call it again with the argument `nullptr`.
+     *
+     * @note The additional transform will be concatenated at the end of getNodeToParentTransform.
+     *        It could be used to simulate `parent-child` relationship between two nodes (e.g. one is in BatchNode, another isn't).
+     *
+     * @param additionalTransform An additional transform matrix.
+     */
+    void setAdditionalTransform(const Mat4* additionalTransform);
+    void setAdditionalTransform(const Mat4& additionalTransform);
+    void setAdditionalTransform(const AffineTransform& additionalTransform);
+
+    /// @} end of Coordinate Converters
+
+      /// @{
+    /// @name component functions
+    /**
+     * Gets a component by its name.
+     *
+     * @param name A given name of component.
+     * @return The Component by name.
+     */
+    Component* getComponent(const std::string& name);
+
+    /**
+     * Adds a component.
+     *
+     * @param component A given component.
+     * @return True if added success.
+     */
+    virtual bool addComponent(Component *component);
+
+    /**
+     * Removes a component by its name.
+     *
+     * @param name A given name of component.
+     * @return True if removed success.
+     */
+    virtual bool removeComponent(const std::string& name);
+
+    /** 
+     * Removes a component by its pointer.
+     *
+     * @param component A given component.
+     * @return True if removed success.
+     */
+    virtual bool removeComponent(Component *component);
+    /**
+     * Removes all components
+     */
+    virtual void removeAllComponents();
+    /// @} end of component functions
+    
+    // overrides
+    /**
+     * Return the node's opacity.
+     * @return A GLubyte value.
+     */
+    virtual uint8_t getOpacity() const;
+    /**
+     * Return the node's display opacity.
+     * The difference between opacity and displayedOpacity is:
+     * The displayedOpacity is what's the final rendering opacity of node.
+     * @return A GLubyte value.
+     */
+    virtual uint8_t getDisplayedOpacity() const;
+    /**
+     * Change node opacity.
+     * @param opacity A GLubyte opacity value.
+     */
+    virtual void setOpacity(uint8_t opacity);
+    /**
+     * Update the displayed opacity of node with it's parent opacity;
+     * @param parentOpacity The opacity of parent node.
+     */
+    virtual void updateDisplayedOpacity(uint8_t parentOpacity);
+    /**
+     * Whether cascadeOpacity is enabled or not.
+     * @return A boolean value.
+     */
+    virtual bool isCascadeOpacityEnabled() const;
+    /**
+     * Change node's cascadeOpacity property.
+     * @param cascadeOpacityEnabled True to enable cascadeOpacity, false otherwise.
+     */
+    virtual void setCascadeOpacityEnabled(bool cascadeOpacityEnabled);
+
+    /**
+     * Query node's color value.
+     * @return A Color3B color value.
+     */
+    virtual const Color3B& getColor() const;
+    /**
+     * Query node's displayed color.
+     * @return A Color3B color value.
+     */
+    virtual const Color3B& getDisplayedColor() const;
+    /**
+     * Change the color of node.
+     * @param color A Color3B color value.
+     */
+    virtual void setColor(const Color3B& color);
+    /**
+     * Update node's displayed color with its parent color.
+     * @param parentColor A Color3B color value.
+     */
+    virtual void updateDisplayedColor(const Color3B& parentColor);
+    /**
+     * Query whether cascadeColor is enabled or not.
+     * @return Whether cascadeColor is enabled or not.
+     */
+    virtual bool isCascadeColorEnabled() const;
+    /**
+     * If you want node's color affect the children node's color, then set it to true.
+     * Otherwise, set it to false.
+     * @param cascadeColorEnabled A boolean value.
+     */
+    virtual void setCascadeColorEnabled(bool cascadeColorEnabled);
+
+    /**
+     *  If you want the opacity affect the color property, then set to true.
+     * @param value A boolean value.
+     */
+    virtual void setOpacityModifyRGB(bool value);
+    /**
+     * If node opacity will modify the RGB color value, then you should override this method and return true.
+     * @return A boolean value, true indicates that opacity will modify color; false otherwise.
+     */
+    virtual bool isOpacityModifyRGB() const;
+
+    /**
+     * Set the callback of event onEnter.
+     * @param callback A std::function<void()> callback.
+     */
+    void setOnEnterCallback(const std::function<void()>& callback) { _onEnterCallback = callback; }
+    /**
+     * Get the callback of event onEnter.
+     * @return A std:function<void()> callback.
+     */
+    const std::function<void()>& getOnEnterCallback() const { return _onEnterCallback; }
+    /**
+     * Set the callback of event onExit.
+     * @param callback A std::function<void()> callback.
+     */
+    void setOnExitCallback(const std::function<void()>& callback) { _onExitCallback = callback; }
+    /**
+     * Get the callback of event onExit.
+     * @return A std::function<void()>.
+     */
+    const std::function<void()>& getOnExitCallback() const { return _onExitCallback; }
+    /**
+     * Set the callback of event EnterTransitionDidFinish.
+     * @param callback A std::function<void()> callback.
+     */
+    void setOnEnterTransitionDidFinishCallback(const std::function<void()>& callback) { _onEnterTransitionDidFinishCallback = callback; }
+    /**
+     * Get the callback of event EnterTransitionDidFinish.
+     * @return std::function<void()>
+     */
+    const std::function<void()>& getOnEnterTransitionDidFinishCallback() const { return _onEnterTransitionDidFinishCallback; }
+    /**
+     * Set the callback of event ExitTransitionDidStart.
+     * @param callback A std::function<void()> callback.
+     */
+    void setOnExitTransitionDidStartCallback(const std::function<void()>& callback) { _onExitTransitionDidStartCallback = callback; }
+    /**
+     * Get the callback of event ExitTransitionDidStart.
+     * @return std::function<void()>
+     */
+    const std::function<void()>& getOnExitTransitionDidStartCallback() const { return _onExitTransitionDidStartCallback; }
+    
+    /**
+     * get & set camera mask, the node is visible by the camera whose camera flag & node's camera mask is true
+     */
+    unsigned short getCameraMask() const { return _cameraMask; }
+    /**
+     * Modify the camera mask for current node.
+     * If applyChildren is true, then it will modify the camera mask of its children recursively.
+     * @param mask A unsigned short bit for mask.
+     * @param applyChildren A boolean value to determine whether the mask bit should apply to its children or not.
+     */
+    virtual void setCameraMask(unsigned short mask, bool applyChildren = true);
+    
+    virtual void setProgramState(backend::ProgramState* programState);
+    virtual backend::ProgramState* getProgramState() const;
+
+CC_CONSTRUCTOR_ACCESS:
+    // Nodes should be created using create();
+    Node();
+    virtual ~Node();
+
+    virtual bool init();
+
+protected:
+    /// lazy allocs
+    void childrenAlloc();
+    
+    /// helper that reorder a child
+    void insertChild(Node* child, int z);
+
+    /// Removes a child, call child->onExit(), do cleanup, remove it from children array.
+    void detachChild(Node *child, ssize_t index, bool doCleanup);
+
+    /// Convert cocos2d coordinates to UI windows coordinate.
+    Vec2 convertToWindowSpace(const Vec2& nodePoint) const;
+
+    Mat4 transform(const Mat4 &parentTransform);
+    uint32_t processParentFlags(const Mat4& parentTransform, uint32_t parentFlags);
+
+    virtual void updateCascadeOpacity();
+    virtual void disableCascadeOpacity();
+    virtual void updateCascadeColor();
+    virtual void disableCascadeColor();
+    virtual void updateColor() {}
+    
+    bool doEnumerate(std::string name, std::function<bool (Node *)> callback) const;
+    bool doEnumerateRecursive(const Node* node, const std::string &name, std::function<bool (Node *)> callback) const;
+    
+    //check whether this camera mask is visible by the current visiting camera
+    bool isVisitableByVisitingCamera() const;
+    
+    // update quaternion from Rotation3D
+    void updateRotationQuat();
+    // update Rotation3D from quaternion
+    void updateRotation3D();
+    
+private:
+    void addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag);
+    
+protected:
+
+    float _rotationX;               ///< rotation on the X-axis
+    float _rotationY;               ///< rotation on the Y-axis
+
+    // rotation Z is decomposed in 2 to simulate Skew for Flash animations
+    float _rotationZ_X;             ///< rotation angle on Z-axis, component X
+    float _rotationZ_Y;             ///< rotation angle on Z-axis, component Y
+    
+    Quaternion _rotationQuat;       ///rotation using quaternion, if _rotationZ_X == _rotationZ_Y, _rotationQuat = RotationZ_X * RotationY * RotationX, else _rotationQuat = RotationY * RotationX
+
+    float _scaleX;                  ///< scaling factor on x-axis
+    float _scaleY;                  ///< scaling factor on y-axis
+    float _scaleZ;                  ///< scaling factor on z-axis
+
+    Vec2 _position;                 ///< position of the node
+    float _positionZ;               ///< OpenGL real Z position
+    Vec2 _normalizedPosition;
+    bool _usingNormalizedPosition;
+    bool _normalizedPositionDirty;
+
+    float _skewX;                   ///< skew angle on x-axis
+    float _skewY;                   ///< skew angle on y-axis
+
+    Vec2 _anchorPointInPoints;      ///< anchor point in points
+    Vec2 _anchorPoint;              ///< anchor point normalized (NOT in points)
+
+    Size _contentSize;              ///< untransformed size of the node
+    bool _contentSizeDirty;         ///< whether or not the contentSize is dirty
+
+    Mat4 _modelViewTransform;       ///< ModelView transform of the Node.
+
+    // "cache" variables are allowed to be mutable
+    mutable Mat4 _transform;        ///< transform
+    mutable bool _transformDirty;   ///< transform dirty flag
+    mutable Mat4 _inverse;          ///< inverse transform
+    mutable bool _inverseDirty;     ///< inverse transform dirty flag
+    mutable Mat4* _additionalTransform; ///< two transforms needed by additional transforms
+    mutable bool _additionalTransformDirty; ///< transform dirty ?
+    bool _transformUpdated;         ///< Whether or not the Transform object was updated since the last frame
+
+#if CC_LITTLE_ENDIAN
+    union {
+        struct {
+            std::uint32_t _orderOfArrival;
+            std::int32_t _localZOrder;
+        };
+        std::int64_t _localZOrder$Arrival;
+    };
+#else
+    union {
+        struct {
+            std::int32_t _localZOrder;
+            std::uint32_t _orderOfArrival;
+        };
+        std::int64_t _localZOrder$Arrival;
+    };
+#endif
+
+    float _globalZOrder;            ///< Global order used to sort the node
+
+    static std::uint32_t s_globalOrderOfArrival;
+
+    Vector<Node*> _children;        ///< array of children nodes
+    Node *_parent;                  ///< weak reference to parent node
+    Director* _director;            //cached director pointer to improve rendering performance
+    int _tag;                       ///< a tag. Can be any number you assigned just to identify this node
+    
+    std::string _name;              ///<a string label, an user defined string to identify this node
+    size_t _hashOfName;             ///<hash value of _name, used for speed in getChildByName
+
+    void *_userData;                ///< A user assigned void pointer, Can be point to any cpp object
+    Ref *_userObject;               ///< A user assigned Object
+    
+    Scheduler *_scheduler;          ///< scheduler used to schedule timers and updates
+
+    ActionManager *_actionManager;  ///< a pointer to ActionManager singleton, which is used to handle all the actions
+
+    EventDispatcher* _eventDispatcher;  ///< event dispatcher used to dispatch all kinds of events
+
+    bool _running;                  ///< is running
+
+    bool _visible;                  ///< is this node visible
+
+    bool _ignoreAnchorPointForPosition; ///< true if the Anchor Vec2 will be (0,0) when you position the Node, false otherwise.
+                                          ///< Used by Layer and Scene.
+
+    bool _reorderChildDirty;          ///< children order dirty flag
+    bool _isTransitionFinished;       ///< flag to indicate whether the transition was finished
+
+#if CC_ENABLE_SCRIPT_BINDING
+    int _scriptHandler;               ///< script handler for onEnter() & onExit(), used in Javascript binding and Lua binding.
+    int _updateScriptHandler;         ///< script handler for update() callback per frame, which is invoked from lua & javascript.
+    ccScriptType _scriptType;         ///< type of script binding, lua or javascript
+#endif
+    
+    ComponentContainer *_componentContainer;        ///< Dictionary of components
+    
+    // opacity controls
+    uint8_t     _displayedOpacity;
+    uint8_t     _realOpacity;
+    Color3B     _displayedColor;
+    Color3B     _realColor;
+    bool        _cascadeColorEnabled;
+    bool        _cascadeOpacityEnabled;
+
+    // camera mask, it is visible only when _cameraMask & current camera' camera flag is true
+    unsigned short _cameraMask;
+    
+    std::function<void()> _onEnterCallback;
+    std::function<void()> _onExitCallback;
+    std::function<void()> _onEnterTransitionDidFinishCallback;
+    std::function<void()> _onExitTransitionDidStartCallback;
+    
+    backend::ProgramState* _programState = nullptr;
+
+//Physics:remaining backwardly compatible  
+#if CC_USE_PHYSICS
+    PhysicsBody* _physicsBody;
+public:
+    void setPhysicsBody(PhysicsBody* physicsBody)
+    {
+        if (_physicsBody != nullptr)
+        {
+            removeComponent(_physicsBody);
+        }
+
+        addComponent(physicsBody);
+    }
+    PhysicsBody* getPhysicsBody() const { return _physicsBody; }
+
+    friend class PhysicsBody;
+#endif
+
+    static int __attachedNodeCount;
+    
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(Node);
+};
+
+/**
+ * This is a helper function, checks a GL screen point is in content rectangle space.
+ *
+ * The content rectangle defined by origin(0,0) and content size.
+ * This function convert GL screen point to near and far planes as points Pn and Pf,
+ * then calculate the intersect point P which the line PnPf intersect with content rectangle.
+ * If P in content rectangle means this node be hit.
+ *
+ * @param pt        The point in GL screen space.
+ * @param camera    Which camera used to unproject pt to near/far planes.
+ * @param w2l       World to local transform matrix, used to convert Pn and Pf to rectangle space.
+ * @param rect      The test rectangle in local space.
+ * @parma p         Point to a Vec3 for store the intersect point, if don't need them set to nullptr.
+ * @return true if the point is in content rectangle, false otherwise.
+ */
+bool CC_DLL isScreenPointInRect(const Vec2 &pt, const Camera* camera, const Mat4& w2l, const Rect& rect, Vec3 *p);
+
+// end of _2d group
+/// @}
+
+NS_CC_END
+
+#endif // __CCNODE_H__

+ 184 - 0
3rdparty/cocos2dx/include/cocos/2d/CCNodeGrid.cpp

@@ -0,0 +1,184 @@
+/****************************************************************************
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+#include "2d/CCNodeGrid.h"
+#include "2d/CCGrid.h"
+#include "renderer/CCRenderer.h"
+
+NS_CC_BEGIN
+
+NodeGrid* NodeGrid::create()
+{
+    NodeGrid * ret = new (std::nothrow) NodeGrid();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+NodeGrid* NodeGrid::create(const cocos2d::Rect &rect)
+{
+    NodeGrid* ret = NodeGrid::create();
+    if (ret) {
+        ret->setGridRect(rect);
+    }
+    return ret;
+}
+
+NodeGrid::NodeGrid()
+{
+}
+
+void NodeGrid::setTarget(Node* target)
+{
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+    if (sEngine)
+    {
+        if (_gridTarget)
+            sEngine->releaseScriptObject(this, _gridTarget);
+        if (target)
+            sEngine->retainScriptObject(this, target);
+    }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+    CC_SAFE_RELEASE(_gridTarget);
+    CC_SAFE_RETAIN(target);
+    _gridTarget = target;
+}
+
+NodeGrid::~NodeGrid()
+{
+    CC_SAFE_RELEASE(_nodeGrid);
+    CC_SAFE_RELEASE(_gridTarget);
+}
+
+void NodeGrid::onGridBeginDraw()
+{
+    if (_nodeGrid && _nodeGrid->isActive())
+    {
+        _nodeGrid->beforeDraw();
+    }
+}
+
+void NodeGrid::onGridEndDraw()
+{
+    if(_nodeGrid && _nodeGrid->isActive())
+    {
+        _nodeGrid->afterDraw(this);
+    }
+}
+
+void NodeGrid::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
+{
+    // quick return if not visible. children won't be drawn.
+    if (!_visible)
+    {
+        return;
+    }
+
+    bool dirty = (parentFlags & FLAGS_TRANSFORM_DIRTY) || _transformUpdated;
+    if(dirty)
+        _modelViewTransform = this->transform(parentTransform);
+    _transformUpdated = false;
+
+    // IMPORTANT:
+    // To ease the migration to v3.0, we still support the Mat4 stack,
+    // but it is deprecated and your code should not rely on it
+    Director* director = Director::getInstance();
+    CCASSERT(nullptr != director, "Director is null when setting matrix stack");
+    
+    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+    director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
+
+    Director::Projection beforeProjectionType = Director::Projection::DEFAULT;
+    if(_nodeGrid && _nodeGrid->isActive())
+    {
+        beforeProjectionType = Director::getInstance()->getProjection();
+        _nodeGrid->set2DProjection();
+    }
+
+    onGridBeginDraw();
+
+    if(_gridTarget)
+    {
+        _gridTarget->visit(renderer, _modelViewTransform, dirty);
+    }
+    
+    int i = 0;
+    bool visibleByCamera = isVisitableByVisitingCamera();
+
+    if(!_children.empty())
+    {
+        sortAllChildren();
+        // draw children zOrder < 0
+        for(auto size = _children.size(); i < size; ++i)
+        {
+            auto node = _children.at(i);
+
+            if ( node && node->getLocalZOrder() < 0 )
+                node->visit(renderer, _modelViewTransform, dirty);
+            else
+                break;
+        }
+        // self draw,currently we have nothing to draw on NodeGrid, so there is no need to add render command
+        if (visibleByCamera)
+            this->draw(renderer, _modelViewTransform, dirty);
+
+        for(auto it=_children.cbegin()+i, itCend = _children.cend(); it != itCend; ++it) {
+            (*it)->visit(renderer, _modelViewTransform, dirty);
+        }
+    }
+    else if (visibleByCamera)
+    {
+        this->draw(renderer, _modelViewTransform, dirty);
+    }
+    
+    // FIX ME: Why need to set _orderOfArrival to 0??
+    // Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
+    // setOrderOfArrival(0);
+    
+    if(_nodeGrid && _nodeGrid->isActive())
+    {
+        // restore projection
+        director->setProjection(beforeProjectionType);
+    }
+
+    onGridEndDraw();
+
+    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+}
+
+void NodeGrid::setGrid(GridBase *grid)
+{
+    CC_SAFE_RELEASE(_nodeGrid);
+    CC_SAFE_RETAIN(grid);
+    _nodeGrid = grid;
+}
+
+NS_CC_END

+ 111 - 0
3rdparty/cocos2dx/include/cocos/2d/CCNodeGrid.h

@@ -0,0 +1,111 @@
+/****************************************************************************
+ Copyright (c) 2013-2016 Chukong Technologies Inc.
+ Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+ http://www.cocos2d-x.org
+
+ Permission is hereby granted, free 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.
+ ****************************************************************************/
+#pragma once
+
+#include "2d/CCNode.h"
+#include "renderer/CCGroupCommand.h"
+#include "renderer/CCCustomCommand.h"
+
+NS_CC_BEGIN
+
+class GridBase;
+/**
+ *  @addtogroup _2d
+ *  @{
+ */
+
+/**
+ * @brief Base class for Grid Node.
+ */
+
+class CC_DLL NodeGrid : public Node
+{
+public:
+    /** Create a Grid Node.
+     *
+     * @return An autorelease Grid Node.
+     */
+    static NodeGrid* create();
+    
+    static NodeGrid* create(const Rect& rect);
+    
+    /** Get a Grid Node. 
+     *
+     * @return Return a GridBase.
+     */
+    GridBase* getGrid() { return _nodeGrid; }
+    /**
+     * @js NA
+     */
+    const GridBase* getGrid() const { return _nodeGrid; }
+
+    /**
+     * Changes a grid object that is used when applying effects.
+     *
+     * @param grid  A Grid object that is used when applying effects.
+     */
+    void setGrid(GridBase *grid);
+    
+    /** Set the Grid Target. 
+     *
+     * @param target A Node is used to set the Grid Target.
+     */
+    void setTarget(Node *target);
+    
+    /**
+     * @brief Set the effect grid rect.
+     * @param gridRect The effect grid rect.
+     */
+    void setGridRect(const Rect& gridRect) { _gridRect = gridRect; }
+    /**
+     * @brief Get the effect grid rect.
+     * @return Return the effect grid rect.
+     */
+    const Rect& getGridRect() const { return _gridRect; }
+
+    // overrides
+    virtual void visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    NodeGrid();
+    virtual ~NodeGrid();
+
+protected:
+    void onGridBeginDraw();
+    void onGridEndDraw();
+
+    Node* _gridTarget = nullptr;
+    GridBase* _nodeGrid = nullptr;
+    GroupCommand _groupCommand;
+    CustomCommand _gridBeginCommand;
+    CustomCommand _gridEndCommand;
+    
+    Rect _gridRect = Rect::ZERO;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(NodeGrid);
+};
+/** @} */
+NS_CC_END

+ 170 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParallaxNode.cpp

@@ -0,0 +1,170 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCParallaxNode.h"
+#include "base/ccCArray.h"
+
+NS_CC_BEGIN
+
+class PointObject : public Ref
+{
+public:
+    static PointObject * create(Vec2 ratio, Vec2 offset)
+    {
+        PointObject *ret = new (std::nothrow) PointObject();
+        ret->initWithPoint(ratio, offset);
+        ret->autorelease();
+        return ret;
+    }
+    
+    bool initWithPoint(Vec2 ratio, Vec2 offset)
+    {
+        _ratio = ratio;
+        _offset = offset;
+        _child = nullptr;
+        return true;
+    }
+    
+    const Vec2& getRatio() const { return _ratio; }
+    void setRatio(const Vec2& ratio) { _ratio = ratio; }
+
+    const Vec2& getOffset() const { return _offset; }
+    void setOffset(const Vec2& offset) { _offset = offset; }
+    
+    Node* getChild() const { return _child; }
+    void setChild(Node* child) { _child = child; }
+    
+private:
+    Vec2 _ratio;
+    Vec2 _offset;
+    Node *_child; // weak ref
+};
+
+ParallaxNode::ParallaxNode()
+{
+    _parallaxArray = ccArrayNew(5);        
+    _lastPosition.set(-100.0f, -100.0f);
+}
+
+ParallaxNode::~ParallaxNode()
+{
+    if( _parallaxArray )
+    {
+        ccArrayFree(_parallaxArray);
+        _parallaxArray = nullptr;
+    }
+}
+
+ParallaxNode * ParallaxNode::create()
+{
+    ParallaxNode *ret = new (std::nothrow) ParallaxNode();
+    ret->autorelease();
+    return ret;
+}
+
+void ParallaxNode::addChild(Node* /*child*/, int /*zOrder*/, int /*tag*/)
+{
+    CCASSERT(0,"ParallaxNode: use addChild:z:parallaxRatio:positionOffset instead");
+}
+
+void ParallaxNode::addChild(Node* /*child*/, int /*zOrder*/, const std::string& /*name*/)
+{
+    CCASSERT(0,"ParallaxNode: use addChild:z:parallaxRatio:positionOffset instead");
+}
+
+void ParallaxNode::addChild(Node *child, int z, const Vec2& ratio, const Vec2& offset)
+{
+    CCASSERT( child != nullptr, "Argument must be non-nil");
+    PointObject *obj = PointObject::create(ratio, offset);
+    obj->setChild(child);
+    ccArrayAppendObjectWithResize(_parallaxArray, (Ref*)obj);
+
+    Vec2 pos = this->absolutePosition();
+    pos.x = -pos.x + pos.x * ratio.x + offset.x;
+    pos.y = -pos.y + pos.y * ratio.y + offset.y;
+    child->setPosition(pos);
+
+    Node::addChild(child, z, child->getName());
+}
+
+void ParallaxNode::removeChild(Node* child, bool cleanup)
+{
+    for( int i=0;i < _parallaxArray->num;i++)
+    {
+        PointObject *point = (PointObject*)_parallaxArray->arr[i];
+        if (point->getChild() == child)
+        {
+            ccArrayRemoveObjectAtIndex(_parallaxArray, i, true);
+            break;
+        }
+    }
+    Node::removeChild(child, cleanup);
+}
+
+void ParallaxNode::removeAllChildrenWithCleanup(bool cleanup)
+{
+    ccArrayRemoveAllObjects(_parallaxArray);
+    Node::removeAllChildrenWithCleanup(cleanup);
+}
+
+Vec2 ParallaxNode::absolutePosition()
+{
+    Vec2 ret = _position;
+    Node *cn = this;
+    while (cn->getParent() != nullptr)
+    {
+        cn = cn->getParent();
+        ret = ret + cn->getPosition();
+    }
+    return ret;
+}
+
+/*
+The positions are updated at visit because:
+- using a timer is not guaranteed that it will called after all the positions were updated
+- overriding "draw" will only precise if the children have a z > 0
+*/
+void ParallaxNode::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
+{
+    //    Vec2 pos = position_;
+    //    Vec2    pos = [self convertToWorldSpace:Vec2::ZERO];
+    Vec2 pos = this->absolutePosition();
+    if( ! pos.equals(_lastPosition) )
+    {
+        for( int i=0; i < _parallaxArray->num; i++ ) 
+        {
+            PointObject *point = (PointObject*)_parallaxArray->arr[i];
+            float x = -pos.x + pos.x * point->getRatio().x + point->getOffset().x;
+            float y = -pos.y + pos.y * point->getRatio().y + point->getOffset().y;            
+            point->getChild()->setPosition(x,y);
+        }
+        _lastPosition = pos;
+    }
+    Node::visit(renderer, parentTransform, parentFlags);
+}
+
+NS_CC_END

+ 121 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParallaxNode.h

@@ -0,0 +1,121 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __CCPARALLAX_NODE_H__
+#define __CCPARALLAX_NODE_H__
+
+#include "2d/CCNode.h"
+/*#include "ccArray.h"*/
+
+NS_CC_BEGIN
+
+struct _ccArray;
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** @class ParallaxNode
+ * @brief ParallaxNode: A node that simulates a parallax scroller
+
+The children will be moved faster / slower than the parent according the parallax ratio.
+
+*/
+class CC_DLL ParallaxNode : public Node
+{
+public:
+    /** Create a Parallax node. 
+     *
+     * @return An autoreleased ParallaxNode object.
+     */
+    static ParallaxNode * create();
+
+    // prevents compiler warning: "Included function hides overloaded virtual functions"
+    using Node::addChild;
+
+    /** Adds a child to the container with a local z-order, parallax ratio and position offset.
+     *
+     * @param child A child node.
+     * @param z Z order for drawing priority.
+     * @param parallaxRatio A given parallax ratio.
+     * @param positionOffset A given position offset.
+     */
+    void addChild(Node * child, int z, const Vec2& parallaxRatio, const Vec2& positionOffset);
+
+    /** Sets an array of layers for the Parallax node.
+     *
+     * @param parallaxArray An array of layers for the Parallax node.
+     */
+    void setParallaxArray( struct _ccArray *parallaxArray) { _parallaxArray = parallaxArray; }
+    /** Returns the array of layers of the Parallax node.
+     *
+     * @return An array of layers for the Parallax node.
+     */
+    struct _ccArray* getParallaxArray() { return _parallaxArray; }
+    const struct _ccArray* getParallaxArray() const { return _parallaxArray; }
+
+    //
+    // Overrides
+    //
+    virtual void addChild(Node * child, int zOrder, int tag) override;
+    virtual void addChild(Node * child, int zOrder, const std::string &name) override;
+    virtual void removeChild(Node* child, bool cleanup) override;
+    virtual void removeAllChildrenWithCleanup(bool cleanup) override;
+    virtual void visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;
+
+CC_CONSTRUCTOR_ACCESS:
+    /** Adds a child to the container with a z-order, a parallax ratio and a position offset
+     It returns self, so you can chain several addChilds.
+     @since v0.8
+     * @js ctor
+     */
+    ParallaxNode();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParallaxNode();
+
+protected:
+    Vec2 absolutePosition();
+
+    Vec2    _lastPosition;
+    struct _ccArray* _parallaxArray;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParallaxNode);
+};
+
+// end of _2d group
+/// @}
+
+NS_CC_END
+
+#endif //__CCPARALLAX_NODE_H__
+
+

+ 556 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParticleBatchNode.cpp

@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2009 Matt Oswald
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2010-2012 cocos2d-x.org
+ * Copyright (c) 2011      Zynga Inc.
+ * Copyright (c) 2011      Marco Tillemans
+ * Copyright (c) 2013-2016 Chukong Technologies Inc.
+ * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ *
+ * http://www.cocos2d-x.org
+ *
+ * Permission is hereby granted, free 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.
+ *
+ */
+#include "2d/CCParticleBatchNode.h"
+#include <stddef.h> // offsetof
+#include "base/ccTypes.h"
+#include "2d/CCGrid.h"
+#include "2d/CCParticleSystem.h"
+#include "renderer/CCTextureCache.h"
+#include "renderer/CCQuadCommand.h"
+#include "renderer/CCRenderer.h"
+#include "renderer/CCTextureAtlas.h"
+#include "base/CCProfiling.h"
+#include "base/ccUTF8.h"
+#include "base/ccUtils.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/ProgramState.h"
+
+NS_CC_BEGIN
+
+ParticleBatchNode::ParticleBatchNode()
+{
+    auto& pipelineDescriptor = _customCommand.getPipelineDescriptor();
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR);
+    _programState = new (std::nothrow) backend::ProgramState(program);
+    pipelineDescriptor.programState = _programState;
+    _mvpMatrixLocaiton = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix");
+    _textureLocation = pipelineDescriptor.programState->getUniformLocation("u_texture");
+    
+    auto layout = _programState->getVertexLayout();
+    const auto& attributeInfo = _programState->getProgram()->getActiveAttributes();
+    auto iter = attributeInfo.find("a_position");
+    if(iter != attributeInfo.end())
+    {
+        layout->setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT3, 0, false);
+    }
+    iter = attributeInfo.find("a_texCoord");
+    if(iter != attributeInfo.end())
+    {
+        layout->setAttribute("a_texCoord", iter->second.location, backend::VertexFormat::FLOAT2, offsetof(V3F_C4B_T2F, texCoords), false);
+    }
+    iter = attributeInfo.find("a_color");
+    if(iter != attributeInfo.end())
+    {
+        layout->setAttribute("a_color", iter->second.location, backend::VertexFormat::UBYTE4, offsetof(V3F_C4B_T2F, colors), true);
+    }
+    layout->setLayout(sizeof(V3F_C4B_T2F));
+
+    _customCommand.setDrawType(CustomCommand::DrawType::ELEMENT);
+    _customCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
+}
+
+ParticleBatchNode::~ParticleBatchNode()
+{
+    CC_SAFE_RELEASE(_textureAtlas);
+}
+/*
+ * creation with Texture2D
+ */
+
+ParticleBatchNode* ParticleBatchNode::createWithTexture(Texture2D *tex, int capacity/* = kParticleDefaultCapacity*/)
+{
+    ParticleBatchNode * p = new (std::nothrow) ParticleBatchNode();
+    if( p && p->initWithTexture(tex, capacity))
+    {
+        p->autorelease();
+        return p;
+    }
+    CC_SAFE_DELETE(p);
+    return nullptr;
+}
+
+/*
+ * creation with File Image
+ */
+
+ParticleBatchNode* ParticleBatchNode::create(const std::string& imageFile, int capacity/* = kParticleDefaultCapacity*/)
+{
+    ParticleBatchNode * p = new (std::nothrow) ParticleBatchNode();
+    if( p && p->initWithFile(imageFile, capacity))
+    {
+        p->autorelease();
+        return p;
+    }
+    CC_SAFE_DELETE(p);
+    return nullptr;
+}
+
+/*
+ * init with Texture2D
+ */
+bool ParticleBatchNode::initWithTexture(Texture2D *tex, int capacity)
+{
+    _textureAtlas = new (std::nothrow) TextureAtlas();
+    _textureAtlas->initWithTexture(tex, capacity);
+
+    _children.reserve(capacity);
+    
+    _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+
+    return true;
+}
+
+/*
+ * init with FileImage
+ */
+bool ParticleBatchNode::initWithFile(const std::string& fileImage, int capacity)
+{
+    Texture2D *tex = Director::getInstance()->getTextureCache()->addImage(fileImage);
+    return initWithTexture(tex, capacity);
+}
+
+// ParticleBatchNode - composition
+
+// override visit.
+// Don't call visit on it's children
+void ParticleBatchNode::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
+{
+    // CAREFUL:
+    // This visit is almost identical to Node#visit
+    // with the exception that it doesn't call visit on it's children
+    //
+    // The alternative is to have a void Sprite#visit, but
+    // although this is less maintainable, is faster
+    //
+    if (!_visible)
+    {
+        return;
+    }
+
+    uint32_t flags = processParentFlags(parentTransform, parentFlags);
+
+    if (isVisitableByVisitingCamera())
+    {
+        // IMPORTANT:d
+        // To ease the migration to v3.0, we still support the Mat4 stack,
+        // but it is deprecated and your code should not rely on it
+        Director* director = Director::getInstance();
+        director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+        director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
+        
+        draw(renderer, _modelViewTransform, flags);
+        
+        director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+    }
+}
+
+// override addChild:
+void ParticleBatchNode::addChild(Node * aChild, int zOrder, int tag)
+{
+    CCASSERT( aChild != nullptr, "Argument must be non-nullptr");
+    CCASSERT( dynamic_cast<ParticleSystem*>(aChild) != nullptr, "CCParticleBatchNode only supports QuadParticleSystems as children");
+    ParticleSystem* child = static_cast<ParticleSystem*>(aChild);
+    CCASSERT( child->getTexture()->getBackendTexture() == _textureAtlas->getTexture()->getBackendTexture(), "CCParticleSystem is not using the same texture id");
+    
+    addChildByTagOrName(child, zOrder, tag, "", true);
+}
+
+void ParticleBatchNode::addChild(Node * aChild, int zOrder, const std::string &name)
+{
+    CCASSERT( aChild != nullptr, "Argument must be non-nullptr");
+    CCASSERT( dynamic_cast<ParticleSystem*>(aChild) != nullptr, "CCParticleBatchNode only supports QuadParticleSystems as children");
+    ParticleSystem* child = static_cast<ParticleSystem*>(aChild);
+    CCASSERT( child->getTexture()->getBackendTexture() == _textureAtlas->getTexture()->getBackendTexture(), "CCParticleSystem is not using the same texture id");
+   
+    addChildByTagOrName(child, zOrder, 0, name, false);
+}
+
+void ParticleBatchNode::addChildByTagOrName(ParticleSystem* child, int zOrder, int tag, const std::string &name, bool setTag)
+{
+    // If this is the 1st children, then copy blending function
+    if (_children.empty())
+    {
+        setBlendFunc(child->getBlendFunc());
+    }
+    
+    CCASSERT( _blendFunc.src  == child->getBlendFunc().src && _blendFunc.dst  == child->getBlendFunc().dst, "Can't add a ParticleSystem that uses a different blending function");
+    
+    //no lazy sorting, so don't call super addChild, call helper instead
+    int pos = 0;
+    if (setTag)
+        pos = addChildHelper(child, zOrder, tag, "", true);
+    else
+        pos = addChildHelper(child, zOrder, 0, name, false);
+    
+    //get new atlasIndex
+    int atlasIndex = 0;
+    
+    if (pos != 0)
+    {
+        ParticleSystem* p = static_cast<ParticleSystem*>(_children.at(pos-1));
+        atlasIndex = p->getAtlasIndex() + p->getTotalParticles();
+    }
+    else
+    {
+        atlasIndex = 0;
+    }
+    
+    insertChild(child, atlasIndex);
+    
+    // update quad info
+    child->setBatchNode(this);
+}
+
+// don't use lazy sorting, reordering the particle systems quads afterwards would be too complex
+// FIXME: research whether lazy sorting + freeing current quads and calloc a new block with size of capacity would be faster
+// FIXME: or possibly using vertexZ for reordering, that would be fastest
+// this helper is almost equivalent to Node's addChild, but doesn't make use of the lazy sorting
+int ParticleBatchNode::addChildHelper(ParticleSystem* child, int z, int aTag, const std::string &name, bool setTag)
+{
+    CCASSERT( child != nullptr, "Argument must be non-nil");
+    CCASSERT( child->getParent() == nullptr, "child already added. It can't be added again");
+
+    _children.reserve(4);
+
+    //don't use a lazy insert
+    auto pos = searchNewPositionInChildrenForZ(z);
+
+    _children.insert(pos, child);
+
+    if (setTag)
+        child->setTag(aTag);
+    else
+        child->setName(name);
+    
+    child->setLocalZOrder(z);
+
+    child->setParent(this);
+
+    if( _running )
+    {
+        child->onEnter();
+        child->onEnterTransitionDidFinish();
+    }
+    return pos;
+}
+
+// Reorder will be done in this function, no "lazy" reorder to particles
+void ParticleBatchNode::reorderChild(Node * aChild, int zOrder)
+{
+    CCASSERT( aChild != nullptr, "Child must be non-nullptr");
+    CCASSERT( dynamic_cast<ParticleSystem*>(aChild) != nullptr, "CCParticleBatchNode only supports QuadParticleSystems as children");
+    CCASSERT( _children.contains(aChild), "Child doesn't belong to batch" );
+
+    ParticleSystem* child = static_cast<ParticleSystem*>(aChild);
+
+    if( zOrder == child->getLocalZOrder() )
+    {
+        return;
+    }
+
+    // no reordering if only 1 child
+    if (!_children.empty())
+    {
+        int newIndex = 0, oldIndex = 0;
+
+        getCurrentIndex(&oldIndex, &newIndex, child, zOrder);
+
+        if( oldIndex != newIndex )
+        {
+
+            // reorder _children->array
+            child->retain();
+            _children.erase(oldIndex);
+            _children.insert(newIndex, child);
+            child->release();
+
+            // save old altasIndex
+            int oldAtlasIndex = child->getAtlasIndex();
+
+            // update atlas index
+            updateAllAtlasIndexes();
+
+            // Find new AtlasIndex
+            int newAtlasIndex = 0;
+            for (const auto& iter : _children)
+            {
+                auto node = static_cast<ParticleSystem*>(iter);
+                if( node == child )
+                {
+                    newAtlasIndex = child->getAtlasIndex();
+                    break;
+                }
+            }
+
+            // reorder textureAtlas quads
+            _textureAtlas->moveQuadsFromIndex(oldAtlasIndex, child->getTotalParticles(), newAtlasIndex);
+
+            child->updateWithNoTime();
+        }
+    }
+
+    child->setLocalZOrder(zOrder);
+}
+
+void ParticleBatchNode::getCurrentIndex(int* oldIndex, int* newIndex, Node* child, int z)
+{
+    bool foundCurrentIdx = false;
+    bool foundNewIdx = false;
+
+    int  minusOne = 0;
+    auto count = _children.size();
+
+    for( int i=0; i < count; i++ )
+    {
+        Node* pNode = _children.at(i);
+
+        // new index
+        if( pNode->getLocalZOrder() > z &&  ! foundNewIdx )
+        {
+            *newIndex = i;
+            foundNewIdx = true;
+
+            if( foundCurrentIdx && foundNewIdx )
+            {
+                break;
+            }
+        }
+
+        // current index
+        if( child == pNode )
+        {
+            *oldIndex = i;
+            foundCurrentIdx = true;
+
+            if( ! foundNewIdx )
+            {
+                minusOne = -1;
+            }
+
+            if( foundCurrentIdx && foundNewIdx )
+            {
+                break;
+            }
+        }
+
+    }
+
+    if( ! foundNewIdx )
+    {
+        *newIndex = static_cast<int>(count);
+    }
+
+    *newIndex += minusOne;
+}
+
+int ParticleBatchNode::searchNewPositionInChildrenForZ(int z)
+{
+    auto count = _children.size();
+
+    for( int i=0; i < count; i++ )
+    {
+        Node *child = _children.at(i);
+        if (child->getLocalZOrder() > z)
+        {
+            return i;
+        }
+    }
+    return static_cast<int>(count);
+}
+
+// override removeChild:
+void  ParticleBatchNode::removeChild(Node* aChild, bool cleanup)
+{
+    // explicit nil handling
+    if (aChild == nullptr)
+        return;
+
+    CCASSERT( dynamic_cast<ParticleSystem*>(aChild) != nullptr, "CCParticleBatchNode only supports QuadParticleSystems as children");
+    CCASSERT(_children.contains(aChild), "CCParticleBatchNode doesn't contain the sprite. Can't remove it");
+
+    ParticleSystem* child = static_cast<ParticleSystem*>(aChild);
+
+    // remove child helper
+    _textureAtlas->removeQuadsAtIndex(child->getAtlasIndex(), child->getTotalParticles());
+
+    // after memmove of data, empty the quads at the end of array
+    _textureAtlas->fillWithEmptyQuadsFromIndex(_textureAtlas->getTotalQuads(), child->getTotalParticles());
+
+    // particle could be reused for self rendering
+    child->setBatchNode(nullptr);
+    Node::removeChild(child, cleanup);
+
+    updateAllAtlasIndexes();
+}
+
+void ParticleBatchNode::removeChildAtIndex(int index, bool doCleanup)
+{
+    removeChild(_children.at(index), doCleanup);
+}
+
+void ParticleBatchNode::removeAllChildrenWithCleanup(bool doCleanup)
+{
+    for(const auto &child : _children)
+        static_cast<ParticleSystem*>(child)->setBatchNode(nullptr);
+
+    Node::removeAllChildrenWithCleanup(doCleanup);
+
+    _textureAtlas->removeAllQuads();
+}
+
+void ParticleBatchNode::draw(Renderer* renderer, const Mat4 & transform, uint32_t flags)
+{
+    CC_PROFILER_START("CCParticleBatchNode - draw");
+
+    if( _textureAtlas->getTotalQuads() == 0 )
+        return;
+    
+    _customCommand.init(_globalZOrder, _blendFunc);
+    
+    // Texture is set in TextureAtlas.
+    const cocos2d::Mat4& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    Mat4 finalMat = projectionMat * transform;
+    auto programState = _customCommand.getPipelineDescriptor().programState;
+    programState->setUniform(_mvpMatrixLocaiton, finalMat.m, sizeof(finalMat.m));
+    programState->setTexture(_textureLocation, 0, _textureAtlas->getTexture()->getBackendTexture());
+
+    if (_textureAtlas->isDirty())
+    {
+        const auto& quads = _textureAtlas->getQuads();
+        unsigned int capacity = (unsigned int)_textureAtlas->getCapacity();
+        const auto& indices = _textureAtlas->getIndices();
+        
+        _customCommand.createVertexBuffer((unsigned int)(sizeof(quads[0]) ), capacity, CustomCommand::BufferUsage::STATIC);
+        _customCommand.updateVertexBuffer(quads, sizeof(quads[0]) * capacity);
+        
+        _customCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT , capacity * 6, CustomCommand::BufferUsage::STATIC);
+        _customCommand.updateIndexBuffer(indices, sizeof(indices[0]) * capacity * 6);
+    }
+        
+    renderer->addCommand(&_customCommand);
+    
+    CC_PROFILER_STOP("CCParticleBatchNode - draw");
+}
+
+void ParticleBatchNode::increaseAtlasCapacityTo(ssize_t quantity)
+{
+    CCLOG("cocos2d: ParticleBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].",
+          (long)_textureAtlas->getCapacity(),
+          (long)quantity);
+
+    if( ! _textureAtlas->resizeCapacity(quantity) ) {
+        // serious problems
+        CCLOGWARN("cocos2d: WARNING: Not enough memory to resize the atlas");
+        CCASSERT(false,"XXX: ParticleBatchNode #increaseAtlasCapacity SHALL handle this assert");
+    }
+}
+
+//sets a 0'd quad into the quads array
+void ParticleBatchNode::disableParticle(int particleIndex)
+{
+    V3F_C4B_T2F_Quad* quad = &((_textureAtlas->getQuads())[particleIndex]);
+    quad->br.vertices.x = quad->br.vertices.y = quad->tr.vertices.x = quad->tr.vertices.y = quad->tl.vertices.x = quad->tl.vertices.y = quad->bl.vertices.x = quad->bl.vertices.y = 0.0f;
+}
+
+// ParticleBatchNode - add / remove / reorder helper methods
+
+// add child helper
+void ParticleBatchNode::insertChild(ParticleSystem* system, int index)
+{
+    system->setAtlasIndex(index);
+
+    if(_textureAtlas->getTotalQuads() + system->getTotalParticles() > _textureAtlas->getCapacity())
+    {
+        increaseAtlasCapacityTo(_textureAtlas->getTotalQuads() + system->getTotalParticles());
+
+        // after a realloc empty quads of textureAtlas can be filled with gibberish (realloc doesn't perform calloc), insert empty quads to prevent it
+        _textureAtlas->fillWithEmptyQuadsFromIndex(_textureAtlas->getCapacity() - system->getTotalParticles(), system->getTotalParticles());
+    }
+
+    // make room for quads, not necessary for last child
+    if (system->getAtlasIndex() + system->getTotalParticles() != _textureAtlas->getTotalQuads())
+    {
+        _textureAtlas->moveQuadsFromIndex(index, index+system->getTotalParticles());
+    }
+
+    // increase totalParticles here for new particles, update method of particle-system will fill the quads
+    _textureAtlas->increaseTotalQuadsWith(system->getTotalParticles());
+
+    updateAllAtlasIndexes();
+}
+
+//rebuild atlas indexes
+void ParticleBatchNode::updateAllAtlasIndexes()
+{
+    int index = 0;
+    
+    for(const auto &child : _children) {
+        ParticleSystem* partiSys = static_cast<ParticleSystem*>(child);
+        partiSys->setAtlasIndex(index);
+        index += partiSys->getTotalParticles();
+    }
+}
+
+// ParticleBatchNode - CocosNodeTexture protocol
+
+void ParticleBatchNode::updateBlendFunc()
+{
+    if( ! _textureAtlas->getTexture()->hasPremultipliedAlpha())
+        _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
+}
+
+void ParticleBatchNode::setTexture(Texture2D* texture)
+{
+    _textureAtlas->setTexture(texture);
+
+    // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it
+    if( texture && ! texture->hasPremultipliedAlpha() && ( _blendFunc.src == CC_BLEND_SRC && _blendFunc.dst == CC_BLEND_DST ) )
+    {
+        _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
+    }
+}
+
+Texture2D* ParticleBatchNode::getTexture() const
+{
+    return _textureAtlas->getTexture();
+}
+
+void ParticleBatchNode::setBlendFunc(const BlendFunc &blendFunc)
+{
+    _blendFunc = blendFunc;
+}
+// returns the blending function used for the texture
+const BlendFunc& ParticleBatchNode::getBlendFunc() const
+{
+    return _blendFunc;
+}
+
+NS_CC_END

+ 188 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParticleBatchNode.h

@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2009 Matt Oswald
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2010-2012 cocos2d-x.org
+ * Copyright (c) 2011      Zynga Inc.
+ * Copyright (c) 2011      Marco Tillemans
+ * Copyright (c) 2013-2016 Chukong Technologies Inc.
+ * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ *
+ * http://www.cocos2d-x.org
+ *
+ * Permission is hereby granted, free 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.
+ *
+ */
+#pragma once
+
+#include "2d/CCNode.h"
+#include "base/CCProtocols.h"
+#include "renderer/CCCustomCommand.h"
+
+NS_CC_BEGIN
+
+class Texture2D;
+class TextureAtlas;
+class ParticleSystem;
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+#define kParticleDefaultCapacity 500
+
+/** @class ParticleBatchNode
+ * @brief ParticleBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call
+ * (often known as "batch draw").
+ *
+ * A ParticleBatchNode can reference one and only one texture (one image file, one texture atlas).
+ * Only the ParticleSystems that are contained in that texture can be added to the SpriteBatchNode.
+ * All ParticleSystems added to a SpriteBatchNode are drawn in one OpenGL ES draw call.
+ * If the ParticleSystems are not added to a ParticleBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient.
+ *
+ *
+ * Limitations:
+ * - At the moment only ParticleSystemQuad is supported
+ * - All systems need to be drawn with the same parameters, blend function, aliasing, texture
+ *
+ * Most efficient usage
+ * - Initialize the ParticleBatchNode with the texture and enough capacity for all the particle systems
+ * - Initialize all particle systems and add them as child to the batch node
+ * @since v1.1
+ */
+
+class CC_DLL ParticleBatchNode : public Node, public TextureProtocol
+{
+public:
+    /** Create the particle system with Texture2D, a capacity of particles, which particle system to use.
+     *
+     * @param tex A given texture.
+     * @param capacity A capacity of particles.
+     * @return An autoreleased ParticleBatchNode object.
+     * @js NA
+     */
+    static ParticleBatchNode* createWithTexture(Texture2D *tex, int capacity = kParticleDefaultCapacity);
+
+    /** Create the particle system with the name of a file on disk (for a list of supported formats look at the Texture2D class), a capacity of particles.
+     *
+     * @param fileImage A given file name.
+     * @param capacity A capacity of particles.
+     * @return An autoreleased ParticleBatchNode object.
+     */
+    static ParticleBatchNode* create(const std::string& fileImage, int capacity = kParticleDefaultCapacity);
+
+    /** Inserts a child into the ParticleBatchNode.
+     *
+     * @param system A given particle system.
+     * @param index The insert index.
+     */
+    void insertChild(ParticleSystem* system, int index);
+
+    /** Remove a child of the ParticleBatchNode.
+     *
+     * @param index The index of the child.
+     * @param doCleanup True if all actions and callbacks on this node should be removed, false otherwise.
+     */
+    void removeChildAtIndex(int index, bool doCleanup);
+    void removeAllChildrenWithCleanup(bool doCleanup) override;
+
+    /** Disables a particle by inserting a 0'd quad into the texture atlas.
+     *
+     * @param particleIndex The index of the particle.
+     */
+    void disableParticle(int particleIndex);
+
+    /** Gets the texture atlas used for drawing the quads.
+     *
+     * @return The texture atlas used for drawing the quads.
+     */
+    TextureAtlas* getTextureAtlas() const { return _textureAtlas; }
+    
+    /** Sets the texture atlas used for drawing the quads.
+     *
+     * @param atlas The texture atlas used for drawing the quads.
+     */
+    void setTextureAtlas(TextureAtlas* atlas) { _textureAtlas = atlas; }
+    
+    // Overrides
+    virtual void visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;
+
+    using Node::addChild;
+    virtual void addChild(Node * child, int zOrder, int tag) override;
+    virtual void addChild(Node * child, int zOrder, const std::string &name) override;
+    virtual void removeChild(Node* child, bool cleanup) override;
+    virtual void reorderChild(Node * child, int zOrder) override;
+    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
+    virtual Texture2D* getTexture() const override;
+    virtual void setTexture(Texture2D *texture) override;
+    /**
+    * @code
+    * When this function bound into js or lua,the parameter will be changed
+    * In js: var setBlendFunc(var src, var dst)
+    * @endcode
+    * @lua NA
+    */
+    virtual void setBlendFunc(const BlendFunc &blendFunc) override;
+    /**
+    * @js NA
+    * @lua NA
+    */
+    virtual const BlendFunc& getBlendFunc() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleBatchNode();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleBatchNode();
+    
+    /** initializes the particle system with Texture2D, a capacity of particles */
+    bool initWithTexture(Texture2D *tex, int capacity);
+    
+    /** initializes the particle system with the name of a file on disk (for a list of supported formats look at the Texture2D class), a capacity of particles */
+    bool initWithFile(const std::string& fileImage, int capacity);
+    
+private:
+    void updateAllAtlasIndexes();
+    void increaseAtlasCapacityTo(ssize_t quantity);
+    int searchNewPositionInChildrenForZ(int z);
+    void getCurrentIndex(int* oldIndex, int* newIndex, Node* child, int z);
+    int addChildHelper(ParticleSystem* child, int z, int aTag, const std::string &name, bool setTag);
+    void addChildByTagOrName(ParticleSystem* child, int z, int tag, const std::string &name, bool setTag);
+    void updateBlendFunc();
+    /** the texture atlas used for drawing the quads */
+    TextureAtlas* _textureAtlas = nullptr;
+
+    /** the blend function used for drawing the quads */
+    BlendFunc _blendFunc;
+    
+    CustomCommand _customCommand;
+    
+    backend::UniformLocation _mvpMatrixLocaiton;
+    backend::UniformLocation _textureLocation;
+};
+
+// end of _2d group
+/// @}
+
+NS_CC_END

+ 1236 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParticleExamples.cpp

@@ -0,0 +1,1236 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+#include "2d/CCParticleExamples.h"
+#include "base/CCDirector.h"
+#include "base/firePngData.h"
+#include "renderer/CCTextureCache.h"
+
+NS_CC_BEGIN
+//
+// ParticleFire
+//
+
+static Texture2D* getDefaultTexture()
+{
+    Texture2D* texture = nullptr;
+    Image* image = nullptr;
+    do 
+    {
+        const std::string key = "/__firePngData";
+        texture = Director::getInstance()->getTextureCache()->getTextureForKey(key);
+        CC_BREAK_IF(texture != nullptr);
+
+        image = new (std::nothrow) Image();
+        CC_BREAK_IF(nullptr == image);
+        bool ret = image->initWithImageData(__firePngData, sizeof(__firePngData));
+        CC_BREAK_IF(!ret);
+
+        texture = Director::getInstance()->getTextureCache()->addImage(image, key);
+    } while (0);
+
+    CC_SAFE_RELEASE(image);
+
+    return texture;
+}
+
+ParticleFire* ParticleFire::create()
+{
+    ParticleFire* ret = new (std::nothrow) ParticleFire();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleFire* ParticleFire::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleFire* ret = new (std::nothrow) ParticleFire();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleFire::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
+    {
+        // duration
+        _duration = DURATION_INFINITY;
+
+        // Gravity Mode
+        this->_emitterMode = Mode::GRAVITY;
+
+        // Gravity Mode: gravity
+        this->modeA.gravity.setZero();
+
+        // Gravity Mode: radial acceleration
+        this->modeA.radialAccel = 0;
+        this->modeA.radialAccelVar = 0;
+
+        // Gravity Mode: speed of particles
+        this->modeA.speed = 60;
+        this->modeA.speedVar = 20;        
+
+        // starting angle
+        _angle = 90;
+        _angleVar = 10;
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2.0f, 60.0f);
+        this->_posVar.set(40.0f, 20.0f);
+
+        // life of particles
+        _life = 3;
+        _lifeVar = 0.25f;
+
+
+        // size, in pixels
+        _startSize = 54.0f;
+        _startSizeVar = 10.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per frame
+        _emissionRate = _totalParticles/_life;
+
+        // color of particles
+        _startColor.r = 0.76f;
+        _startColor.g = 0.25f;
+        _startColor.b = 0.12f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.0f;
+        _startColorVar.g = 0.0f;
+        _startColorVar.b = 0.0f;
+        _startColorVar.a = 0.0f;
+        _endColor.r = 0.0f;
+        _endColor.g = 0.0f;
+        _endColor.b = 0.0f;
+        _endColor.a = 1.0f;
+        _endColorVar.r = 0.0f;
+        _endColorVar.g = 0.0f;
+        _endColorVar.b = 0.0f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+        
+        // additive
+        this->setBlendAdditive(true);
+        return true;
+    }
+    return false;
+}
+//
+// ParticleFireworks
+//
+
+ParticleFireworks* ParticleFireworks::create()
+{
+    ParticleFireworks* ret = new (std::nothrow) ParticleFireworks();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleFireworks* ParticleFireworks::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleFireworks* ret = new (std::nothrow) ParticleFireworks();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleFireworks::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
+    {
+        // duration
+        _duration= DURATION_INFINITY;
+
+        // Gravity Mode
+        this->_emitterMode = Mode::GRAVITY;
+
+        // Gravity Mode: gravity
+        this->modeA.gravity.set(0.0f, -90.0f);
+
+        // Gravity Mode:  radial
+        this->modeA.radialAccel = 0.0f;
+        this->modeA.radialAccelVar = 0.0f;
+
+        //  Gravity Mode: speed of particles
+        this->modeA.speed = 180.0f;
+        this->modeA.speedVar = 50.0f;
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, winSize.height/2);
+
+        // angle
+        this->_angle= 90.0f;
+        this->_angleVar = 20.0f;
+
+        // life of particles
+        this->_life = 3.5f;
+        this->_lifeVar = 1.0f;
+
+        // emits per frame
+        this->_emissionRate = _totalParticles/_life;
+
+        // color of particles
+        _startColor.r = 0.5f;
+        _startColor.g = 0.5f;
+        _startColor.b = 0.5f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.5f;
+        _startColorVar.g = 0.5f;
+        _startColorVar.b = 0.5f;
+        _startColorVar.a = 0.1f;
+        _endColor.r = 0.1f;
+        _endColor.g = 0.1f;
+        _endColor.b = 0.1f;
+        _endColor.a = 0.2f;
+        _endColorVar.r = 0.1f;
+        _endColorVar.g = 0.1f;
+        _endColorVar.b = 0.1f;
+        _endColorVar.a = 0.2f;
+
+        // size, in pixels
+        _startSize = 8.0f;
+        _startSizeVar = 2.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+        // additive
+        this->setBlendAdditive(false);
+        return true;
+    }
+    return false;
+}
+//
+// ParticleSun
+//
+ParticleSun* ParticleSun::create()
+{
+    ParticleSun* ret = new (std::nothrow) ParticleSun();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleSun* ParticleSun::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleSun* ret = new (std::nothrow) ParticleSun();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleSun::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
+    {
+        // additive
+        this->setBlendAdditive(true);
+
+        // duration
+        _duration = DURATION_INFINITY;
+
+        // Gravity Mode
+        setEmitterMode(Mode::GRAVITY);
+
+        // Gravity Mode: gravity
+        setGravity(Vec2(0,0));
+
+        // Gravity mode: radial acceleration
+        setRadialAccel(0);
+        setRadialAccelVar(0);
+
+        // Gravity mode: speed of particles
+        setSpeed(20);
+        setSpeedVar(5);
+
+
+        // angle
+        _angle = 90;
+        _angleVar = 360;
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, winSize.height/2);
+        setPosVar(Vec2::ZERO);
+
+        // life of particles
+        _life = 1;
+        _lifeVar = 0.5f;
+
+        // size, in pixels
+        _startSize = 30.0f;
+        _startSizeVar = 10.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per seconds
+        _emissionRate = _totalParticles/_life;
+
+        // color of particles
+        _startColor.r = 0.76f;
+        _startColor.g = 0.25f;
+        _startColor.b = 0.12f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.0f;
+        _startColorVar.g = 0.0f;
+        _startColorVar.b = 0.0f;
+        _startColorVar.a = 0.0f;
+        _endColor.r = 0.0f;
+        _endColor.g = 0.0f;
+        _endColor.b = 0.0f;
+        _endColor.a = 1.0f;
+        _endColorVar.r = 0.0f;
+        _endColorVar.g = 0.0f;
+        _endColorVar.b = 0.0f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+
+        return true;
+    }
+    return false;
+}
+
+//
+// ParticleGalaxy
+//
+
+ParticleGalaxy* ParticleGalaxy::create()
+{
+    ParticleGalaxy* ret = new (std::nothrow) ParticleGalaxy();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleGalaxy* ParticleGalaxy::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleGalaxy* ret = new (std::nothrow) ParticleGalaxy();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleGalaxy::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
+    {
+        // duration
+        _duration = DURATION_INFINITY;
+
+        // Gravity Mode
+        setEmitterMode(Mode::GRAVITY);
+
+        // Gravity Mode: gravity
+        setGravity(Vec2(0,0));
+
+        // Gravity Mode: speed of particles
+        setSpeed(60);
+        setSpeedVar(10);
+
+        // Gravity Mode: radial
+        setRadialAccel(-80);
+        setRadialAccelVar(0);
+
+        // Gravity Mode: tangential
+        setTangentialAccel(80);
+        setTangentialAccelVar(0);
+
+        // angle
+        _angle = 90;
+        _angleVar = 360;
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, winSize.height/2);
+        setPosVar(Vec2::ZERO);
+
+        // life of particles
+        _life = 4;
+        _lifeVar = 1;
+
+        // size, in pixels
+        _startSize = 37.0f;
+        _startSizeVar = 10.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per second
+        _emissionRate = _totalParticles/_life;
+
+        // color of particles
+        _startColor.r = 0.12f;
+        _startColor.g = 0.25f;
+        _startColor.b = 0.76f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.0f;
+        _startColorVar.g = 0.0f;
+        _startColorVar.b = 0.0f;
+        _startColorVar.a = 0.0f;
+        _endColor.r = 0.0f;
+        _endColor.g = 0.0f;
+        _endColor.b = 0.0f;
+        _endColor.a = 1.0f;
+        _endColorVar.r = 0.0f;
+        _endColorVar.g = 0.0f;
+        _endColorVar.b = 0.0f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+
+        // additive
+        this->setBlendAdditive(true);
+        return true;
+    }
+    return false;
+}
+
+//
+// ParticleFlower
+//
+
+ParticleFlower* ParticleFlower::create()
+{
+    ParticleFlower* ret = new (std::nothrow) ParticleFlower();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleFlower* ParticleFlower::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleFlower* ret = new (std::nothrow) ParticleFlower();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleFlower::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
+    {
+        // duration
+        _duration = DURATION_INFINITY;
+
+        // Gravity Mode
+        setEmitterMode(Mode::GRAVITY);
+
+        // Gravity Mode: gravity
+        setGravity(Vec2(0,0));
+
+        // Gravity Mode: speed of particles
+        setSpeed(80);
+        setSpeedVar(10);
+
+        // Gravity Mode: radial
+        setRadialAccel(-60);
+        setRadialAccelVar(0);
+
+        // Gravity Mode: tangential
+        setTangentialAccel(15);
+        setTangentialAccelVar(0);
+
+        // angle
+        _angle = 90;
+        _angleVar = 360;
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, winSize.height/2);
+        setPosVar(Vec2::ZERO);
+
+        // life of particles
+        _life = 4;
+        _lifeVar = 1;
+
+        // size, in pixels
+        _startSize = 30.0f;
+        _startSizeVar = 10.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per second
+        _emissionRate = _totalParticles/_life;
+
+        // color of particles
+        _startColor.r = 0.50f;
+        _startColor.g = 0.50f;
+        _startColor.b = 0.50f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.5f;
+        _startColorVar.g = 0.5f;
+        _startColorVar.b = 0.5f;
+        _startColorVar.a = 0.5f;
+        _endColor.r = 0.0f;
+        _endColor.g = 0.0f;
+        _endColor.b = 0.0f;
+        _endColor.a = 1.0f;
+        _endColorVar.r = 0.0f;
+        _endColorVar.g = 0.0f;
+        _endColorVar.b = 0.0f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+
+        // additive
+        this->setBlendAdditive(true);
+        return true;
+    }
+    return false;
+}
+//
+// ParticleMeteor
+//
+
+ParticleMeteor * ParticleMeteor::create()
+{
+    ParticleMeteor *ret = new (std::nothrow) ParticleMeteor();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleMeteor* ParticleMeteor::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleMeteor* ret = new (std::nothrow) ParticleMeteor();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleMeteor::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
+    {
+        // duration
+        _duration = DURATION_INFINITY;
+
+        // Gravity Mode
+        setEmitterMode(Mode::GRAVITY);
+
+        // Gravity Mode: gravity
+        setGravity(Vec2(-200,200));
+
+        // Gravity Mode: speed of particles
+        setSpeed(15);
+        setSpeedVar(5);
+
+        // Gravity Mode: radial
+        setRadialAccel(0);
+        setRadialAccelVar(0);
+
+        // Gravity Mode: tangential
+        setTangentialAccel(0);
+        setTangentialAccelVar(0);
+
+        // angle
+        _angle = 90;
+        _angleVar = 360;
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, winSize.height/2);
+        setPosVar(Vec2::ZERO);
+
+        // life of particles
+        _life = 2;
+        _lifeVar = 1;
+
+        // size, in pixels
+        _startSize = 60.0f;
+        _startSizeVar = 10.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per second
+        _emissionRate = _totalParticles/_life;
+
+        // color of particles
+        _startColor.r = 0.2f;
+        _startColor.g = 0.4f;
+        _startColor.b = 0.7f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.0f;
+        _startColorVar.g = 0.0f;
+        _startColorVar.b = 0.2f;
+        _startColorVar.a = 0.1f;
+        _endColor.r = 0.0f;
+        _endColor.g = 0.0f;
+        _endColor.b = 0.0f;
+        _endColor.a = 1.0f;
+        _endColorVar.r = 0.0f;
+        _endColorVar.g = 0.0f;
+        _endColorVar.b = 0.0f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+
+        // additive
+        this->setBlendAdditive(true);
+        return true;
+    }
+    return false;
+}
+
+//
+// ParticleSpiral
+//
+
+ParticleSpiral* ParticleSpiral::create()
+{
+    ParticleSpiral* ret = new (std::nothrow) ParticleSpiral();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleSpiral* ParticleSpiral::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleSpiral* ret = new (std::nothrow) ParticleSpiral();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleSpiral::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) ) 
+    {
+        // duration
+        _duration = DURATION_INFINITY;
+
+        // Gravity Mode
+        setEmitterMode(Mode::GRAVITY);
+
+        // Gravity Mode: gravity
+        setGravity(Vec2(0,0));
+
+        // Gravity Mode: speed of particles
+        setSpeed(150);
+        setSpeedVar(0);
+
+        // Gravity Mode: radial
+        setRadialAccel(-380);
+        setRadialAccelVar(0);
+
+        // Gravity Mode: tangential
+        setTangentialAccel(45);
+        setTangentialAccelVar(0);
+
+        // angle
+        _angle = 90;
+        _angleVar = 0;
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, winSize.height/2);
+        setPosVar(Vec2::ZERO);
+
+        // life of particles
+        _life = 12;
+        _lifeVar = 0;
+
+        // size, in pixels
+        _startSize = 20.0f;
+        _startSizeVar = 0.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per second
+        _emissionRate = _totalParticles/_life;
+
+        // color of particles
+        _startColor.r = 0.5f;
+        _startColor.g = 0.5f;
+        _startColor.b = 0.5f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.5f;
+        _startColorVar.g = 0.5f;
+        _startColorVar.b = 0.5f;
+        _startColorVar.a = 0.0f;
+        _endColor.r = 0.5f;
+        _endColor.g = 0.5f;
+        _endColor.b = 0.5f;
+        _endColor.a = 1.0f;
+        _endColorVar.r = 0.5f;
+        _endColorVar.g = 0.5f;
+        _endColorVar.b = 0.5f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+
+        // additive
+        this->setBlendAdditive(false);
+        return true;
+    }
+    return false;
+}
+
+//
+// ParticleExplosion
+//
+
+ParticleExplosion* ParticleExplosion::create()
+{
+    ParticleExplosion* ret = new (std::nothrow) ParticleExplosion();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleExplosion* ParticleExplosion::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleExplosion* ret = new (std::nothrow) ParticleExplosion();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleExplosion::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) ) 
+    {
+        // duration
+        _duration = 0.1f;
+
+        setEmitterMode(Mode::GRAVITY);
+
+        // Gravity Mode: gravity
+        setGravity(Vec2(0,0));
+
+        // Gravity Mode: speed of particles
+        setSpeed(70);
+        setSpeedVar(40);
+
+        // Gravity Mode: radial
+        setRadialAccel(0);
+        setRadialAccelVar(0);
+
+        // Gravity Mode: tangential
+        setTangentialAccel(0);
+        setTangentialAccelVar(0);
+
+        // angle
+        _angle = 90;
+        _angleVar = 360;
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, winSize.height/2);
+        setPosVar(Vec2::ZERO);
+
+        // life of particles
+        _life = 5.0f;
+        _lifeVar = 2;
+
+        // size, in pixels
+        _startSize = 15.0f;
+        _startSizeVar = 10.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per second
+        _emissionRate = _totalParticles/_duration;
+
+        // color of particles
+        _startColor.r = 0.7f;
+        _startColor.g = 0.1f;
+        _startColor.b = 0.2f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.5f;
+        _startColorVar.g = 0.5f;
+        _startColorVar.b = 0.5f;
+        _startColorVar.a = 0.0f;
+        _endColor.r = 0.5f;
+        _endColor.g = 0.5f;
+        _endColor.b = 0.5f;
+        _endColor.a = 0.0f;
+        _endColorVar.r = 0.5f;
+        _endColorVar.g = 0.5f;
+        _endColorVar.b = 0.5f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+
+        // additive
+        this->setBlendAdditive(false);
+        return true;
+    }
+    return false;
+}
+
+//
+// ParticleSmoke
+//
+
+ParticleSmoke* ParticleSmoke::create()
+{
+    ParticleSmoke* ret = new (std::nothrow) ParticleSmoke();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleSmoke* ParticleSmoke::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleSmoke* ret = new (std::nothrow) ParticleSmoke();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleSmoke::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
+    {
+        // duration
+        _duration = DURATION_INFINITY;
+
+        // Emitter mode: Gravity Mode
+        setEmitterMode(Mode::GRAVITY);
+
+        // Gravity Mode: gravity
+        setGravity(Vec2(0,0));
+
+        // Gravity Mode: radial acceleration
+        setRadialAccel(0);
+        setRadialAccelVar(0);
+
+        // Gravity Mode: speed of particles
+        setSpeed(25);
+        setSpeedVar(10);
+
+        // angle
+        _angle = 90;
+        _angleVar = 5;
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, 0);
+        setPosVar(Vec2(20, 0));
+
+        // life of particles
+        _life = 4;
+        _lifeVar = 1;
+
+        // size, in pixels
+        _startSize = 60.0f;
+        _startSizeVar = 10.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per frame
+        _emissionRate = _totalParticles/_life;
+
+        // color of particles
+        _startColor.r = 0.8f;
+        _startColor.g = 0.8f;
+        _startColor.b = 0.8f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.02f;
+        _startColorVar.g = 0.02f;
+        _startColorVar.b = 0.02f;
+        _startColorVar.a = 0.0f;
+        _endColor.r = 0.0f;
+        _endColor.g = 0.0f;
+        _endColor.b = 0.0f;
+        _endColor.a = 1.0f;
+        _endColorVar.r = 0.0f;
+        _endColorVar.g = 0.0f;
+        _endColorVar.b = 0.0f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+
+        // additive
+        this->setBlendAdditive(false);
+        return true;
+    }
+    return false;
+}
+
+//
+// ParticleSnow
+//
+
+ParticleSnow* ParticleSnow::create()
+{
+    ParticleSnow* ret = new (std::nothrow) ParticleSnow();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleSnow* ParticleSnow::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleSnow* ret = new (std::nothrow) ParticleSnow();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleSnow::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) ) 
+    {
+        // duration
+        _duration = DURATION_INFINITY;
+
+        // set gravity mode.
+        setEmitterMode(Mode::GRAVITY);
+
+        // Gravity Mode: gravity
+        setGravity(Vec2(0,-1));
+
+        // Gravity Mode: speed of particles
+        setSpeed(5);
+        setSpeedVar(1);
+
+        // Gravity Mode: radial
+        setRadialAccel(0);
+        setRadialAccelVar(1);
+
+        // Gravity mode: tangential
+        setTangentialAccel(0);
+        setTangentialAccelVar(1);
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, winSize.height + 10);
+        setPosVar(Vec2(winSize.width/2, 0.0f));
+
+        // angle
+        _angle = -90;
+        _angleVar = 5;
+
+        // life of particles
+        _life = 45;
+        _lifeVar = 15;
+
+        // size, in pixels
+        _startSize = 10.0f;
+        _startSizeVar = 5.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per second
+        _emissionRate = 10;
+
+        // color of particles
+        _startColor.r = 1.0f;
+        _startColor.g = 1.0f;
+        _startColor.b = 1.0f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.0f;
+        _startColorVar.g = 0.0f;
+        _startColorVar.b = 0.0f;
+        _startColorVar.a = 0.0f;
+        _endColor.r = 1.0f;
+        _endColor.g = 1.0f;
+        _endColor.b = 1.0f;
+        _endColor.a = 0.0f;
+        _endColorVar.r = 0.0f;
+        _endColorVar.g = 0.0f;
+        _endColorVar.b = 0.0f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+
+        // additive
+        this->setBlendAdditive(false);
+        return true;
+    }
+    return false;
+}
+//
+// ParticleRain
+//
+
+ParticleRain* ParticleRain::create()
+{
+    ParticleRain* ret = new (std::nothrow) ParticleRain();
+    if (ret && ret->init())
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+ParticleRain* ParticleRain::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleRain* ret = new (std::nothrow) ParticleRain();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+    }
+    else
+    {
+        CC_SAFE_DELETE(ret);
+    }
+    return ret;
+}
+
+bool ParticleRain::initWithTotalParticles(int numberOfParticles)
+{
+    if( ParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
+    {
+        // duration
+        _duration = DURATION_INFINITY;
+
+        setEmitterMode(Mode::GRAVITY);
+
+        // Gravity Mode: gravity
+        setGravity(Vec2(10,-10));
+
+        // Gravity Mode: radial
+        setRadialAccel(0);
+        setRadialAccelVar(1);
+
+        // Gravity Mode: tangential
+        setTangentialAccel(0);
+        setTangentialAccelVar(1);
+
+        // Gravity Mode: speed of particles
+        setSpeed(130);
+        setSpeedVar(30);
+
+        // angle
+        _angle = -90;
+        _angleVar = 5;
+
+
+        // emitter position
+        Size winSize = Director::getInstance()->getWinSize();
+        this->setPosition(winSize.width/2, winSize.height);
+        setPosVar(Vec2(winSize.width/2, 0.0f));
+
+        // life of particles
+        _life = 4.5f;
+        _lifeVar = 0;
+
+        // size, in pixels
+        _startSize = 4.0f;
+        _startSizeVar = 2.0f;
+        _endSize = START_SIZE_EQUAL_TO_END_SIZE;
+
+        // emits per second
+        _emissionRate = 20;
+
+        // color of particles
+        _startColor.r = 0.7f;
+        _startColor.g = 0.8f;
+        _startColor.b = 1.0f;
+        _startColor.a = 1.0f;
+        _startColorVar.r = 0.0f;
+        _startColorVar.g = 0.0f;
+        _startColorVar.b = 0.0f;
+        _startColorVar.a = 0.0f;
+        _endColor.r = 0.7f;
+        _endColor.g = 0.8f;
+        _endColor.b = 1.0f;
+        _endColor.a = 0.5f;
+        _endColorVar.r = 0.0f;
+        _endColorVar.g = 0.0f;
+        _endColorVar.b = 0.0f;
+        _endColorVar.a = 0.0f;
+
+        Texture2D* texture = getDefaultTexture();
+        if (texture != nullptr)
+        {
+            setTexture(texture);
+        }
+
+        // additive
+        this->setBlendAdditive(false);
+        return true;
+    }
+    return false;
+}
+
+NS_CC_END

+ 456 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParticleExamples.h

@@ -0,0 +1,456 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __CCPARTICLE_EXAMPLE_H__
+#define __CCPARTICLE_EXAMPLE_H__
+
+#include "2d/CCParticleSystemQuad.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** @class ParticleFire
+ * @brief A fire particle system.
+ */
+class CC_DLL ParticleFire : public ParticleSystemQuad
+{
+public:
+    /** Create a fire particle system.
+     *
+     * @return An autoreleased ParticleFire object.
+     */
+    static ParticleFire* create();
+    /** Create a fire particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleFire object.
+     * @js NA
+     */
+    static ParticleFire* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleFire(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleFire(){}
+
+    bool init() override { return initWithTotalParticles(250); }
+    virtual bool initWithTotalParticles(int numberOfParticles) override;
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleFire);
+};
+
+/** @class ParticleFireworks
+ * @brief A fireworks particle system.
+ */
+class CC_DLL ParticleFireworks : public ParticleSystemQuad
+{
+public:
+    /** Create a fireworks particle system.
+     *
+     * @return An autoreleased ParticleFireworks object.
+     */
+    static ParticleFireworks* create();
+    /** Create a fireworks particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleFireworks object.
+     * @js NA
+     */
+    static ParticleFireworks* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleFireworks(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleFireworks(){}
+
+    bool init(){ return initWithTotalParticles(1500); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleFireworks);
+
+};
+
+/** @class ParticleSun
+ * @brief A sun particle system.
+ */
+class CC_DLL ParticleSun : public ParticleSystemQuad
+{
+public:
+    /** Create a sun particle system.
+     *
+     * @return An autoreleased ParticleSun object.
+     */
+    static ParticleSun* create();
+    /** Create a sun particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleSun object.
+     * @js NA
+     */
+    static ParticleSun* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleSun(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleSun(){}
+
+    bool init(){ return initWithTotalParticles(350); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleSun);
+
+};
+
+/** @class ParticleGalaxy
+ * @brief A galaxy particle system.
+ */
+class CC_DLL ParticleGalaxy : public ParticleSystemQuad
+{
+public:
+    /** Create a galaxy particle system.
+     *
+     * @return An autoreleased ParticleGalaxy object.
+     */
+    static ParticleGalaxy* create();
+    /** Create a galaxy particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleGalaxy object.
+     * @js NA
+     */
+    static ParticleGalaxy* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleGalaxy(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleGalaxy(){}
+
+    bool init(){ return initWithTotalParticles(200); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleGalaxy);
+
+};
+
+/** @class ParticleFlower
+ * @brief A flower particle system.
+ */
+class CC_DLL ParticleFlower : public ParticleSystemQuad
+{
+public:
+    /** Create a flower particle system.
+     *
+     * @return An autoreleased ParticleFlower object.
+     */
+    static ParticleFlower* create();
+    /** Create a flower particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleFlower object.
+     * @js NA
+     */
+    static ParticleFlower* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleFlower(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleFlower(){}
+
+    bool init(){ return initWithTotalParticles(250); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleFlower);
+};
+
+/** @class ParticleMeteor
+ * @brief A meteor particle system.
+ */
+class CC_DLL ParticleMeteor : public ParticleSystemQuad
+{
+public:
+    /** Create a meteor particle system.
+     *
+     * @return An autoreleased ParticleMeteor object.
+     */
+    static ParticleMeteor * create();
+    /** Create a meteor particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleMeteor object.
+     * @js NA
+     */
+    static ParticleMeteor* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleMeteor(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleMeteor(){}
+
+    bool init(){ return initWithTotalParticles(150); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleMeteor);
+};
+
+/** @class ParticleSpiral
+ * @brief An spiral particle system.
+ */
+class CC_DLL ParticleSpiral : public ParticleSystemQuad
+{
+public:
+    /** Create a spiral particle system.
+     *
+     * @return An autoreleased ParticleSpiral object.
+     */
+    static ParticleSpiral* create();
+    /** Create a spiral particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleSpiral object.
+     * @js NA
+     */
+    static ParticleSpiral* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleSpiral(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleSpiral(){}
+
+    bool init(){ return initWithTotalParticles(500); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleSpiral);
+
+};
+
+/** @class ParticleExplosion
+ * @brief An explosion particle system.
+ */
+class CC_DLL ParticleExplosion : public ParticleSystemQuad
+{
+public:
+    /** Create a explosion particle system.
+     *
+     * @return An autoreleased ParticleExplosion object.
+     */
+    static ParticleExplosion* create();
+    /** Create a explosion particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleExplosion object.
+     * @js NA
+     */
+    static ParticleExplosion* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleExplosion(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleExplosion(){}
+
+    bool init(){ return initWithTotalParticles(700); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleExplosion);
+};
+
+/** @class ParticleSmoke
+ * @brief An smoke particle system.
+ */
+class CC_DLL ParticleSmoke : public ParticleSystemQuad
+{
+public:
+    /** Create a smoke particle system.
+     *
+     * @return An autoreleased ParticleSmoke object.
+     */
+    static ParticleSmoke* create();
+    /** Create a smoke particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleSmoke object.
+     * @js NA
+     */
+    static ParticleSmoke* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleSmoke(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleSmoke(){}
+
+    bool init(){ return initWithTotalParticles(200); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleSmoke);
+};
+
+/** @class ParticleSnow
+ * @brief An snow particle system.
+ */
+class CC_DLL ParticleSnow : public ParticleSystemQuad
+{
+public:
+    /** Create a snow particle system.
+     *
+     * @return An autoreleased ParticleSnow object.
+     */
+    static ParticleSnow* create();
+    /** Create a snow particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleSnow object.
+     * @js NA
+     */
+    static ParticleSnow* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleSnow(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleSnow(){}
+
+    bool init(){ return initWithTotalParticles(700); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleSnow);
+};
+
+/** @class ParticleRain
+ * @brief A rain particle system.
+ */
+class CC_DLL ParticleRain : public ParticleSystemQuad
+{
+public:
+    /** Create a rain particle system.
+     *
+     * @return An autoreleased ParticleRain object.
+     */
+    static ParticleRain* create();
+    /** Create a rain particle system withe a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleRain object.
+     * @js NA
+     */
+    static ParticleRain* createWithTotalParticles(int numberOfParticles);
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleRain(){}
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleRain(){}
+
+    bool init(){ return initWithTotalParticles(1000); }
+    virtual bool initWithTotalParticles(int numberOfParticles);
+
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleRain);
+};
+
+// end of _2d group
+/// @}
+
+NS_CC_END
+
+#endif //__CCPARTICLE_EXAMPLE_H__

+ 1404 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParticleSystem.cpp

@@ -0,0 +1,1404 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+
+// ideas taken from:
+//     . The ocean spray in your face [Jeff Lander]
+//        http://www.double.co.nz/dust/col0798.pdf
+//     . Building an Advanced Particle System [John van der Burg]
+//        http://www.gamasutra.com/features/20000623/vanderburg_01.htm
+//   . LOVE game engine
+//        http://love2d.org/
+//
+//
+// Radius mode support, from 71 squared
+//        http://particledesigner.71squared.com/
+//
+// IMPORTANT: Particle Designer is supported by cocos2d, but
+// 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guaranteed in cocos2d,
+//  cocos2d uses a another approach, but the results are almost identical. 
+//
+
+#include "2d/CCParticleSystem.h"
+
+#include <string>
+
+#include "2d/CCParticleBatchNode.h"
+#include "renderer/CCTextureAtlas.h"
+#include "base/base64.h"
+#include "base/ZipUtils.h"
+#include "base/CCDirector.h"
+#include "base/CCProfiling.h"
+#include "base/ccUTF8.h"
+#include "base/ccUtils.h"
+#include "renderer/CCTextureCache.h"
+#include "platform/CCFileUtils.h"
+
+using namespace std;
+
+NS_CC_BEGIN
+
+// ideas taken from:
+//     . The ocean spray in your face [Jeff Lander]
+//        http://www.double.co.nz/dust/col0798.pdf
+//     . Building an Advanced Particle System [John van der Burg]
+//        http://www.gamasutra.com/features/20000623/vanderburg_01.htm
+//   . LOVE game engine
+//        http://love2d.org/
+//
+//
+// Radius mode support, from 71 squared
+//        http://particledesigner.71squared.com/
+//
+// IMPORTANT: Particle Designer is supported by cocos2d, but
+// 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guaranteed in cocos2d,
+//  cocos2d uses a another approach, but the results are almost identical. 
+//
+
+
+inline void normalize_point(float x, float y, particle_point* out)
+{
+    float n = x * x + y * y;
+    // Already normalized.
+    if (n == 1.0f)
+        return;
+    
+    n = sqrt(n);
+    // Too close to zero.
+    if (n < MATH_TOLERANCE)
+        return;
+    
+    n = 1.0f / n;
+    out->x = x * n;
+    out->y = y * n;
+}
+
+/**
+ A more effect random number getter function, get from ejoy2d.
+ */
+inline static float RANDOM_M11(unsigned int *seed) {
+    *seed = *seed * 134775813 + 1;
+    union {
+        uint32_t d;                                     
+        float f;
+    } u;
+    u.d = (((uint32_t)(*seed) & 0x7fff) << 8) | 0x40000000;
+    return u.f - 3.0f;
+}
+
+ParticleData::ParticleData()
+{
+    memset(this, 0, sizeof(ParticleData));
+}
+
+bool ParticleData::init(int count)
+{
+    maxCount = count;
+    
+    posx= (float*)malloc(count * sizeof(float));
+    posy= (float*)malloc(count * sizeof(float));
+    startPosX= (float*)malloc(count * sizeof(float));
+    startPosY= (float*)malloc(count * sizeof(float));
+    colorR= (float*)malloc(count * sizeof(float));
+    colorG= (float*)malloc(count * sizeof(float));
+    colorB= (float*)malloc(count * sizeof(float));
+    colorA= (float*)malloc(count * sizeof(float));
+    deltaColorR= (float*)malloc(count * sizeof(float));
+    deltaColorG= (float*)malloc(count * sizeof(float));
+    deltaColorB= (float*)malloc(count * sizeof(float));
+    deltaColorA= (float*)malloc(count * sizeof(float));
+    size= (float*)malloc(count * sizeof(float));
+    deltaSize= (float*)malloc(count * sizeof(float));
+    rotation= (float*)malloc(count * sizeof(float));
+    deltaRotation= (float*)malloc(count * sizeof(float));
+    timeToLive= (float*)malloc(count * sizeof(float));
+    atlasIndex= (unsigned int*)malloc(count * sizeof(unsigned int));
+    
+    modeA.dirX= (float*)malloc(count * sizeof(float));
+    modeA.dirY= (float*)malloc(count * sizeof(float));
+    modeA.radialAccel= (float*)malloc(count * sizeof(float));
+    modeA.tangentialAccel= (float*)malloc(count * sizeof(float));
+    
+    modeB.angle= (float*)malloc(count * sizeof(float));
+    modeB.degreesPerSecond= (float*)malloc(count * sizeof(float));
+    modeB.deltaRadius= (float*)malloc(count * sizeof(float));
+    modeB.radius= (float*)malloc(count * sizeof(float));
+    
+    return posx && posy && startPosY && startPosX && colorR && colorG && colorB && colorA &&
+    deltaColorR && deltaColorG && deltaColorB && deltaColorA && size && deltaSize &&
+    rotation && deltaRotation && timeToLive && atlasIndex && modeA.dirX && modeA.dirY &&
+    modeA.radialAccel && modeA.tangentialAccel && modeB.angle && modeB.degreesPerSecond &&
+    modeB.deltaRadius && modeB.radius;
+}
+
+void ParticleData::release()
+{
+    CC_SAFE_FREE(posx);
+    CC_SAFE_FREE(posy);
+    CC_SAFE_FREE(startPosX);
+    CC_SAFE_FREE(startPosY);
+    CC_SAFE_FREE(colorR);
+    CC_SAFE_FREE(colorG);
+    CC_SAFE_FREE(colorB);
+    CC_SAFE_FREE(colorA);
+    CC_SAFE_FREE(deltaColorR);
+    CC_SAFE_FREE(deltaColorG);
+    CC_SAFE_FREE(deltaColorB);
+    CC_SAFE_FREE(deltaColorA);
+    CC_SAFE_FREE(size);
+    CC_SAFE_FREE(deltaSize);
+    CC_SAFE_FREE(rotation);
+    CC_SAFE_FREE(deltaRotation);
+    CC_SAFE_FREE(timeToLive);
+    CC_SAFE_FREE(atlasIndex);
+    
+    CC_SAFE_FREE(modeA.dirX);
+    CC_SAFE_FREE(modeA.dirY);
+    CC_SAFE_FREE(modeA.radialAccel);
+    CC_SAFE_FREE(modeA.tangentialAccel);
+    
+    CC_SAFE_FREE(modeB.angle);
+    CC_SAFE_FREE(modeB.degreesPerSecond);
+    CC_SAFE_FREE(modeB.deltaRadius);
+    CC_SAFE_FREE(modeB.radius);
+}
+
+Vector<ParticleSystem*> ParticleSystem::__allInstances;
+float ParticleSystem::__totalParticleCountFactor = 1.0f;
+
+ParticleSystem::ParticleSystem()
+: _isBlendAdditive(false)
+, _isAutoRemoveOnFinish(false)
+, _plistFile("")
+, _elapsed(0)
+, _configName("")
+, _emitCounter(0)
+, _batchNode(nullptr)
+, _atlasIndex(0)
+, _transformSystemDirty(false)
+, _allocatedParticles(0)
+, _isActive(true)
+, _particleCount(0)
+, _duration(0)
+, _life(0)
+, _lifeVar(0)
+, _angle(0)
+, _angleVar(0)
+, _emitterMode(Mode::GRAVITY)
+, _startSize(0)
+, _startSizeVar(0)
+, _endSize(0)
+, _endSizeVar(0)
+, _startSpin(0)
+, _startSpinVar(0)
+, _endSpin(0)
+, _endSpinVar(0)
+, _emissionRate(0)
+, _totalParticles(0)
+, _texture(nullptr)
+, _blendFunc(BlendFunc::ALPHA_PREMULTIPLIED)
+, _opacityModifyRGB(false)
+, _yCoordFlipped(1)
+, _positionType(PositionType::FREE)
+, _paused(false)
+, _sourcePositionCompatible(true) // In the furture this member's default value maybe false or be removed.
+{
+    modeA.gravity.setZero();
+    modeA.speed = 0;
+    modeA.speedVar = 0;
+    modeA.tangentialAccel = 0;
+    modeA.tangentialAccelVar = 0;
+    modeA.radialAccel = 0;
+    modeA.radialAccelVar = 0;
+    modeA.rotationIsDir = false;
+    modeB.startRadius = 0;
+    modeB.startRadiusVar = 0;
+    modeB.endRadius = 0;
+    modeB.endRadiusVar = 0;            
+    modeB.rotatePerSecond = 0;
+    modeB.rotatePerSecondVar = 0;
+}
+// implementation ParticleSystem
+
+ParticleSystem * ParticleSystem::create(const std::string& plistFile)
+{
+    ParticleSystem *ret = new (std::nothrow) ParticleSystem();
+    if (ret && ret->initWithFile(plistFile))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return ret;
+}
+
+ParticleSystem* ParticleSystem::createWithTotalParticles(int numberOfParticles)
+{
+    ParticleSystem *ret = new (std::nothrow) ParticleSystem();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return ret;
+}
+
+// static
+Vector<ParticleSystem*>& ParticleSystem::getAllParticleSystems()
+{
+    return __allInstances;
+}
+
+void ParticleSystem::setTotalParticleCountFactor(float factor)
+{
+    __totalParticleCountFactor = factor;
+}
+
+bool ParticleSystem::init()
+{
+    return initWithTotalParticles(150);
+}
+
+bool ParticleSystem::initWithFile(const std::string& plistFile)
+{
+    bool ret = false;
+    _plistFile = FileUtils::getInstance()->fullPathForFilename(plistFile);
+    ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(_plistFile);
+
+    CCASSERT( !dict.empty(), "Particles: file not found");
+    
+    // FIXME: compute path from a path, should define a function somewhere to do it
+    string listFilePath = plistFile;
+    if (listFilePath.find('/') != string::npos)
+    {
+        listFilePath = listFilePath.substr(0, listFilePath.rfind('/') + 1);
+        ret = this->initWithDictionary(dict, listFilePath);
+    }
+    else
+    {
+        ret = this->initWithDictionary(dict, "");
+    }
+    
+    return ret;
+}
+
+bool ParticleSystem::initWithDictionary(ValueMap& dictionary)
+{
+    return initWithDictionary(dictionary, "");
+}
+
+bool ParticleSystem::initWithDictionary(ValueMap& dictionary, const std::string& dirname)
+{
+    bool ret = false;
+    unsigned char *buffer = nullptr;
+    unsigned char *deflated = nullptr;
+    Image *image = nullptr;
+    do 
+    {
+        int maxParticles = dictionary["maxParticles"].asInt();
+        // self, not super
+        if(this->initWithTotalParticles(maxParticles))
+        {
+            // Emitter name in particle designer 2.0
+            _configName = dictionary["configName"].asString();
+
+            // angle
+            _angle = dictionary["angle"].asFloat();
+            _angleVar = dictionary["angleVariance"].asFloat();
+
+            // duration
+            _duration = dictionary["duration"].asFloat();
+
+            // blend function 
+            if (!_configName.empty())
+            {
+                _blendFunc.src = utils::toBackendBlendFactor((int)dictionary["blendFuncSource"].asFloat());
+            }
+            else
+            {
+                _blendFunc.src = utils::toBackendBlendFactor(dictionary["blendFuncSource"].asInt());
+            }
+            _blendFunc.dst = utils::toBackendBlendFactor(dictionary["blendFuncDestination"].asInt());
+
+            // color
+            _startColor.r = dictionary["startColorRed"].asFloat();
+            _startColor.g = dictionary["startColorGreen"].asFloat();
+            _startColor.b = dictionary["startColorBlue"].asFloat();
+            _startColor.a = dictionary["startColorAlpha"].asFloat();
+
+            _startColorVar.r = dictionary["startColorVarianceRed"].asFloat();
+            _startColorVar.g = dictionary["startColorVarianceGreen"].asFloat();
+            _startColorVar.b = dictionary["startColorVarianceBlue"].asFloat();
+            _startColorVar.a = dictionary["startColorVarianceAlpha"].asFloat();
+
+            _endColor.r = dictionary["finishColorRed"].asFloat();
+            _endColor.g = dictionary["finishColorGreen"].asFloat();
+            _endColor.b = dictionary["finishColorBlue"].asFloat();
+            _endColor.a = dictionary["finishColorAlpha"].asFloat();
+
+            _endColorVar.r = dictionary["finishColorVarianceRed"].asFloat();
+            _endColorVar.g = dictionary["finishColorVarianceGreen"].asFloat();
+            _endColorVar.b = dictionary["finishColorVarianceBlue"].asFloat();
+            _endColorVar.a = dictionary["finishColorVarianceAlpha"].asFloat();
+
+            // particle size
+            _startSize = dictionary["startParticleSize"].asFloat();
+            _startSizeVar = dictionary["startParticleSizeVariance"].asFloat();
+            _endSize = dictionary["finishParticleSize"].asFloat();
+            _endSizeVar = dictionary["finishParticleSizeVariance"].asFloat();
+
+            // position
+            float x = dictionary["sourcePositionx"].asFloat();
+            float y = dictionary["sourcePositiony"].asFloat();
+	    if(!_sourcePositionCompatible) {
+                this->setSourcePosition(Vec2(x, y));
+	    }
+            else {
+		this->setPosition(Vec2(x, y));
+	    }
+            _posVar.x = dictionary["sourcePositionVariancex"].asFloat();
+            _posVar.y = dictionary["sourcePositionVariancey"].asFloat();
+
+            // Spinning
+            _startSpin = dictionary["rotationStart"].asFloat();
+            _startSpinVar = dictionary["rotationStartVariance"].asFloat();
+            _endSpin= dictionary["rotationEnd"].asFloat();
+            _endSpinVar= dictionary["rotationEndVariance"].asFloat();
+
+            _emitterMode = (Mode) dictionary["emitterType"].asInt();
+
+            // Mode A: Gravity + tangential accel + radial accel
+            if (_emitterMode == Mode::GRAVITY)
+            {
+                // gravity
+                modeA.gravity.x = dictionary["gravityx"].asFloat();
+                modeA.gravity.y = dictionary["gravityy"].asFloat();
+
+                // speed
+                modeA.speed = dictionary["speed"].asFloat();
+                modeA.speedVar = dictionary["speedVariance"].asFloat();
+
+                // radial acceleration
+                modeA.radialAccel = dictionary["radialAcceleration"].asFloat();
+                modeA.radialAccelVar = dictionary["radialAccelVariance"].asFloat();
+
+                // tangential acceleration
+                modeA.tangentialAccel = dictionary["tangentialAcceleration"].asFloat();
+                modeA.tangentialAccelVar = dictionary["tangentialAccelVariance"].asFloat();
+                
+                // rotation is dir
+                modeA.rotationIsDir = dictionary["rotationIsDir"].asBool();
+            }
+
+            // or Mode B: radius movement
+            else if (_emitterMode == Mode::RADIUS)
+            {
+                if (!_configName.empty())
+                {
+                    modeB.startRadius = dictionary["maxRadius"].asInt();
+                }
+                else
+                {
+                    modeB.startRadius = dictionary["maxRadius"].asFloat();
+                }
+                modeB.startRadiusVar = dictionary["maxRadiusVariance"].asFloat();
+                if (!_configName.empty())
+                {
+                    modeB.endRadius = dictionary["minRadius"].asInt();
+                }
+                else
+                {
+                    modeB.endRadius = dictionary["minRadius"].asFloat();
+                }
+                
+                if (dictionary.find("minRadiusVariance") != dictionary.end())
+                {
+                    modeB.endRadiusVar = dictionary["minRadiusVariance"].asFloat();
+                }
+                else
+                {
+                    modeB.endRadiusVar = 0.0f;
+                }
+                
+                if (!_configName.empty())
+                {
+                    modeB.rotatePerSecond = dictionary["rotatePerSecond"].asInt();
+                }
+                else
+                {
+                    modeB.rotatePerSecond = dictionary["rotatePerSecond"].asFloat();
+                }
+                modeB.rotatePerSecondVar = dictionary["rotatePerSecondVariance"].asFloat();
+
+            } else {
+                CCASSERT( false, "Invalid emitterType in config file");
+                CC_BREAK_IF(true);
+            }
+
+            // life span
+            _life = dictionary["particleLifespan"].asFloat();
+            _lifeVar = dictionary["particleLifespanVariance"].asFloat();
+
+            // emission Rate
+            _emissionRate = _totalParticles / _life;
+
+            //don't get the internal texture if a batchNode is used
+            if (!_batchNode)
+            {
+                // Set a compatible default for the alpha transfer
+                _opacityModifyRGB = false;
+
+                // texture        
+                // Try to get the texture from the cache
+                std::string textureName = dictionary["textureFileName"].asString();
+                
+                size_t rPos = textureName.rfind('/');
+               
+                if (rPos != string::npos)
+                {
+                    string textureDir = textureName.substr(0, rPos + 1);
+                    
+                    if (!dirname.empty() && textureDir != dirname)
+                    {
+                        textureName = textureName.substr(rPos+1);
+                        textureName = dirname + textureName;
+                    }
+                }
+                else if (!dirname.empty() && !textureName.empty())
+                {
+                	textureName = dirname + textureName;
+                }
+                
+                Texture2D *tex = nullptr;
+                
+                if (!textureName.empty())
+                {
+                    // set not pop-up message box when load image failed
+                    bool notify = FileUtils::getInstance()->isPopupNotify();
+                    FileUtils::getInstance()->setPopupNotify(false);
+                    tex = Director::getInstance()->getTextureCache()->addImage(textureName);
+                    // reset the value of UIImage notify
+                    FileUtils::getInstance()->setPopupNotify(notify);
+                }
+                
+                if (tex)
+                {
+                    setTexture(tex);
+                }
+                else if( dictionary.find("textureImageData") != dictionary.end() )
+                {                        
+                    std::string textureData = dictionary.at("textureImageData").asString();
+                    CCASSERT(!textureData.empty(), "textureData can't be empty!");
+                    
+                    auto dataLen = textureData.size();
+                    if (dataLen != 0)
+                    {
+                        // if it fails, try to get it from the base64-gzipped data    
+                        int decodeLen = base64Decode((unsigned char*)textureData.c_str(), (unsigned int)dataLen, &buffer);
+                        CCASSERT( buffer != nullptr, "CCParticleSystem: error decoding textureImageData");
+                        CC_BREAK_IF(!buffer);
+                        
+                        ssize_t deflatedLen = ZipUtils::inflateMemory(buffer, decodeLen, &deflated);
+                        CCASSERT( deflated != nullptr, "CCParticleSystem: error ungzipping textureImageData");
+                        CC_BREAK_IF(!deflated);
+                        
+                        // For android, we should retain it in VolatileTexture::addImage which invoked in Director::getInstance()->getTextureCache()->addUIImage()
+                        image = new (std::nothrow) Image();
+                        bool isOK = image->initWithImageData(deflated, deflatedLen);
+                        CCASSERT(isOK, "CCParticleSystem: error init image with Data");
+                        CC_BREAK_IF(!isOK);
+                        
+                        setTexture(Director::getInstance()->getTextureCache()->addImage(image, _plistFile + textureName));
+
+                        image->release();
+                    }
+                }
+                
+                _yCoordFlipped = dictionary.find("yCoordFlipped") == dictionary.end() ? 1 : dictionary.at("yCoordFlipped").asInt();
+
+                if( !this->_texture)
+                    CCLOGWARN("cocos2d: Warning: ParticleSystemQuad system without a texture");
+            }
+            ret = true;
+        }
+    } while (0);
+    free(buffer);
+    free(deflated);
+    return ret;
+}
+
+bool ParticleSystem::initWithTotalParticles(int numberOfParticles)
+{
+    _totalParticles = numberOfParticles;
+    
+    _particleData.release();
+
+    if( !_particleData.init(_totalParticles) )
+    {
+        CCLOG("Particle system: not enough memory");
+        this->release();
+        return false;
+    }
+    _allocatedParticles = numberOfParticles;
+
+    if (_batchNode)
+    {
+        for (int i = 0; i < _totalParticles; i++)
+        {
+            _particleData.atlasIndex[i] = i;
+        }
+    }
+    // default, active
+    _isActive = true;
+
+    // default blend function
+    _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+
+    // default movement type;
+    _positionType = PositionType::FREE;
+
+    // by default be in mode A:
+    _emitterMode = Mode::GRAVITY;
+
+    // default: modulate
+    // FIXME:: not used
+    //    colorModulate = YES;
+
+    _isAutoRemoveOnFinish = false;
+
+    // Optimization: compile updateParticle method
+    //updateParticleSel = @selector(updateQuadWithParticle:newPosition:);
+    //updateParticleImp = (CC_UPDATE_PARTICLE_IMP) [self methodForSelector:updateParticleSel];
+    //for batchNode
+    _transformSystemDirty = false;
+
+    return true;
+}
+
+ParticleSystem::~ParticleSystem()
+{
+    // Since the scheduler retains the "target (in this case the ParticleSystem)
+	// it is not needed to call "unscheduleUpdate" here. In fact, it will be called in "cleanup"
+    //unscheduleUpdate();
+    _particleData.release();
+    CC_SAFE_RELEASE(_texture);
+}
+
+void ParticleSystem::addParticles(int count)
+{
+    if (_paused)
+        return;
+    uint32_t RANDSEED = rand();
+
+    int start = _particleCount;
+    _particleCount += count;
+    
+    //life
+    for (int i = start; i < _particleCount ; ++i)
+    {
+        float theLife = _life + _lifeVar * RANDOM_M11(&RANDSEED);
+        _particleData.timeToLive[i] = MAX(0, theLife);
+    }
+    
+    //position
+    for (int i = start; i < _particleCount; ++i)
+    {
+        _particleData.posx[i] = _sourcePosition.x + _posVar.x * RANDOM_M11(&RANDSEED);
+    }
+    
+    for (int i = start; i < _particleCount; ++i)
+    {
+        _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_M11(&RANDSEED);
+    }
+    
+    //color
+#define SET_COLOR(c, b, v)\
+for (int i = start; i < _particleCount; ++i)\
+{\
+c[i] = clampf( b + v * RANDOM_M11(&RANDSEED) , 0 , 1 );\
+}
+    
+    SET_COLOR(_particleData.colorR, _startColor.r, _startColorVar.r);
+    SET_COLOR(_particleData.colorG, _startColor.g, _startColorVar.g);
+    SET_COLOR(_particleData.colorB, _startColor.b, _startColorVar.b);
+    SET_COLOR(_particleData.colorA, _startColor.a, _startColorVar.a);
+    
+    SET_COLOR(_particleData.deltaColorR, _endColor.r, _endColorVar.r);
+    SET_COLOR(_particleData.deltaColorG, _endColor.g, _endColorVar.g);
+    SET_COLOR(_particleData.deltaColorB, _endColor.b, _endColorVar.b);
+    SET_COLOR(_particleData.deltaColorA, _endColor.a, _endColorVar.a);
+    
+#define SET_DELTA_COLOR(c, dc)\
+for (int i = start; i < _particleCount; ++i)\
+{\
+dc[i] = (dc[i] - c[i]) / _particleData.timeToLive[i];\
+}
+    
+    SET_DELTA_COLOR(_particleData.colorR, _particleData.deltaColorR);
+    SET_DELTA_COLOR(_particleData.colorG, _particleData.deltaColorG);
+    SET_DELTA_COLOR(_particleData.colorB, _particleData.deltaColorB);
+    SET_DELTA_COLOR(_particleData.colorA, _particleData.deltaColorA);
+    
+    //size
+    for (int i = start; i < _particleCount; ++i)
+    {
+        _particleData.size[i] = _startSize + _startSizeVar * RANDOM_M11(&RANDSEED);
+        _particleData.size[i] = MAX(0, _particleData.size[i]);
+    }
+    
+    if (_endSize != START_SIZE_EQUAL_TO_END_SIZE)
+    {
+        for (int i = start; i < _particleCount; ++i)
+        {
+            float endSize = _endSize + _endSizeVar * RANDOM_M11(&RANDSEED);
+            endSize = MAX(0, endSize);
+            _particleData.deltaSize[i] = (endSize - _particleData.size[i]) / _particleData.timeToLive[i];
+        }
+    }
+    else
+    {
+        for (int i = start; i < _particleCount; ++i)
+        {
+            _particleData.deltaSize[i] = 0.0f;
+        }
+    }
+    
+    // rotation
+    for (int i = start; i < _particleCount; ++i)
+    {
+        _particleData.rotation[i] = _startSpin + _startSpinVar * RANDOM_M11(&RANDSEED);
+    }
+    for (int i = start; i < _particleCount; ++i)
+    {
+        float endA = _endSpin + _endSpinVar * RANDOM_M11(&RANDSEED);
+        _particleData.deltaRotation[i] = (endA - _particleData.rotation[i]) / _particleData.timeToLive[i];
+    }
+    
+    // position
+    Vec2 pos;
+    if (_positionType == PositionType::FREE)
+    {
+        pos = this->convertToWorldSpace(Vec2::ZERO);
+    }
+    else if (_positionType == PositionType::RELATIVE)
+    {
+        pos = _position;
+    }
+    for (int i = start; i < _particleCount; ++i)
+    {
+        _particleData.startPosX[i] = pos.x;
+    }
+    for (int i = start; i < _particleCount; ++i)
+    {
+        _particleData.startPosY[i] = pos.y;
+    }
+    
+    // Mode Gravity: A
+    if (_emitterMode == Mode::GRAVITY)
+    {
+        
+        // radial accel
+        for (int i = start; i < _particleCount; ++i)
+        {
+            _particleData.modeA.radialAccel[i] = modeA.radialAccel + modeA.radialAccelVar * RANDOM_M11(&RANDSEED);
+        }
+        
+        // tangential accel
+        for (int i = start; i < _particleCount; ++i)
+        {
+            _particleData.modeA.tangentialAccel[i] = modeA.tangentialAccel + modeA.tangentialAccelVar * RANDOM_M11(&RANDSEED);
+        }
+        
+        // rotation is dir
+        if( modeA.rotationIsDir )
+        {
+            for (int i = start; i < _particleCount; ++i)
+            {
+                float a = CC_DEGREES_TO_RADIANS( _angle + _angleVar * RANDOM_M11(&RANDSEED) );
+                Vec2 v(cosf( a ), sinf( a ));
+                float s = modeA.speed + modeA.speedVar * RANDOM_M11(&RANDSEED);
+                Vec2 dir = v * s;
+                _particleData.modeA.dirX[i] = dir.x;//v * s ;
+                _particleData.modeA.dirY[i] = dir.y;
+                _particleData.rotation[i] = -CC_RADIANS_TO_DEGREES(dir.getAngle());
+            }
+        }
+        else
+        {
+            for (int i = start; i < _particleCount; ++i)
+            {
+                float a = CC_DEGREES_TO_RADIANS( _angle + _angleVar * RANDOM_M11(&RANDSEED) );
+                Vec2 v(cosf( a ), sinf( a ));
+                float s = modeA.speed + modeA.speedVar * RANDOM_M11(&RANDSEED);
+                Vec2 dir = v * s;
+                _particleData.modeA.dirX[i] = dir.x;//v * s ;
+                _particleData.modeA.dirY[i] = dir.y;
+            }
+        }
+        
+    }
+    
+    // Mode Radius: B
+    else
+    {
+        //Need to check by Jacky
+        // Set the default diameter of the particle from the source position
+        for (int i = start; i < _particleCount; ++i)
+        {
+            _particleData.modeB.radius[i] = modeB.startRadius + modeB.startRadiusVar * RANDOM_M11(&RANDSEED);
+        }
+
+        for (int i = start; i < _particleCount; ++i)
+        {
+            _particleData.modeB.angle[i] = CC_DEGREES_TO_RADIANS( _angle + _angleVar * RANDOM_M11(&RANDSEED));
+        }
+        
+        for (int i = start; i < _particleCount; ++i)
+        {
+            _particleData.modeB.degreesPerSecond[i] = CC_DEGREES_TO_RADIANS(modeB.rotatePerSecond + modeB.rotatePerSecondVar * RANDOM_M11(&RANDSEED));
+        }
+        
+        if(modeB.endRadius == START_RADIUS_EQUAL_TO_END_RADIUS)
+        {
+            for (int i = start; i < _particleCount; ++i)
+            {
+                _particleData.modeB.deltaRadius[i] = 0.0f;
+            }
+        }
+        else
+        {
+            for (int i = start; i < _particleCount; ++i)
+            {
+                float endRadius = modeB.endRadius + modeB.endRadiusVar * RANDOM_M11(&RANDSEED);
+                _particleData.modeB.deltaRadius[i] = (endRadius - _particleData.modeB.radius[i]) / _particleData.timeToLive[i];
+            }
+        }
+    }
+}
+
+void ParticleSystem::onEnter()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnter))
+            return;
+    }
+#endif
+    
+    Node::onEnter();
+    
+    // update after action in run!
+    this->scheduleUpdateWithPriority(1);
+
+    __allInstances.pushBack(this);
+}
+
+void ParticleSystem::onExit()
+{
+#if CC_ENABLE_SCRIPT_BINDING
+    if (_scriptType == kScriptTypeJavascript)
+    {
+        if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit))
+            return;
+    }
+#endif
+    
+    this->unscheduleUpdate();
+    Node::onExit();
+
+    auto iter = std::find(std::begin(__allInstances), std::end(__allInstances), this);
+    if (iter != std::end(__allInstances))
+    {
+        __allInstances.erase(iter);
+    }
+}
+
+void ParticleSystem::stopSystem()
+{
+    _isActive = false;
+    _elapsed = _duration;
+    _emitCounter = 0;
+}
+
+void ParticleSystem::resetSystem()
+{
+    _isActive = true;
+    _elapsed = 0;
+    for (int i = 0; i < _particleCount; ++i)
+    {
+        _particleData.timeToLive[i] = 0.0f;
+    }
+}
+
+bool ParticleSystem::isFull()
+{
+    return (_particleCount == _totalParticles);
+}
+
+// ParticleSystem - MainLoop
+void ParticleSystem::update(float dt)
+{
+    CC_PROFILER_START_CATEGORY(kProfilerCategoryParticles , "CCParticleSystem - update");
+
+    if (_isActive && _emissionRate)
+    {
+        float rate = 1.0f / _emissionRate;
+        int totalParticles = static_cast<int>(_totalParticles * __totalParticleCountFactor);
+        
+        //issue #1201, prevent bursts of particles, due to too high emitCounter
+        if (_particleCount < totalParticles)
+        {
+            _emitCounter += dt;
+            if (_emitCounter < 0.f)
+                _emitCounter = 0.f;
+        }
+        
+        int emitCount = MIN(totalParticles - _particleCount, _emitCounter / rate);
+        addParticles(emitCount);
+        _emitCounter -= rate * emitCount;
+        
+        _elapsed += dt;
+        if (_elapsed < 0.f)
+            _elapsed = 0.f;
+        if (_duration != DURATION_INFINITY && _duration < _elapsed)
+        {
+            this->stopSystem();
+        }
+    }
+    
+    {
+        for (int i = 0; i < _particleCount; ++i)
+        {
+            _particleData.timeToLive[i] -= dt;
+        }
+        
+        for (int i = 0; i < _particleCount; ++i)
+        {
+            if (_particleData.timeToLive[i] <= 0.0f)
+            {
+                int j = _particleCount - 1;
+                while (j > 0 && _particleData.timeToLive[j] <= 0)
+                {
+                    _particleCount--;
+                    j--;
+                }
+                _particleData.copyParticle(i, _particleCount - 1);
+                if (_batchNode)
+                {
+                    //disable the switched particle
+                    int currentIndex = _particleData.atlasIndex[i];
+                    _batchNode->disableParticle(_atlasIndex + currentIndex);
+                    //switch indexes
+                    _particleData.atlasIndex[_particleCount - 1] = currentIndex;
+                }
+                --_particleCount;
+                if( _particleCount == 0 && _isAutoRemoveOnFinish )
+                {
+                    this->unscheduleUpdate();
+                    _parent->removeChild(this, true);
+                    return;
+                }
+            }
+        }
+        
+        if (_emitterMode == Mode::GRAVITY)
+        {
+            for (int i = 0 ; i < _particleCount; ++i)
+            {
+                particle_point tmp, radial = {0.0f, 0.0f}, tangential;
+                
+                // radial acceleration
+                if (_particleData.posx[i] || _particleData.posy[i])
+                {
+                    normalize_point(_particleData.posx[i], _particleData.posy[i], &radial);
+                }
+                tangential = radial;
+                radial.x *= _particleData.modeA.radialAccel[i];
+                radial.y *= _particleData.modeA.radialAccel[i];
+                
+                // tangential acceleration
+                std::swap(tangential.x, tangential.y);
+                tangential.x *= - _particleData.modeA.tangentialAccel[i];
+                tangential.y *= _particleData.modeA.tangentialAccel[i];
+                
+                // (gravity + radial + tangential) * dt
+                tmp.x = radial.x + tangential.x + modeA.gravity.x;
+                tmp.y = radial.y + tangential.y + modeA.gravity.y;
+                tmp.x *= dt;
+                tmp.y *= dt;
+                
+                _particleData.modeA.dirX[i] += tmp.x;
+                _particleData.modeA.dirY[i] += tmp.y;
+                
+                // this is cocos2d-x v3.0
+                // if (_configName.length()>0 && _yCoordFlipped != -1)
+                
+                // this is cocos2d-x v3.0
+                tmp.x = _particleData.modeA.dirX[i] * dt * _yCoordFlipped;
+                tmp.y = _particleData.modeA.dirY[i] * dt * _yCoordFlipped;
+                _particleData.posx[i] += tmp.x;
+                _particleData.posy[i] += tmp.y;
+            }
+        }
+        else
+        {
+            //Why use so many for-loop separately instead of putting them together?
+            //When the processor needs to read from or write to a location in memory,
+            //it first checks whether a copy of that data is in the cache.
+            //And every property's memory of the particle system is continuous,
+            //for the purpose of improving cache hit rate, we should process only one property in one for-loop AFAP.
+            //It was proved to be effective especially for low-end machine. 
+            for (int i = 0; i < _particleCount; ++i)
+            {
+                _particleData.modeB.angle[i] += _particleData.modeB.degreesPerSecond[i] * dt;
+            }
+            
+            for (int i = 0; i < _particleCount; ++i)
+            {
+                _particleData.modeB.radius[i] += _particleData.modeB.deltaRadius[i] * dt;
+            }
+            
+            for (int i = 0; i < _particleCount; ++i)
+            {
+                _particleData.posx[i] = - cosf(_particleData.modeB.angle[i]) * _particleData.modeB.radius[i];
+            }
+            for (int i = 0; i < _particleCount; ++i)
+            {
+                _particleData.posy[i] = - sinf(_particleData.modeB.angle[i]) * _particleData.modeB.radius[i] * _yCoordFlipped;
+            }
+        }
+        
+        //color r,g,b,a
+        for (int i = 0 ; i < _particleCount; ++i)
+        {
+            _particleData.colorR[i] += _particleData.deltaColorR[i] * dt;
+        }
+        
+        for (int i = 0 ; i < _particleCount; ++i)
+        {
+            _particleData.colorG[i] += _particleData.deltaColorG[i] * dt;
+        }
+        
+        for (int i = 0 ; i < _particleCount; ++i)
+        {
+            _particleData.colorB[i] += _particleData.deltaColorB[i] * dt;
+        }
+        
+        for (int i = 0 ; i < _particleCount; ++i)
+        {
+            _particleData.colorA[i] += _particleData.deltaColorA[i] * dt;
+        }
+        //size
+        for (int i = 0 ; i < _particleCount; ++i)
+        {
+            _particleData.size[i] += (_particleData.deltaSize[i] * dt);
+            _particleData.size[i] = MAX(0, _particleData.size[i]);
+        }
+        //angle
+        for (int i = 0 ; i < _particleCount; ++i)
+        {
+            _particleData.rotation[i] += _particleData.deltaRotation[i] * dt;
+        }
+        
+        updateParticleQuads();
+        _transformSystemDirty = false;
+    }
+
+    // only update gl buffer when visible
+    if (_visible && ! _batchNode)
+    {
+        postStep();
+    }
+
+    CC_PROFILER_STOP_CATEGORY(kProfilerCategoryParticles , "CCParticleSystem - update");
+}
+
+void ParticleSystem::updateWithNoTime()
+{
+    this->update(0.0f);
+}
+
+void ParticleSystem::updateParticleQuads()
+{
+    //should be overridden
+}
+
+void ParticleSystem::postStep()
+{
+    // should be overridden
+}
+
+// ParticleSystem - Texture protocol
+void ParticleSystem::setTexture(Texture2D* var)
+{
+    if (_texture != var)
+    {
+        CC_SAFE_RETAIN(var);
+        CC_SAFE_RELEASE(_texture);
+        _texture = var;
+        updateBlendFunc();
+    }
+}
+
+void ParticleSystem::updateBlendFunc()
+{
+    CCASSERT(! _batchNode, "Can't change blending functions when the particle is being batched");
+
+    if(_texture)
+    {
+        bool premultiplied = _texture->hasPremultipliedAlpha();
+        
+        _opacityModifyRGB = false;
+        
+        if( _texture && ( _blendFunc.src == CC_BLEND_SRC && _blendFunc.dst == CC_BLEND_DST ) )
+        {
+            if( premultiplied )
+            {
+                _opacityModifyRGB = true;
+            }
+            else
+            {
+                _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
+            }
+        }
+    }
+}
+
+Texture2D * ParticleSystem::getTexture() const
+{
+    return _texture;
+}
+
+// ParticleSystem - Additive Blending
+void ParticleSystem::setBlendAdditive(bool additive)
+{
+    if( additive )
+    {
+        _blendFunc = BlendFunc::ADDITIVE;
+    }
+    else
+    {
+        if( _texture && ! _texture->hasPremultipliedAlpha() )
+            _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
+        else 
+            _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
+    }
+}
+
+bool ParticleSystem::isBlendAdditive() const
+{
+    return( _blendFunc.src == backend::BlendFactor::SRC_ALPHA && _blendFunc.dst == backend::BlendFactor::ONE);
+}
+
+// ParticleSystem - Properties of Gravity Mode 
+void ParticleSystem::setTangentialAccel(float t)
+{
+    CCASSERT( _emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    modeA.tangentialAccel = t;
+}
+
+float ParticleSystem::getTangentialAccel() const
+{
+    CCASSERT( _emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    return modeA.tangentialAccel;
+}
+
+void ParticleSystem::setTangentialAccelVar(float t)
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    modeA.tangentialAccelVar = t;
+}
+
+float ParticleSystem::getTangentialAccelVar() const
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    return modeA.tangentialAccelVar;
+}    
+
+void ParticleSystem::setRadialAccel(float t)
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    modeA.radialAccel = t;
+}
+
+float ParticleSystem::getRadialAccel() const
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    return modeA.radialAccel;
+}
+
+void ParticleSystem::setRadialAccelVar(float t)
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    modeA.radialAccelVar = t;
+}
+
+float ParticleSystem::getRadialAccelVar() const
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    return modeA.radialAccelVar;
+}
+
+void ParticleSystem::setRotationIsDir(bool t)
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    modeA.rotationIsDir = t;
+}
+
+bool ParticleSystem::getRotationIsDir() const
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    return modeA.rotationIsDir;
+}
+
+void ParticleSystem::setGravity(const Vec2& g)
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    modeA.gravity = g;
+}
+
+const Vec2& ParticleSystem::getGravity()
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    return modeA.gravity;
+}
+
+void ParticleSystem::setSpeed(float speed)
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    modeA.speed = speed;
+}
+
+float ParticleSystem::getSpeed() const
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    return modeA.speed;
+}
+
+void ParticleSystem::setSpeedVar(float speedVar)
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    modeA.speedVar = speedVar;
+}
+
+float ParticleSystem::getSpeedVar() const
+{
+    CCASSERT(_emitterMode == Mode::GRAVITY, "Particle Mode should be Gravity");
+    return modeA.speedVar;
+}
+
+// ParticleSystem - Properties of Radius Mode
+void ParticleSystem::setStartRadius(float startRadius)
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    modeB.startRadius = startRadius;
+}
+
+float ParticleSystem::getStartRadius() const
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    return modeB.startRadius;
+}
+
+void ParticleSystem::setStartRadiusVar(float startRadiusVar)
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    modeB.startRadiusVar = startRadiusVar;
+}
+
+float ParticleSystem::getStartRadiusVar() const
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    return modeB.startRadiusVar;
+}
+
+void ParticleSystem::setEndRadius(float endRadius)
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    modeB.endRadius = endRadius;
+}
+
+float ParticleSystem::getEndRadius() const
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    return modeB.endRadius;
+}
+
+void ParticleSystem::setEndRadiusVar(float endRadiusVar)
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    modeB.endRadiusVar = endRadiusVar;
+}
+
+float ParticleSystem::getEndRadiusVar() const
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    return modeB.endRadiusVar;
+}
+
+void ParticleSystem::setRotatePerSecond(float degrees)
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    modeB.rotatePerSecond = degrees;
+}
+
+float ParticleSystem::getRotatePerSecond() const
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    return modeB.rotatePerSecond;
+}
+
+void ParticleSystem::setRotatePerSecondVar(float degrees)
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    modeB.rotatePerSecondVar = degrees;
+}
+
+float ParticleSystem::getRotatePerSecondVar() const
+{
+    CCASSERT(_emitterMode == Mode::RADIUS, "Particle Mode should be Radius");
+    return modeB.rotatePerSecondVar;
+}
+
+bool ParticleSystem::isActive() const
+{
+    return _isActive;
+}
+
+int ParticleSystem::getTotalParticles() const
+{
+    return _totalParticles;
+}
+
+void ParticleSystem::setTotalParticles(int var)
+{
+    CCASSERT( var <= _allocatedParticles, "Particle: resizing particle array only supported for quads");
+    _totalParticles = var;
+}
+
+const BlendFunc& ParticleSystem::getBlendFunc() const
+{
+    return _blendFunc;
+}
+
+void ParticleSystem::setBlendFunc(const BlendFunc &blendFunc)
+{
+    if( _blendFunc.src != blendFunc.src || _blendFunc.dst != blendFunc.dst ) {
+        _blendFunc = blendFunc;
+        this->updateBlendFunc();
+    }
+}
+
+bool ParticleSystem::isAutoRemoveOnFinish() const
+{
+    return _isAutoRemoveOnFinish;
+}
+
+void ParticleSystem::setAutoRemoveOnFinish(bool var)
+{
+    _isAutoRemoveOnFinish = var;
+}
+
+
+// ParticleSystem - methods for batchNode rendering
+
+ParticleBatchNode* ParticleSystem::getBatchNode() const
+{
+    return _batchNode;
+}
+
+void ParticleSystem::setBatchNode(ParticleBatchNode* batchNode)
+{
+    if( _batchNode != batchNode ) {
+
+        _batchNode = batchNode; // weak reference
+
+        if( batchNode ) {
+            //each particle needs a unique index
+            for (int i = 0; i < _totalParticles; i++)
+            {
+                _particleData.atlasIndex[i] = i;
+            }
+        }
+    }
+}
+
+//don't use a transform matrix, this is faster
+void ParticleSystem::setScale(float s)
+{
+    _transformSystemDirty = true;
+    Node::setScale(s);
+}
+
+void ParticleSystem::setRotation(float newRotation)
+{
+    _transformSystemDirty = true;
+    Node::setRotation(newRotation);
+}
+
+void ParticleSystem::setScaleX(float newScaleX)
+{
+    _transformSystemDirty = true;
+    Node::setScaleX(newScaleX);
+}
+
+void ParticleSystem::setScaleY(float newScaleY)
+{
+    _transformSystemDirty = true;
+    Node::setScaleY(newScaleY);
+}
+
+void ParticleSystem::start()
+{
+    resetSystem();
+}
+
+void ParticleSystem::stop()
+{
+    stopSystem();
+}
+
+bool ParticleSystem::isPaused() const
+{
+    return _paused;
+}
+
+void ParticleSystem::pauseEmissions()
+{
+    _paused = true;
+}
+
+void ParticleSystem::resumeEmissions()
+{
+    _paused = false;
+}
+
+
+
+NS_CC_END

+ 999 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParticleSystem.h

@@ -0,0 +1,999 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#ifndef __CCPARTICLE_SYSTEM_H__
+#define __CCPARTICLE_SYSTEM_H__
+
+#include "base/CCProtocols.h"
+#include "2d/CCNode.h"
+#include "base/CCValue.h"
+
+NS_CC_BEGIN
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+class ParticleBatchNode;
+
+/** @struct sParticle
+Structure that contains the values of each particle.
+*/
+
+struct particle_point
+{
+    float x;
+    float y;
+};
+
+class CC_DLL ParticleData
+{
+public:
+    float* posx;
+    float* posy;
+    float* startPosX;
+    float* startPosY;
+
+    float* colorR;
+    float* colorG;
+    float* colorB;
+    float* colorA;
+    
+    float* deltaColorR;
+    float* deltaColorG;
+    float* deltaColorB;
+    float* deltaColorA;
+    
+    float* size;
+    float* deltaSize;
+    float* rotation;
+    float* deltaRotation;
+    float* timeToLive;
+    unsigned int* atlasIndex;
+    
+    //! Mode A: gravity, direction, radial accel, tangential accel
+    struct{
+        float* dirX;
+        float* dirY;
+        float* radialAccel;
+        float* tangentialAccel;
+    } modeA;
+    
+    //! Mode B: radius mode
+    struct{
+        float* angle;
+        float* degreesPerSecond;
+        float* radius;
+        float* deltaRadius;
+    } modeB;
+    
+    unsigned int maxCount;
+    ParticleData();
+    bool init(int count);
+    void release();
+    unsigned int getMaxCount() { return maxCount; }
+    
+    void copyParticle(int p1, int p2)
+    {
+        posx[p1] = posx[p2];
+        posy[p1] = posy[p2];
+        startPosX[p1] = startPosX[p2];
+        startPosY[p1] = startPosY[p2];
+        
+        colorR[p1] = colorR[p2];
+        colorG[p1] = colorG[p2];
+        colorB[p1] = colorB[p2];
+        colorA[p1] = colorA[p2];
+        
+        deltaColorR[p1] = deltaColorR[p2];
+        deltaColorG[p1] = deltaColorG[p2];
+        deltaColorB[p1] = deltaColorB[p2];
+        deltaColorA[p1] = deltaColorA[p2];
+        
+        size[p1] = size[p2];
+        deltaSize[p1] = deltaSize[p2];
+        
+        rotation[p1] = rotation[p2];
+        deltaRotation[p1] = deltaRotation[p2];
+        
+        timeToLive[p1] = timeToLive[p2];
+        
+        atlasIndex[p1] = atlasIndex[p2];
+        
+        modeA.dirX[p1] = modeA.dirX[p2];
+        modeA.dirY[p1] = modeA.dirY[p2];
+        modeA.radialAccel[p1] = modeA.radialAccel[p2];
+        modeA.tangentialAccel[p1] = modeA.tangentialAccel[p2];
+        
+        modeB.angle[p1] = modeB.angle[p2];
+        modeB.degreesPerSecond[p1] = modeB.degreesPerSecond[p2];
+        modeB.radius[p1] = modeB.radius[p2];
+        modeB.deltaRadius[p1] = modeB.deltaRadius[p2];
+        
+    }
+};
+
+
+
+//typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tParticle*, Vec2);
+
+class Texture2D;
+
+/** @class ParticleSystem
+ * @brief Particle System base class.
+Attributes of a Particle System:
+- emission rate of the particles
+- Gravity Mode (Mode A):
+- gravity
+- direction
+- speed +-  variance
+- tangential acceleration +- variance
+- radial acceleration +- variance
+- Radius Mode (Mode B):
+- startRadius +- variance
+- endRadius +- variance
+- rotate +- variance
+- Properties common to all modes:
+- life +- life variance
+- start spin +- variance
+- end spin +- variance
+- start size +- variance
+- end size +- variance
+- start color +- variance
+- end color +- variance
+- life +- variance
+- blending function
+- texture
+
+Cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/).
+'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guaranteed in cocos2d,
+cocos2d uses a another approach, but the results are almost identical. 
+
+Cocos2d supports all the variables used by Particle Designer plus a bit more:
+- spinning particles (supported when using ParticleSystemQuad)
+- tangential acceleration (Gravity mode)
+- radial acceleration (Gravity mode)
+- radius direction (Radius mode) (Particle Designer supports outwards to inwards direction only)
+
+It is possible to customize any of the above mentioned properties in runtime. Example:
+
+@code
+emitter.radialAccel = 15;
+emitter.startSpin = 0;
+@endcode
+
+*/
+
+class CC_DLL ParticleSystem : public Node, public TextureProtocol, public PlayableProtocol
+{
+public:
+    /** Mode
+     * @js cc.ParticleSystem.MODE_GRAVITY;
+     */
+    enum class Mode
+    {
+        GRAVITY,
+        RADIUS,
+    };
+    
+    /** PositionType
+     Possible types of particle positions.
+     * @js cc.ParticleSystem.TYPE_FREE
+     */
+    enum class PositionType
+    {
+        FREE, /** Living particles are attached to the world and are unaffected by emitter repositioning. */
+        
+        RELATIVE, /** Living particles are attached to the world but will follow the emitter repositioning.
+                   Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite.*/
+        
+        GROUPED, /** Living particles are attached to the emitter and are translated along with it. */
+
+    };
+    
+    //* @enum
+    enum {
+        /** The Particle emitter lives forever. */
+        DURATION_INFINITY = -1,
+        
+        /** The starting size of the particle is equal to the ending size. */
+        START_SIZE_EQUAL_TO_END_SIZE = -1,
+        
+        /** The starting radius of the particle is equal to the ending radius. */
+        START_RADIUS_EQUAL_TO_END_RADIUS = -1,
+    };
+    
+    /** Creates an initializes a ParticleSystem from a plist file.
+    This plist files can be created manually or with Particle Designer:
+    http://particledesigner.71squared.com/
+     @since v2.0
+     *
+     * @param plistFile Particle plist file name.
+     * @return An autoreleased ParticleSystem object.
+     */
+    static ParticleSystem * create(const std::string& plistFile);
+
+    /** Create a system with a fixed number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleSystemQuad object.
+     * @js NA
+     */
+    static ParticleSystem* createWithTotalParticles(int numberOfParticles);
+
+    /** Gets all ParticleSystem references
+     */
+    static Vector<ParticleSystem*>& getAllParticleSystems();
+public:
+    void addParticles(int count);
+    
+    void stopSystem();
+    /** Kill all living particles.
+     */
+    void resetSystem();
+    /** Whether or not the system is full.
+     *
+     * @return True if the system is full.
+     */
+    bool isFull();
+
+    /** Update the verts position data of particle,
+     should be overridden by subclasses. 
+     */
+    virtual void updateParticleQuads();
+    /** Update the VBO verts buffer which does not use batch node,
+     should be overridden by subclasses. */
+    virtual void postStep();
+
+    /** Call the update method with no time..
+     */
+    virtual void updateWithNoTime();
+
+    /** Whether or not the particle system removed self on finish.
+     *
+     * @return True if the particle system removed self on finish.
+     */
+    virtual bool isAutoRemoveOnFinish() const;
+    
+    /** Set the particle system auto removed it self on finish.
+     *
+     * @param var True if the particle system removed self on finish.
+     */
+    virtual void setAutoRemoveOnFinish(bool var);
+
+    // mode A
+    /** Gets the gravity.
+     *
+     * @return The gravity.
+     */
+    virtual const Vec2& getGravity();
+    /** Sets the gravity.
+     *
+     * @param g The gravity.
+     */
+    virtual void setGravity(const Vec2& g);
+    /** Gets the speed.
+     *
+     * @return The speed.
+     */
+    virtual float getSpeed() const;
+    /** Sets the speed.
+     *
+     * @param speed The speed.
+     */
+    virtual void setSpeed(float speed);
+    /** Gets the speed variance.
+     *
+     * @return The speed variance.
+     */
+    virtual float getSpeedVar() const;
+    /** Sets the speed variance.
+     *
+     * @param speed The speed variance.
+     */
+    virtual void setSpeedVar(float speed);
+    /** Gets the tangential acceleration.
+     *
+     * @return The tangential acceleration.
+     */
+    virtual float getTangentialAccel() const;
+    /** Sets the tangential acceleration.
+     *
+     * @param t The tangential acceleration.
+     */
+    virtual void setTangentialAccel(float t);
+    /** Gets the tangential acceleration variance.
+     *
+     * @return The tangential acceleration variance.
+     */
+    virtual float getTangentialAccelVar() const;
+    /** Sets the tangential acceleration variance.
+     *
+     * @param t The tangential acceleration variance.
+     */
+    virtual void setTangentialAccelVar(float t);
+    /** Gets the radial acceleration.
+     *
+     * @return The radial acceleration.
+     */
+    virtual float getRadialAccel() const;
+    /** Sets the radial acceleration.
+     *
+     * @param t The radial acceleration.
+     */
+    virtual void setRadialAccel(float t);
+    /** Gets the radial acceleration variance.
+     *
+     * @return The radial acceleration variance.
+     */
+    virtual float getRadialAccelVar() const;
+    /** Sets the radial acceleration variance.
+     *
+     * @param t The radial acceleration variance.
+     */
+    virtual void setRadialAccelVar(float t);
+    /** Whether or not the rotation of each particle to its direction.
+     *
+     * @return True if the rotation is the direction.
+     */
+    virtual bool getRotationIsDir() const;
+    /** Sets the rotation of each particle to its direction.
+     *
+     * @param t True if the rotation is the direction.
+     */
+    virtual void setRotationIsDir(bool t);
+    // mode B
+    /** Gets the start radius.
+     *
+     * @return The start radius.
+     */
+    virtual float getStartRadius() const;
+    /** Sets the start radius.
+     *
+     * @param startRadius The start radius.
+     */
+    virtual void setStartRadius(float startRadius);
+    /** Gets the start radius variance.
+     *
+     * @return The start radius variance.
+     */
+    virtual float getStartRadiusVar() const;
+    /** Sets the start radius variance.
+     *
+     * @param startRadiusVar The start radius variance.
+     */
+    virtual void setStartRadiusVar(float startRadiusVar);
+    /** Gets the end radius.
+     *
+     * @return The end radius.
+     */
+    virtual float getEndRadius() const;
+    /** Sets the end radius.
+     *
+     * @param endRadius The end radius.
+     */
+    virtual void setEndRadius(float endRadius);
+    /** Gets the end radius variance.
+     *
+     * @return The end radius variance.
+     */
+    virtual float getEndRadiusVar() const;
+    /** Sets the end radius variance.
+     *
+     * @param endRadiusVar The end radius variance.
+     */
+    virtual void setEndRadiusVar(float endRadiusVar);
+    /** Gets the number of degrees to rotate a particle around the source pos per second.
+     *
+     * @return The number of degrees to rotate a particle around the source pos per second.
+     */
+    virtual float getRotatePerSecond() const;
+    /** Sets the number of degrees to rotate a particle around the source pos per second.
+     *
+     * @param degrees The number of degrees to rotate a particle around the source pos per second.
+     */
+    virtual void setRotatePerSecond(float degrees);
+    /** Gets the rotate per second variance.
+     *
+     * @return The rotate per second variance.
+     */
+    virtual float getRotatePerSecondVar() const;
+    /** Sets the rotate per second variance.
+     *
+     * @param degrees The rotate per second variance.
+     */
+    virtual void setRotatePerSecondVar(float degrees);
+
+    virtual void setScale(float s) override;
+    virtual void setRotation(float newRotation) override;
+    virtual void setScaleX(float newScaleX) override;
+    virtual void setScaleY(float newScaleY) override;
+
+    /** Whether or not the particle system is active.
+     *
+     * @return True if the particle system is active.
+     */
+    virtual bool isActive() const;
+    /** Whether or not the particle system is blend additive.
+     *
+     * @return True if the particle system is blend additive.
+     */
+    virtual bool isBlendAdditive() const;
+    /** Sets the particle system blend additive.
+     *
+     * @param value True if the particle system is blend additive.
+     */
+    virtual void setBlendAdditive(bool value);
+
+    /** Gets the batch node.
+     *
+     * @return The batch node.
+     */
+    virtual ParticleBatchNode* getBatchNode() const;
+    /** Sets the batch node.
+     *
+     * @param batchNode The batch node.
+     */
+    virtual void setBatchNode(ParticleBatchNode* batchNode);
+    
+    /** Gets the index of system in batch node array.
+     *
+     * @return The index of system in batch node array.
+     */
+    int getAtlasIndex() const { return _atlasIndex; }
+    /** Sets the index of system in batch node array.
+     *
+     * @param index The index of system in batch node array.
+     */
+    void setAtlasIndex(int index) { _atlasIndex = index; }
+
+    /** Gets the Quantity of particles that are being simulated at the moment.
+     *
+     * @return The Quantity of particles that are being simulated at the moment.
+     */
+    unsigned int getParticleCount() const { return _particleCount; }
+    
+    /** Gets how many seconds the emitter will run. -1 means 'forever'.
+     *
+     * @return The seconds that the emitter will run. -1 means 'forever'.
+     */
+    float getDuration() const { return _duration; }
+    /** Sets how many seconds the emitter will run. -1 means 'forever'.
+     *
+     * @param duration The seconds that the emitter will run. -1 means 'forever'.
+     */
+    void setDuration(float duration) { _duration = duration; }
+    
+    /** Gets the source position of the emitter.
+     *
+     * @return The source position of the emitter.
+     */
+    const Vec2& getSourcePosition() const { return _sourcePosition; }
+    /** Sets the source position of the emitter.
+     *
+     * @param pos The source position of the emitter.
+     */
+    void setSourcePosition(const Vec2& pos) { _sourcePosition = pos; }
+    
+    /** Gets the position variance of the emitter.
+     *
+     * @return The position variance of the emitter.
+     */
+    const Vec2& getPosVar() const { return _posVar; }
+    /** Sets the position variance of the emitter.
+     *
+     * @param pos The position variance of the emitter.
+     */
+    void setPosVar(const Vec2& pos) { _posVar = pos; }
+
+    /** Gets the life of each particle.
+     *
+     * @return The life of each particle.
+     */
+    float getLife() const { return _life; }
+    /** Sets the life of each particle.
+     *
+     * @param life The life of each particle.
+     */
+    void setLife(float life) { _life = life; }
+
+    /** Gets the life variance of each particle.
+     *
+     * @return The life variance of each particle.
+     */
+    float getLifeVar() const { return _lifeVar; }
+    /** Sets the life variance of each particle.
+     *
+     * @param lifeVar The life variance of each particle.
+     */
+    void setLifeVar(float lifeVar) { _lifeVar = lifeVar; }
+
+    /** Gets the angle of each particle. 
+     *
+     * @return The angle of each particle.
+     */
+    float getAngle() const { return _angle; }
+    /** Sets the angle of each particle.
+     *
+     * @param angle The angle of each particle.
+     */
+    void setAngle(float angle) { _angle = angle; }
+
+    /** Gets the angle variance of each particle.
+     *
+     * @return The angle variance of each particle.
+     */
+    float getAngleVar() const { return _angleVar; }
+    /** Sets the angle variance of each particle.
+     *
+     * @param angleVar The angle variance of each particle.
+     */
+    void setAngleVar(float angleVar) { _angleVar = angleVar; }
+    
+    /** Switch between different kind of emitter modes:
+     - kParticleModeGravity: uses gravity, speed, radial and tangential acceleration.
+     - kParticleModeRadius: uses radius movement + rotation.
+     *
+     * @return The mode of the emitter.
+     */
+    Mode getEmitterMode() const { return _emitterMode; }
+    /** Sets the mode of the emitter.
+     *
+     * @param mode The mode of the emitter.
+     */
+    void setEmitterMode(Mode mode) { _emitterMode = mode; }
+    
+    /** Gets the start size in pixels of each particle.
+     *
+     * @return The start size in pixels of each particle.
+     */
+    float getStartSize() const { return _startSize; }
+    /** Sets the start size in pixels of each particle.
+     *
+     * @param startSize The start size in pixels of each particle.
+     */
+    void setStartSize(float startSize) { _startSize = startSize; }
+
+    /** Gets the start size variance in pixels of each particle.
+     *
+     * @return The start size variance in pixels of each particle.
+     */
+    float getStartSizeVar() const { return _startSizeVar; }
+    /** Sets the start size variance in pixels of each particle.
+     *
+     * @param sizeVar The start size variance in pixels of each particle.
+     */
+    void setStartSizeVar(float sizeVar) { _startSizeVar = sizeVar; }
+
+    /** Gets the end size in pixels of each particle.
+     *
+     * @return The end size in pixels of each particle.
+     */
+    float getEndSize() const { return _endSize; }
+    /** Sets the end size in pixels of each particle.
+     *
+     * @param endSize The end size in pixels of each particle.
+     */
+    void setEndSize(float endSize) { _endSize = endSize; }
+
+    /** Gets the end size variance in pixels of each particle.
+     *
+     * @return The end size variance in pixels of each particle.
+     */
+    float getEndSizeVar() const { return _endSizeVar; }
+    /** Sets the end size variance in pixels of each particle.
+     *
+     * @param sizeVar The end size variance in pixels of each particle.
+     */
+    void setEndSizeVar(float sizeVar) { _endSizeVar = sizeVar; }
+
+    /** Gets the start color of each particle.
+     *
+     * @return The start color of each particle.
+     */
+    const Color4F& getStartColor() const { return _startColor; }
+    /** Sets the start color of each particle.
+     *
+     * @param color The start color of each particle.
+     */
+    void setStartColor(const Color4F& color) { _startColor = color; }
+
+    /** Gets the start color variance of each particle.
+     *
+     * @return The start color variance of each particle.
+     */
+    const Color4F& getStartColorVar() const { return _startColorVar; }
+    /** Sets the start color variance of each particle.
+     *
+     * @param color The start color variance of each particle.
+     */
+    void setStartColorVar(const Color4F& color) { _startColorVar = color; }
+
+    /** Gets the end color and end color variation of each particle.
+     *
+     * @return The end color and end color variation of each particle.
+     */
+    const Color4F& getEndColor() const { return _endColor; }
+    /** Sets the end color and end color variation of each particle.
+     *
+     * @param color The end color and end color variation of each particle.
+     */
+    void setEndColor(const Color4F& color) { _endColor = color; }
+
+    /** Gets the end color variance of each particle.
+     *
+     * @return The end color variance of each particle.
+     */
+    const Color4F& getEndColorVar() const { return _endColorVar; }
+    /** Sets the end color variance of each particle.
+     *
+     * @param color The end color variance of each particle.
+     */
+    void setEndColorVar(const Color4F& color) { _endColorVar = color; }
+
+    /** Gets the start spin of each particle.
+     *
+     * @return The start spin of each particle.
+     */
+    float getStartSpin() const { return _startSpin; }
+    /** Sets the start spin of each particle.
+     *
+     * @param spin The start spin of each particle.
+     */
+    void setStartSpin(float spin) { _startSpin = spin; }
+
+    /** Gets the start spin variance of each particle.
+     *
+     * @return The start spin variance of each particle.
+     */
+    float getStartSpinVar() const { return _startSpinVar; }
+    /** Sets the start spin variance of each particle.
+     *
+     * @param pinVar The start spin variance of each particle.
+     */
+    void setStartSpinVar(float pinVar) { _startSpinVar = pinVar; }
+
+    /** Gets the end spin of each particle.
+     *
+     * @return The end spin of each particle.
+     */
+    float getEndSpin() const { return _endSpin; }
+    /** Sets the end spin of each particle.
+     *
+     * @param endSpin The end spin of each particle.
+     */
+    void setEndSpin(float endSpin) { _endSpin = endSpin; }
+
+    /** Gets the end spin variance of each particle.
+     *
+     * @return The end spin variance of each particle.
+     */
+    float getEndSpinVar() const { return _endSpinVar; }
+    /** Sets the end spin variance of each particle.
+     *
+     * @param endSpinVar The end spin variance of each particle.
+     */
+    void setEndSpinVar(float endSpinVar) { _endSpinVar = endSpinVar; }
+
+    /** Gets the emission rate of the particles.
+     *
+     * @return The emission rate of the particles.
+     */
+    float getEmissionRate() const { return _emissionRate; }
+    /** Sets the emission rate of the particles.
+     *
+     * @param rate The emission rate of the particles.
+     */
+    void setEmissionRate(float rate) { _emissionRate = rate; }
+
+    /** Gets the maximum particles of the system.
+     *
+     * @return The maximum particles of the system.
+     */
+    virtual int getTotalParticles() const;
+    /** Sets the maximum particles of the system.
+     *
+     * @param totalParticles The maximum particles of the system.
+     */
+    virtual void setTotalParticles(int totalParticles);
+
+    /** does the alpha value modify color */
+    void setOpacityModifyRGB(bool opacityModifyRGB) override { _opacityModifyRGB = opacityModifyRGB; }
+    bool isOpacityModifyRGB() const override { return _opacityModifyRGB; }
+    
+    /** Gets the particles movement type: Free or Grouped.
+     @since v0.8
+     *
+     * @return The particles movement type.
+     */
+    PositionType getPositionType() const { return _positionType; }
+    /** Sets the particles movement type: Free or Grouped.
+    @since v0.8
+     *
+     * @param type The particles movement type.
+     */
+    void setPositionType(PositionType type) { _positionType = type; }
+    
+    // Overrides
+    virtual void onEnter() override;
+    virtual void onExit() override;
+    virtual void update(float dt) override;
+    virtual Texture2D* getTexture() const override;
+    virtual void setTexture(Texture2D *texture) override;
+    /**
+    *@code
+    *When this function bound into js or lua,the parameter will be changed
+    *In js: var setBlendFunc(var src, var dst)
+    *In lua: local setBlendFunc(local src, local dst)
+    *@endcode
+    */
+    virtual void setBlendFunc(const BlendFunc &blendFunc) override;
+    /**
+    * @js NA
+    * @lua NA
+    */
+    virtual const BlendFunc &getBlendFunc() const override;
+
+    const std::string& getResourceFile() const { return _plistFile; }
+
+    /// @{
+    /// @name implement Playable Protocol
+    virtual void start() override;
+    virtual void stop() override;
+    /// @} end of PlayableProtocol
+    
+    void setSourcePositionCompatible(bool sourcePositionCompatible) { _sourcePositionCompatible = sourcePositionCompatible; }
+    bool isSourcePositionCompatible() const { return _sourcePositionCompatible; }
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleSystem();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleSystem();
+
+    /** initializes a ParticleSystem*/
+    bool init() override;
+    /** initializes a ParticleSystem from a plist file.
+     This plist files can be created manually or with Particle Designer:
+     http://particledesigner.71squared.com/
+     @since v0.99.3
+     */
+    bool initWithFile(const std::string& plistFile);
+    
+    /** initializes a QuadParticleSystem from a Dictionary.
+     @since v0.99.3
+     */
+    bool initWithDictionary(ValueMap& dictionary);
+    
+    /** initializes a particle system from a NSDictionary and the path from where to load the png
+     @since v2.1
+     */
+    bool initWithDictionary(ValueMap& dictionary, const std::string& dirname);
+    
+    //! Initializes a system with a fixed number of particles
+    virtual bool initWithTotalParticles(int numberOfParticles);
+    
+    /** Are the emissions paused
+     @return True if the emissions are paused, else false
+     */
+    virtual bool isPaused() const;
+    
+    /* Pause the emissions*/
+    virtual void pauseEmissions();
+    
+    /* UnPause the emissions*/
+    virtual void resumeEmissions();
+
+protected:
+    virtual void updateBlendFunc();
+    
+private:
+    friend class EngineDataManager;
+    /** Internal use only, it's used by EngineDataManager class for Android platform */
+    static void setTotalParticleCountFactor(float factor);
+    
+protected:
+
+    /** whether or not the particles are using blend additive.
+     If enabled, the following blending function will be used.
+     @code
+     source blend function = BlendFactor::SRC_ALPHA;
+     dest blend function = BlendFactor::ONE;
+     @endcode
+     */
+    bool _isBlendAdditive;
+
+    /** whether or not the node will be auto-removed when it has no particles left.
+     By default it is false.
+     @since v0.8
+     */
+    bool _isAutoRemoveOnFinish;
+
+    std::string _plistFile;
+    //! time elapsed since the start of the system (in seconds)
+    float _elapsed;
+
+    // Different modes
+    //! Mode A:Gravity + Tangential Accel + Radial Accel
+    struct {
+        /** Gravity value. Only available in 'Gravity' mode. */
+        Vec2 gravity;
+        /** speed of each particle. Only available in 'Gravity' mode.  */
+        float speed;
+        /** speed variance of each particle. Only available in 'Gravity' mode. */
+        float speedVar;
+        /** tangential acceleration of each particle. Only available in 'Gravity' mode. */
+        float tangentialAccel;
+        /** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */
+        float tangentialAccelVar;
+        /** radial acceleration of each particle. Only available in 'Gravity' mode. */
+        float radialAccel;
+        /** radial acceleration variance of each particle. Only available in 'Gravity' mode. */
+        float radialAccelVar;
+        /** set the rotation of each particle to its direction Only available in 'Gravity' mode. */
+        bool rotationIsDir;
+    } modeA;
+
+    //! Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode)
+    struct {
+        /** The starting radius of the particles. Only available in 'Radius' mode. */
+        float startRadius;
+        /** The starting radius variance of the particles. Only available in 'Radius' mode. */
+        float startRadiusVar;
+        /** The ending radius of the particles. Only available in 'Radius' mode. */
+        float endRadius;
+        /** The ending radius variance of the particles. Only available in 'Radius' mode. */
+        float endRadiusVar;
+        /** Number of degrees to rotate a particle around the source pos per second. Only available in 'Radius' mode. */
+        float rotatePerSecond;
+        /** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */
+        float rotatePerSecondVar;
+    } modeB;
+    
+    //particle data
+    ParticleData _particleData;
+
+    //Emitter name
+    std::string _configName;
+
+    // color modulate
+    //    BOOL colorModulate;
+
+    //! How many particles can be emitted per second
+    float _emitCounter;
+
+    // Optimization
+    //CC_UPDATE_PARTICLE_IMP    updateParticleImp;
+    //SEL                        updateParticleSel;
+
+    /** weak reference to the SpriteBatchNode that renders the Sprite */
+    ParticleBatchNode* _batchNode;
+
+    // index of system in batch node array
+    int _atlasIndex;
+
+    //true if scaled or rotated
+    bool _transformSystemDirty;
+    // Number of allocated particles
+    int _allocatedParticles;
+
+    /** Is the emitter active */
+    bool _isActive;
+    
+    /** Quantity of particles that are being simulated at the moment */
+    int _particleCount;
+    /** The factor affects the total particle count, its value should be 0.0f ~ 1.0f, default 1.0f*/
+    static float __totalParticleCountFactor;
+    
+    /** How many seconds the emitter will run. -1 means 'forever' */
+    float _duration;
+    /** sourcePosition of the emitter */
+    Vec2 _sourcePosition;
+    /** Position variance of the emitter */
+    Vec2 _posVar;
+    /** life, and life variation of each particle */
+    float _life;
+    /** life variance of each particle */
+    float _lifeVar;
+    /** angle and angle variation of each particle */
+    float _angle;
+    /** angle variance of each particle */
+    float _angleVar;
+
+    /** Switch between different kind of emitter modes:
+     - kParticleModeGravity: uses gravity, speed, radial and tangential acceleration
+     - kParticleModeRadius: uses radius movement + rotation
+     */
+    Mode _emitterMode;
+
+    /** start size in pixels of each particle */
+    float _startSize;
+    /** size variance in pixels of each particle */
+    float _startSizeVar;
+    /** end size in pixels of each particle */
+    float _endSize;
+    /** end size variance in pixels of each particle */
+    float _endSizeVar;
+    /** start color of each particle */
+    Color4F _startColor;
+    /** start color variance of each particle */
+    Color4F _startColorVar;
+    /** end color and end color variation of each particle */
+    Color4F _endColor;
+    /** end color variance of each particle */
+    Color4F _endColorVar;
+    //* initial angle of each particle
+    float _startSpin;
+    //* initial angle of each particle
+    float _startSpinVar;
+    //* initial angle of each particle
+    float _endSpin;
+    //* initial angle of each particle
+    float _endSpinVar;
+    /** emission rate of the particles */
+    float _emissionRate;
+    /** maximum particles of the system */
+    int _totalParticles;
+    /** conforms to CocosNodeTexture protocol */
+    Texture2D* _texture;
+    /** conforms to CocosNodeTexture protocol */
+    BlendFunc _blendFunc;
+    /** does the alpha value modify color */
+    bool _opacityModifyRGB;
+    /** does FlippedY variance of each particle */
+    int _yCoordFlipped;
+
+
+    /** particles movement type: Free or Grouped
+     @since v0.8
+     */
+    PositionType _positionType;
+    
+    /** is the emitter paused */
+    bool _paused;
+    
+    /** is sourcePosition compatible */
+    bool _sourcePositionCompatible;
+
+    static Vector<ParticleSystem*> __allInstances;
+    
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleSystem);
+};
+
+// end of _2d group
+/// @}
+
+NS_CC_END
+
+#endif //__CCPARTICLE_SYSTEM_H__

+ 615 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParticleSystemQuad.cpp

@@ -0,0 +1,615 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2009      Leonardo Kasperavičius
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+ 
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCParticleSystemQuad.h"
+#include <algorithm>
+#include <stddef.h> // offsetof
+#include "base/ccTypes.h"
+#include "2d/CCSpriteFrame.h"
+#include "2d/CCParticleBatchNode.h"
+#include "renderer/CCTextureAtlas.h"
+#include "renderer/CCRenderer.h"
+#include "base/CCDirector.h"
+#include "base/CCEventType.h"
+#include "base/CCConfiguration.h"
+#include "base/CCEventListenerCustom.h"
+#include "base/CCEventDispatcher.h"
+#include "base/ccUTF8.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/ProgramState.h"
+
+NS_CC_BEGIN
+
+ParticleSystemQuad::ParticleSystemQuad()
+{
+    auto& pipelieDescriptor = _quadCommand.getPipelineDescriptor();
+    auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR);
+    _programState = new (std::nothrow) backend::ProgramState(program);
+    pipelieDescriptor.programState = _programState;
+    _mvpMatrixLocaiton = pipelieDescriptor.programState->getUniformLocation("u_MVPMatrix");
+    _textureLocation = pipelieDescriptor.programState->getUniformLocation("u_texture");
+    
+    auto vertexLayout = _programState->getVertexLayout();
+    const auto& attributeInfo = _programState->getProgram()->getActiveAttributes();
+    auto iter = attributeInfo.find("a_position");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT3, 0, false);
+    }
+    iter = attributeInfo.find("a_texCoord");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_texCoord", iter->second.location, backend::VertexFormat::FLOAT2, offsetof(V3F_C4B_T2F, texCoords), false);
+    }
+    iter = attributeInfo.find("a_color");
+    if(iter != attributeInfo.end())
+    {
+        vertexLayout->setAttribute("a_color", iter->second.location, backend::VertexFormat::UBYTE4, offsetof(V3F_C4B_T2F, colors), true);
+    }
+    vertexLayout->setLayout(sizeof(V3F_C4B_T2F));
+}
+
+ParticleSystemQuad::~ParticleSystemQuad()
+{
+    if (nullptr == _batchNode)
+    {
+        CC_SAFE_FREE(_quads);
+        CC_SAFE_FREE(_indices);
+    }
+}
+
+// implementation ParticleSystemQuad
+
+ParticleSystemQuad * ParticleSystemQuad::create(const std::string& filename)
+{
+    ParticleSystemQuad *ret = new (std::nothrow) ParticleSystemQuad();
+    if (ret && ret->initWithFile(filename))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return ret;
+}
+
+ParticleSystemQuad * ParticleSystemQuad::createWithTotalParticles(int numberOfParticles) {
+    ParticleSystemQuad *ret = new (std::nothrow) ParticleSystemQuad();
+    if (ret && ret->initWithTotalParticles(numberOfParticles))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return ret;
+}
+
+ParticleSystemQuad * ParticleSystemQuad::create(ValueMap &dictionary)
+{
+    ParticleSystemQuad *ret = new (std::nothrow) ParticleSystemQuad();
+    if (ret && ret->initWithDictionary(dictionary))
+    {
+        ret->autorelease();
+        return ret;
+    }
+    CC_SAFE_DELETE(ret);
+    return ret;
+}
+
+//implementation ParticleSystemQuad
+// overriding the init method
+bool ParticleSystemQuad::initWithTotalParticles(int numberOfParticles)
+{
+    // base initialization
+    if( ParticleSystem::initWithTotalParticles(numberOfParticles) )
+    {
+        // allocating data space
+        if( ! this->allocMemory() ) {
+            this->release();
+            return false;
+        }
+
+        initIndices();
+//        setupVBO();
+
+#if CC_ENABLE_CACHE_TEXTURE_DATA
+        // Need to listen the event only when not use batchnode, because it will use VBO
+        auto listener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, CC_CALLBACK_1(ParticleSystemQuad::listenRendererRecreated, this));
+        _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
+#endif
+
+        return true;
+    }
+    return false;
+}
+
+// pointRect should be in Texture coordinates, not pixel coordinates
+void ParticleSystemQuad::initTexCoordsWithRect(const Rect& pointRect)
+{
+    // convert to Tex coords
+
+    Rect rect = Rect(
+        pointRect.origin.x * CC_CONTENT_SCALE_FACTOR(),
+        pointRect.origin.y * CC_CONTENT_SCALE_FACTOR(),
+        pointRect.size.width * CC_CONTENT_SCALE_FACTOR(),
+        pointRect.size.height * CC_CONTENT_SCALE_FACTOR());
+
+    float wide = (float) pointRect.size.width;
+    float high = (float) pointRect.size.height;
+
+    if (_texture)
+    {
+        wide = (float)_texture->getPixelsWide();
+        high = (float)_texture->getPixelsHigh();
+    }
+
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+    float left = (rect.origin.x*2+1) / (wide*2);
+    float bottom = (rect.origin.y*2+1) / (high*2);
+    float right = left + (rect.size.width*2-2) / (wide*2);
+    float top = bottom + (rect.size.height*2-2) / (high*2);
+#else
+    float left = rect.origin.x / wide;
+    float bottom = rect.origin.y / high;
+    float right = left + rect.size.width / wide;
+    float top = bottom + rect.size.height / high;
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+
+    // Important. Texture in cocos2d are inverted, so the Y component should be inverted
+    std::swap(top, bottom);
+
+    V3F_C4B_T2F_Quad *quads = nullptr;
+    unsigned int start = 0, end = 0;
+    if (_batchNode)
+    {
+        quads = _batchNode->getTextureAtlas()->getQuads();
+        start = _atlasIndex;
+        end = _atlasIndex + _totalParticles;
+    }
+    else
+    {
+        quads = _quads;
+        start = 0;
+        end = _totalParticles;
+    }
+
+    for(unsigned int i=start; i<end; i++) 
+    {
+        // bottom-left vertex:
+        quads[i].bl.texCoords.u = left;
+        quads[i].bl.texCoords.v = bottom;
+        // bottom-right vertex:
+        quads[i].br.texCoords.u = right;
+        quads[i].br.texCoords.v = bottom;
+        // top-left vertex:
+        quads[i].tl.texCoords.u = left;
+        quads[i].tl.texCoords.v = top;
+        // top-right vertex:
+        quads[i].tr.texCoords.u = right;
+        quads[i].tr.texCoords.v = top;
+    }
+}
+
+void ParticleSystemQuad::updateTexCoords()
+{
+    if (_texture)
+    {
+        const Size& s = _texture->getContentSize();
+        initTexCoordsWithRect(Rect(0, 0, s.width, s.height));
+    }
+}
+
+void ParticleSystemQuad::setTextureWithRect(Texture2D *texture, const Rect& rect)
+{
+    // Only update the texture if is different from the current one
+    if( !_texture || texture->getBackendTexture() != _texture->getBackendTexture() )
+    {
+        ParticleSystem::setTexture(texture);
+    }
+
+    this->initTexCoordsWithRect(rect);
+}
+
+void ParticleSystemQuad::setTexture(Texture2D* texture)
+{
+    const Size& s = texture->getContentSize();
+    this->setTextureWithRect(texture, Rect(0, 0, s.width, s.height));
+}
+
+void ParticleSystemQuad::setDisplayFrame(SpriteFrame *spriteFrame)
+{
+    CCASSERT(spriteFrame->getOffsetInPixels().isZero(), 
+             "QuadParticle only supports SpriteFrames with no offsets");
+
+    this->setTextureWithRect(spriteFrame->getTexture(), spriteFrame->getRect());
+}
+
+void ParticleSystemQuad::initIndices()
+{
+    for(int i = 0; i < _totalParticles; ++i)
+    {
+        const unsigned int i6 = i*6;
+        const unsigned int i4 = i*4;
+        _indices[i6+0] = (unsigned short) i4+0;
+        _indices[i6+1] = (unsigned short) i4+1;
+        _indices[i6+2] = (unsigned short) i4+2;
+
+        _indices[i6+5] = (unsigned short) i4+1;
+        _indices[i6+4] = (unsigned short) i4+2;
+        _indices[i6+3] = (unsigned short) i4+3;
+    }
+}
+
+inline void updatePosWithParticle(V3F_C4B_T2F_Quad *quad, const Vec2& newPosition,float size,float rotation)
+{
+    // vertices
+    float size_2 = size/2;
+    float x1 = -size_2;
+    float y1 = -size_2;
+    
+    float x2 = size_2;
+    float y2 = size_2;
+    float x = newPosition.x;
+    float y = newPosition.y;
+    
+    float r = (float)-CC_DEGREES_TO_RADIANS(rotation);
+    float cr = cosf(r);
+    float sr = sinf(r);
+    float ax = x1 * cr - y1 * sr + x;
+    float ay = x1 * sr + y1 * cr + y;
+    float bx = x2 * cr - y1 * sr + x;
+    float by = x2 * sr + y1 * cr + y;
+    float cx = x2 * cr - y2 * sr + x;
+    float cy = x2 * sr + y2 * cr + y;
+    float dx = x1 * cr - y2 * sr + x;
+    float dy = x1 * sr + y2 * cr + y;
+    
+    // bottom-left
+    quad->bl.vertices.x = ax;
+    quad->bl.vertices.y = ay;
+    
+    // bottom-right vertex:
+    quad->br.vertices.x = bx;
+    quad->br.vertices.y = by;
+    
+    // top-left vertex:
+    quad->tl.vertices.x = dx;
+    quad->tl.vertices.y = dy;
+    
+    // top-right vertex:
+    quad->tr.vertices.x = cx;
+    quad->tr.vertices.y = cy;
+}
+
+void ParticleSystemQuad::updateParticleQuads()
+{
+    if (_particleCount <= 0) {
+        return;
+    }
+ 
+    Vec2 currentPosition;
+    if (_positionType == PositionType::FREE)
+    {
+        currentPosition = this->convertToWorldSpace(Vec2::ZERO);
+    }
+    else if (_positionType == PositionType::RELATIVE)
+    {
+        currentPosition = _position;
+    }
+    
+    V3F_C4B_T2F_Quad *startQuad;
+    Vec2 pos = Vec2::ZERO;
+    if (_batchNode)
+    {
+        V3F_C4B_T2F_Quad *batchQuads = _batchNode->getTextureAtlas()->getQuads();
+        startQuad = &(batchQuads[_atlasIndex]);
+        pos = _position;
+    }
+    else
+    {
+        startQuad = &(_quads[0]);
+    }
+    
+    if( _positionType == PositionType::FREE )
+    {
+        Vec3 p1(currentPosition.x, currentPosition.y, 0);
+        Mat4 worldToNodeTM = getWorldToNodeTransform();
+        worldToNodeTM.transformPoint(&p1);
+        Vec3 p2;
+        Vec2 newPos;
+        float* startX = _particleData.startPosX;
+        float* startY = _particleData.startPosY;
+        float* x = _particleData.posx;
+        float* y = _particleData.posy;
+        float* s = _particleData.size;
+        float* r = _particleData.rotation;
+        V3F_C4B_T2F_Quad* quadStart = startQuad;
+        for (int i = 0 ; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r)
+        {
+            p2.set(*startX, *startY, 0);
+            worldToNodeTM.transformPoint(&p2);
+            newPos.set(*x,*y);
+            p2 = p1 - p2;
+            newPos.x -= p2.x - pos.x;
+            newPos.y -= p2.y - pos.y;
+            updatePosWithParticle(quadStart, newPos, *s, *r);
+        }
+    }
+    else if( _positionType == PositionType::RELATIVE )
+    {
+        Vec2 newPos;
+        float* startX = _particleData.startPosX;
+        float* startY = _particleData.startPosY;
+        float* x = _particleData.posx;
+        float* y = _particleData.posy;
+        float* s = _particleData.size;
+        float* r = _particleData.rotation;
+        V3F_C4B_T2F_Quad* quadStart = startQuad;
+        for (int i = 0 ; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r)
+        {
+            newPos.set(*x, *y);
+            newPos.x = *x - (currentPosition.x - *startX);
+            newPos.y = *y - (currentPosition.y - *startY);
+            newPos += pos;
+            updatePosWithParticle(quadStart, newPos, *s, *r);
+        }
+    }
+    else
+    {
+        Vec2 newPos;
+        float* startX = _particleData.startPosX;
+        float* startY = _particleData.startPosY;
+        float* x = _particleData.posx;
+        float* y = _particleData.posy;
+        float* s = _particleData.size;
+        float* r = _particleData.rotation;
+        V3F_C4B_T2F_Quad* quadStart = startQuad;
+        for (int i = 0 ; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r)
+        {
+            newPos.set(*x + pos.x, *y + pos.y);
+            updatePosWithParticle(quadStart, newPos, *s, *r);
+        }
+    }
+    
+    //set color
+    if(_opacityModifyRGB)
+    {
+        V3F_C4B_T2F_Quad* quad = startQuad;
+        float* r = _particleData.colorR;
+        float* g = _particleData.colorG;
+        float* b = _particleData.colorB;
+        float* a = _particleData.colorA;
+        
+        for (int i = 0; i < _particleCount; ++i,++quad,++r,++g,++b,++a)
+        {
+            uint8_t colorR = *r * *a * 255;
+            uint8_t colorG = *g * *a * 255;
+            uint8_t colorB = *b * *a * 255;
+            uint8_t colorA = *a * 255;
+            quad->bl.colors.set(colorR, colorG, colorB, colorA);
+            quad->br.colors.set(colorR, colorG, colorB, colorA);
+            quad->tl.colors.set(colorR, colorG, colorB, colorA);
+            quad->tr.colors.set(colorR, colorG, colorB, colorA);
+        }
+    }
+    else
+    {
+        V3F_C4B_T2F_Quad* quad = startQuad;
+        float* r = _particleData.colorR;
+        float* g = _particleData.colorG;
+        float* b = _particleData.colorB;
+        float* a = _particleData.colorA;
+        
+        for (int i = 0; i < _particleCount; ++i,++quad,++r,++g,++b,++a)
+        {
+            uint8_t colorR = *r * 255;
+            uint8_t colorG = *g * 255;
+            uint8_t colorB = *b * 255;
+            uint8_t colorA = *a * 255;
+            quad->bl.colors.set(colorR, colorG, colorB, colorA);
+            quad->br.colors.set(colorR, colorG, colorB, colorA);
+            quad->tl.colors.set(colorR, colorG, colorB, colorA);
+            quad->tr.colors.set(colorR, colorG, colorB, colorA);
+        }
+    }
+}
+
+// overriding draw method
+void ParticleSystemQuad::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
+{
+    //quad command
+    if(_particleCount > 0)
+    {
+        auto programState = _quadCommand.getPipelineDescriptor().programState;
+        programState->setTexture(_textureLocation, 0, _texture->getBackendTexture());
+        
+        cocos2d::Mat4 projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+        programState->setUniform(_mvpMatrixLocaiton, projectionMat.m, sizeof(projectionMat.m));
+        
+        _quadCommand.init(_globalZOrder, _texture, _blendFunc, _quads, _particleCount, transform, flags);
+        renderer->addCommand(&_quadCommand);
+    }
+}
+
+void ParticleSystemQuad::setTotalParticles(int tp)
+{
+    // If we are setting the total number of particles to a number higher
+    // than what is allocated, we need to allocate new arrays
+    if( tp > _allocatedParticles )
+    {
+        // Allocate new memory
+        size_t quadsSize = sizeof(_quads[0]) * tp * 1;
+        size_t indicesSize = sizeof(_indices[0]) * tp * 6 * 1;
+
+        _particleData.release();
+        if (!_particleData.init(tp))
+        {
+            CCLOG("Particle system: not enough memory");
+            return;
+        }
+        V3F_C4B_T2F_Quad* quadsNew = (V3F_C4B_T2F_Quad*)realloc(_quads, quadsSize);
+        unsigned short* indicesNew = (unsigned short*)realloc(_indices, indicesSize);
+
+        if (quadsNew && indicesNew)
+        {
+            // Assign pointers
+            _quads = quadsNew;
+            _indices = indicesNew;
+
+            // Clear the memory
+            memset(_quads, 0, quadsSize);
+            memset(_indices, 0, indicesSize);
+            
+            _allocatedParticles = tp;
+        }
+        else
+        {
+            // Out of memory, failed to resize some array
+            if (quadsNew) _quads = quadsNew;
+            if (indicesNew) _indices = indicesNew;
+
+            CCLOG("Particle system: out of memory");
+            return;
+        }
+
+        _totalParticles = tp;
+
+        // Init particles
+        if (_batchNode)
+        {
+            for (int i = 0; i < _totalParticles; i++)
+            {
+                _particleData.atlasIndex[i] = i;
+            }
+        }
+
+        initIndices();
+//        setupVBO();
+        
+        // fixed http://www.cocos2d-x.org/issues/3990
+        // Updates texture coords.
+        updateTexCoords();
+    }
+    else
+    {
+        _totalParticles = tp;
+    }
+    
+    // fixed issue #5762
+    // reset the emission rate
+    setEmissionRate(_totalParticles / _life);
+    
+    resetSystem();
+}
+
+void ParticleSystemQuad::listenRendererRecreated(EventCustom* /*event*/)
+{
+    //when comes to foreground in android, _buffersVBO and _VAOname is a wild handle
+    //before recreating, we need to reset them to 0
+//    memset(_buffersVBO, 0, sizeof(_buffersVBO));
+//    if (Configuration::getInstance()->supportsShareableVAO())
+//    {
+//        _VAOname = 0;
+//        setupVBOandVAO();
+//    }
+//    else
+//    {
+//        setupVBO();
+//    }
+}
+
+bool ParticleSystemQuad::allocMemory()
+{
+    CCASSERT( !_batchNode, "Memory should not be alloced when not using batchNode");
+
+    CC_SAFE_FREE(_quads);
+    CC_SAFE_FREE(_indices);
+
+    _quads = (V3F_C4B_T2F_Quad*)malloc(_totalParticles * sizeof(V3F_C4B_T2F_Quad));
+    _indices = (unsigned short*)malloc(_totalParticles * 6 * sizeof(unsigned short));
+    
+    if( !_quads || !_indices) 
+    {
+        CCLOG("cocos2d: Particle system: not enough memory");
+        CC_SAFE_FREE(_quads);
+        CC_SAFE_FREE(_indices);
+
+        return false;
+    }
+
+    memset(_quads, 0, _totalParticles * sizeof(V3F_C4B_T2F_Quad));
+    memset(_indices, 0, _totalParticles * 6 * sizeof(unsigned short));
+
+    return true;
+}
+
+void ParticleSystemQuad::setBatchNode(ParticleBatchNode * batchNode)
+{
+    if( _batchNode != batchNode ) 
+    {
+        ParticleBatchNode* oldBatch = _batchNode;
+
+        ParticleSystem::setBatchNode(batchNode);
+
+        // NEW: is self render ?
+        if( ! batchNode ) 
+        {
+            allocMemory();
+            initIndices();
+            setTexture(oldBatch->getTexture());
+//            setupVBO();
+        }
+        // OLD: was it self render ? cleanup
+        else if( !oldBatch )
+        {
+            // copy current state to batch
+            V3F_C4B_T2F_Quad *batchQuads = _batchNode->getTextureAtlas()->getQuads();
+            V3F_C4B_T2F_Quad *quad = &(batchQuads[_atlasIndex] );
+            memcpy( quad, _quads, _totalParticles * sizeof(_quads[0]) );
+
+            CC_SAFE_FREE(_quads);
+            CC_SAFE_FREE(_indices);
+        }
+    }
+}
+
+ParticleSystemQuad * ParticleSystemQuad::create() {
+    ParticleSystemQuad *particleSystemQuad = new (std::nothrow) ParticleSystemQuad();
+    if (particleSystemQuad && particleSystemQuad->init())
+    {
+        particleSystemQuad->autorelease();
+        return particleSystemQuad;
+    }
+    CC_SAFE_DELETE(particleSystemQuad);
+    return nullptr;
+}
+
+std::string ParticleSystemQuad::getDescription() const
+{
+    return StringUtils::format("<ParticleSystemQuad | Tag = %d, Total Particles = %d>", _tag, _totalParticles);
+}
+NS_CC_END

+ 186 - 0
3rdparty/cocos2dx/include/cocos/2d/CCParticleSystemQuad.h

@@ -0,0 +1,186 @@
+/****************************************************************************
+Copyright (c) 2008-2010 Ricardo Quesada
+Copyright (c) 2009      Leonardo Kasperavičius
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2011      Zynga Inc.
+Copyright (c) 2013-2016 Chukong Technologies Inc.
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+ 
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#pragma once
+
+#include "2d/CCParticleSystem.h"
+#include "renderer/CCQuadCommand.h"
+
+NS_CC_BEGIN
+
+class SpriteFrame;
+class EventCustom;
+
+/**
+ * @addtogroup _2d
+ * @{
+ */
+
+/** @class ParticleSystemQuad
+ * @brief ParticleSystemQuad is a subclass of ParticleSystem.
+
+It includes all the features of ParticleSystem.
+
+Special features and Limitations:    
+- Particle size can be any float number.
+- The system can be scaled.
+- The particles can be rotated.
+- It supports subrects.
+- It supports batched rendering since 1.1.
+@since v0.8
+@js NA
+*/
+class CC_DLL ParticleSystemQuad : public ParticleSystem
+{
+public:
+
+    /** Creates a Particle Emitter.
+     *
+     * @return An autoreleased ParticleSystemQuad object.
+     */
+    static ParticleSystemQuad * create();
+    /** Creates a Particle Emitter with a number of particles.
+     *
+     * @param numberOfParticles A given number of particles.
+     * @return An autoreleased ParticleSystemQuad object.
+     */
+    static ParticleSystemQuad * createWithTotalParticles(int numberOfParticles);
+    /** Creates an initializes a ParticleSystemQuad from a plist file.
+     This plist files can be created manually or with Particle Designer.
+     *
+     * @param filename Particle plist file name.
+     * @return An autoreleased ParticleSystemQuad object.
+     */
+    static ParticleSystemQuad * create(const std::string& filename);
+    /** Creates a Particle Emitter with a dictionary.
+     * 
+     * @param dictionary Particle dictionary.
+     * @return An autoreleased ParticleSystemQuad object.
+     */
+    static ParticleSystemQuad * create(ValueMap &dictionary);
+
+    /** Sets a new SpriteFrame as particle.
+    WARNING: this method is experimental. Use setTextureWithRect instead.
+     *
+     * @param spriteFrame A given sprite frame as particle texture.
+    @since v0.99.4
+    */
+    void setDisplayFrame(SpriteFrame *spriteFrame);
+
+    /** Sets a new texture with a rect. The rect is in Points.
+     @since v0.99.4
+     * @js NA
+     * @lua NA
+     *
+     * @param texture A given texture.
+     8 @param rect A given rect, in points.
+     */
+    void setTextureWithRect(Texture2D *texture, const Rect& rect);
+
+    /** Listen the event that renderer was recreated on Android/WP8.
+     * @js NA
+     * @lua NA
+     *
+     * @param event the event that renderer was recreated on Android/WP8.
+     */
+    void listenRendererRecreated(EventCustom* event);
+
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual void setTexture(Texture2D* texture) override;
+    /**
+     * @js NA
+     * @lua NA
+     */    
+    virtual void updateParticleQuads() override;
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
+
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual void setBatchNode(ParticleBatchNode* batchNode) override;
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual void setTotalParticles(int tp) override;
+
+    virtual std::string getDescription() const override;
+    
+CC_CONSTRUCTOR_ACCESS:
+    /**
+     * @js ctor
+     */
+    ParticleSystemQuad();
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual ~ParticleSystemQuad();
+    
+    // Overrides
+    /**
+     * @js NA
+     * @lua NA
+     */
+    virtual bool initWithTotalParticles(int numberOfParticles) override;
+
+protected:
+    /** initializes the indices for the vertices*/
+    void initIndices();
+    
+    /** initializes the texture with a rectangle measured Points */
+    void initTexCoordsWithRect(const Rect& rect);
+    
+    /** Updates texture coords */
+    void updateTexCoords();
+
+    bool allocMemory();
+
+    V3F_C4B_T2F_Quad    *_quads = nullptr;        // quads to be rendered
+    unsigned short      *_indices = nullptr;      // indices
+
+    QuadCommand _quadCommand;           // quad command
+    
+    backend::UniformLocation _mvpMatrixLocaiton;
+    backend::UniformLocation _textureLocation;    
+private:
+    CC_DISALLOW_COPY_AND_ASSIGN(ParticleSystemQuad);
+};
+
+// end of _2d group
+/// @}
+
+NS_CC_END

+ 604 - 0
3rdparty/cocos2dx/include/cocos/2d/CCProgressTimer.cpp

@@ -0,0 +1,604 @@
+/****************************************************************************
+Copyright (c) 2010      Lam Pham
+Copyright (c) 2010-2012 cocos2d-x.org
+Copyright (c) 2013-2017 Chukong Technologies Inc
+Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
+
+http://www.cocos2d-x.org
+
+Permission is hereby granted, free 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.
+****************************************************************************/
+#include "2d/CCProgressTimer.h"
+#include <algorithm>
+#include <stddef.h> // offsetof
+#include "base/ccTypes.h"
+#include "base/ccMacros.h"
+#include "base/CCDirector.h"
+#include "2d/CCSprite.h"
+#include "renderer/CCRenderer.h"
+#include "base/ccUtils.h"
+#include "renderer/ccShaders.h"
+#include "renderer/backend/ProgramState.h"
+
+NS_CC_BEGIN
+
+#define kProgressTextureCoordsCount 4
+//  kProgressTextureCoords holds points {0,1} {0,0} {1,0} {1,1} we can represent it as bits
+const char kProgressTextureCoords = 0x4b;
+
+namespace
+{
+    backend::ProgramState* initPipelineDescriptor(cocos2d::CustomCommand& command, bool ridal, backend::UniformLocation &locMVP, backend::UniformLocation &locTexture)
+    {
+        auto& pipelieDescriptor = command.getPipelineDescriptor();
+        auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR);
+        auto programState = new (std::nothrow) backend::ProgramState(program);
+        CC_SAFE_RELEASE(pipelieDescriptor.programState);
+        pipelieDescriptor.programState = programState;
+        
+        //set vertexLayout according to V2F_C4B_T2F structure
+        auto vertexLayout = programState->getVertexLayout();
+        const auto& attributeInfo = programState->getProgram()->getActiveAttributes();
+        auto iter = attributeInfo.find("a_position");
+        if(iter != attributeInfo.end())
+        {
+            vertexLayout->setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT2, 0, false);
+        }
+        iter = attributeInfo.find("a_texCoord");
+        if(iter != attributeInfo.end())
+        {
+            vertexLayout->setAttribute("a_texCoord", iter->second.location, backend::VertexFormat::FLOAT2, offsetof(V2F_C4B_T2F, texCoords), false);
+        }
+        iter = attributeInfo.find("a_color");
+        if(iter != attributeInfo.end())
+        {
+            vertexLayout->setAttribute("a_color", iter->second.location, backend::VertexFormat::UBYTE4, offsetof(V2F_C4B_T2F, colors), true);
+        }
+        vertexLayout->setLayout(sizeof(V2F_C4B_T2F));
+
+        if (ridal)
+        {
+            command.setDrawType(CustomCommand::DrawType::ELEMENT);
+            command.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
+        }
+        else
+        {
+            command.setDrawType(CustomCommand::DrawType::ARRAY);
+            command.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE_STRIP);
+        }
+
+
+        locMVP = programState->getUniformLocation("u_MVPMatrix");
+        locTexture = programState->getUniformLocation("u_texture");
+
+        return programState;
+    }
+}
+
+
+ProgressTimer* ProgressTimer::create(Sprite* sp)
+{
+    ProgressTimer *progressTimer = new (std::nothrow) ProgressTimer();
+    if (progressTimer && progressTimer->initWithSprite(sp))
+    {
+        progressTimer->autorelease();
+        return progressTimer;
+    }
+    
+    delete progressTimer;
+    return nullptr;
+}
+
+bool ProgressTimer::initWithSprite(Sprite* sp)
+{
+    setAnchorPoint(Vec2(0.5f,0.5f));
+    setMidpoint(Vec2(0.5f, 0.5f));
+    setBarChangeRate(Vec2(1,1));
+    setSprite(sp);
+
+    CC_SAFE_RELEASE(_programState);
+    CC_SAFE_RELEASE(_programState2);
+    _programState = initPipelineDescriptor(_customCommand, true, _locMVP1, _locTex1); 
+    _programState2 = initPipelineDescriptor(_customCommand2, false, _locMVP2, _locTex2);
+    
+    return true;
+}
+
+ProgressTimer::~ProgressTimer()
+{
+    CC_SAFE_RELEASE(_sprite);
+    CC_SAFE_RELEASE(_programState);
+    CC_SAFE_RELEASE(_programState2);
+}
+
+void ProgressTimer::setPercentage(float percentage)
+{
+    if (_percentage != percentage)
+    {
+        _percentage = clampf(percentage, 0, 100);
+        updateProgress();
+    }
+}
+
+void ProgressTimer::setSprite(Sprite *sprite)
+{
+    if (_sprite != sprite)
+    {
+#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
+        if (sEngine)
+        {
+            if (_sprite)
+                sEngine->releaseScriptObject(this, _sprite);
+            if (sprite)
+                sEngine->retainScriptObject(this, sprite);
+        }
+#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
+        CC_SAFE_RETAIN(sprite);
+        CC_SAFE_RELEASE(_sprite);
+        _sprite = sprite;
+        setContentSize(_sprite->getContentSize());
+
+        //    Every time we set a new sprite, we free the current vertex data
+        if (!_vertexData.empty())
+        {
+            _vertexData.clear();
+            updateProgress();
+        }
+    }        
+}
+
+void ProgressTimer::setType(Type type)
+{
+
+    if (type == Type::RADIAL)
+    {
+        _customCommand.setDrawType(CustomCommand::DrawType::ELEMENT);
+        _customCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
+    }
+    else
+    {
+        _customCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE_STRIP);
+        _customCommand.setDrawType(CustomCommand::DrawType::ARRAY);
+    }
+
+    if (type != _type)
+    {
+        //    release all previous information
+        _vertexData.clear();
+        _type = type;
+    }
+}
+
+void ProgressTimer::setReverseDirection(bool reverse)
+{
+    if( _reverseDirection != reverse ) {
+        _reverseDirection = reverse;
+        //    release all previous information
+        _vertexData.clear();
+    }
+}
+
+// Interval
+
+///
+//    @returns the vertex position from the texture coordinate
+///
+Tex2F ProgressTimer::textureCoordFromAlphaPoint(Vec2 alpha)
+{
+    Tex2F ret(0.0f, 0.0f);
+    if (!_sprite) {
+        return ret;
+    }
+    V3F_C4B_T2F_Quad quad = _sprite->getQuad();
+    Vec2 min(quad.bl.texCoords.u,quad.bl.texCoords.v);
+    Vec2 max(quad.tr.texCoords.u,quad.tr.texCoords.v);
+    //  Fix bug #1303 so that progress timer handles sprite frame texture rotation
+    if (_sprite->isTextureRectRotated()) {
+        std::swap(alpha.x, alpha.y);
+    }
+    return Tex2F(min.x * (1.f - alpha.x) + max.x * alpha.x, min.y * (1.f - alpha.y) + max.y * alpha.y);
+}
+
+Vec2 ProgressTimer::vertexFromAlphaPoint(Vec2 alpha)
+{
+    Vec2 ret(0.0f, 0.0f);
+    if (!_sprite) {
+        return ret;
+    }
+    V3F_C4B_T2F_Quad quad = _sprite->getQuad();
+    Vec2 min(quad.bl.vertices.x,quad.bl.vertices.y);
+    Vec2 max(quad.tr.vertices.x,quad.tr.vertices.y);
+    ret.x = min.x * (1.f - alpha.x) + max.x * alpha.x;
+    ret.y = min.y * (1.f - alpha.y) + max.y * alpha.y;
+    return ret;
+}
+
+void ProgressTimer::updateColor()
+{
+    if (!_sprite)
+        return;
+
+    if (!_vertexData.empty())
+    {
+        const Color4B& sc = _sprite->getQuad().tl.colors;
+        for (int i = 0; i < _vertexData.size(); ++i)
+        {
+            _vertexData[i].colors = sc;
+        }
+    }
+}
+
+void ProgressTimer::updateProgress()
+{
+    switch (_type)
+    {
+    case Type::RADIAL:
+        updateRadial();
+        break;
+    case Type::BAR:
+        updateBar();
+        break;
+    default:
+        break;
+    }
+}
+
+void ProgressTimer::setAnchorPoint(const Vec2& anchorPoint)
+{
+    Node::setAnchorPoint(anchorPoint);
+}
+
+Vec2 ProgressTimer::getMidpoint() const
+{
+    return _midpoint;
+}
+
+void ProgressTimer::setColor(const Color3B &color)
+{
+    _sprite->setColor(color);
+    updateColor();
+}
+
+const Color3B& ProgressTimer::getColor() const
+{
+    return _sprite->getColor();
+}
+
+void ProgressTimer::setOpacity(uint8_t opacity)
+{
+    _sprite->setOpacity(opacity);
+    updateColor();
+}
+
+uint8_t ProgressTimer::getOpacity() const
+{
+    return _sprite->getOpacity();
+}
+
+void ProgressTimer::setMidpoint(const Vec2& midPoint)
+{
+    _midpoint = midPoint.getClampPoint(Vec2::ZERO, Vec2(1, 1));
+}
+
+///
+//    Update does the work of mapping the texture onto the triangles
+//    It now doesn't occur the cost of free/alloc data every update cycle.
+//    It also only changes the percentage point but no other points if they have not
+//    been modified.
+//    
+//    It now deals with flipped texture. If you run into this problem, just use the
+//    sprite property and enable the methods flipX, flipY.
+///
+void ProgressTimer::updateRadial()
+{
+    if (!_sprite) {
+        return;
+    }
+    float alpha = _percentage / 100.f;
+
+    float angle = 2.f*((float)M_PI) * ( _reverseDirection ? alpha : 1.0f - alpha);
+
+    //    We find the vector to do a hit detection based on the percentage
+    //    We know the first vector is the one @ 12 o'clock (top,mid) so we rotate
+    //    from that by the progress angle around the _midpoint pivot
+    Vec2 topMid(_midpoint.x, 1.f);
+    Vec2 percentagePt = topMid.rotateByAngle(_midpoint, angle);
+
+
+    int index = 0;
+    Vec2 hit;
+
+    if (alpha == 0.f) {
+        //    More efficient since we don't always need to check intersection
+        //    If the alpha is zero then the hit point is top mid and the index is 0.
+        hit = topMid;
+        index = 0;
+    } else if (alpha == 1.f) {
+        //    More efficient since we don't always need to check intersection
+        //    If the alpha is one then the hit point is top mid and the index is 4.
+        hit = topMid;
+        index = 4;
+    } else {
+        //    We run a for loop checking the edges of the texture to find the
+        //    intersection point
+        //    We loop through five points since the top is split in half
+
+        float min_t = FLT_MAX;
+
+        for (int i = 0; i <= kProgressTextureCoordsCount; ++i) {
+            int pIndex = (i + (kProgressTextureCoordsCount - 1))%kProgressTextureCoordsCount;
+
+            Vec2 edgePtA = boundaryTexCoord(i % kProgressTextureCoordsCount);
+            Vec2 edgePtB = boundaryTexCoord(pIndex);
+
+            //    Remember that the top edge is split in half for the 12 o'clock position
+            //    Let's deal with that here by finding the correct endpoints
+            if(i == 0){
+                edgePtB = edgePtA.lerp(edgePtB, 1-_midpoint.x);
+            } else if(i == 4){
+                edgePtA = edgePtA.lerp(edgePtB, 1-_midpoint.x);
+            }
+
+            //    s and t are returned by ccpLineIntersect
+            float s = 0, t = 0;
+            if(Vec2::isLineIntersect(edgePtA, edgePtB, _midpoint, percentagePt, &s, &t))
+            {
+
+                //    Since our hit test is on rays we have to deal with the top edge
+                //    being in split in half so we have to test as a segment
+                if ((i == 0 || i == 4)) {
+                    //    s represents the point between edgePtA--edgePtB
+                    if (!(0.f <= s && s <= 1.f)) {
+                        continue;
+                    }
+                }
+                //    As long as our t isn't negative we are at least finding a
+                //    correct hitpoint from _midpoint to percentagePt.
+                if (t >= 0.f) {
+                    //    Because the percentage line and all the texture edges are
+                    //    rays we should only account for the shortest intersection
+                    if (t < min_t) {
+                        min_t = t;
+                        index = i;
+                    }
+                }
+            }
+        }
+
+        //    Now that we have the minimum magnitude we can use that to find our intersection
+        hit = _midpoint+ ((percentagePt - _midpoint) * min_t);
+    }
+
+
+    //    The size of the vertex data is the index from the hitpoint
+    //    the 3 is for the _midpoint, 12 o'clock point and hitpoint position.
+
+    bool sameIndexCount = true;
+    if (_vertexData.size() != index + 3)
+    {
+        sameIndexCount = false;
+        _vertexData.resize(index + 3);
+        _customCommand.createVertexBuffer(sizeof(_vertexData[0]), (unsigned int)_vertexData.size(),  CustomCommand::BufferUsage::DYNAMIC);
+    }
+
+    if (_indexData.size() != 3 + 3 * index)
+    {
+        _indexData.resize(3 + 3 * index);
+        _customCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, (unsigned int)_indexData.size(), CustomCommand::BufferUsage::STATIC);
+    }
+
+    if (!sameIndexCount)
+    {
+        //    First we populate the array with the _midpoint, then all
+        //    vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
+        _vertexData[0].texCoords = textureCoordFromAlphaPoint(_midpoint);
+        _vertexData[0].vertices = vertexFromAlphaPoint(_midpoint);
+
+        _vertexData[1].texCoords = textureCoordFromAlphaPoint(topMid);
+        _vertexData[1].vertices = vertexFromAlphaPoint(topMid);
+
+        for(int i = 0; i < index; ++i)
+        {
+            Vec2 alphaPoint = boundaryTexCoord(i);
+            _vertexData[i+2].texCoords = textureCoordFromAlphaPoint(alphaPoint);
+            _vertexData[i+2].vertices = vertexFromAlphaPoint(alphaPoint);
+        }
+
+        for (int i = 0; i < index + 1; i++)
+        {
+            _indexData[i * 3] = 0;
+            _indexData[i * 3 + 1] = i + 2;
+            _indexData[i * 3 + 2] = i + 1;
+        }
+
+        _customCommand.updateIndexBuffer(_indexData.data(), (unsigned int)(_indexData.size() * sizeof(_indexData[0])) );
+    }
+
+    //    hitpoint will go last
+    _vertexData[_vertexData.size() - 1].texCoords = textureCoordFromAlphaPoint(hit);
+    _vertexData[_vertexData.size() - 1].vertices = vertexFromAlphaPoint(hit);
+
+    updateColor();
+    _customCommand.updateVertexBuffer(_vertexData.data(), (unsigned int)(sizeof(_vertexData[0]) * _vertexData.size()) );
+}
+
+///
+//    Update does the work of mapping the texture onto the triangles for the bar
+//    It now doesn't occur the cost of free/alloc data every update cycle.
+//    It also only changes the percentage point but no other points if they have not
+//    been modified.
+//    
+//    It now deals with flipped texture. If you run into this problem, just use the
+//    sprite property and enable the methods flipX, flipY.
+///
+void ProgressTimer::updateBar()
+{
+    if (!_sprite)
+        return;
+    
+    float alpha = _percentage / 100.0f;
+    Vec2 alphaOffset = Vec2(1.0f * (1.0f - _barChangeRate.x) + alpha * _barChangeRate.x, 1.0f * (1.0f - _barChangeRate.y) + alpha * _barChangeRate.y) * 0.5f;
+    Vec2 min = _midpoint - alphaOffset;
+    Vec2 max = _midpoint + alphaOffset;
+
+    if (min.x < 0.f) {
+        max.x += -min.x;
+        min.x = 0.f;
+    }
+
+    if (max.x > 1.f) {
+        min.x -= max.x - 1.f;
+        max.x = 1.f;
+    }
+
+    if (min.y < 0.f) {
+        max.y += -min.y;
+        min.y = 0.f;
+    }
+
+    if (max.y > 1.f) {
+        min.y -= max.y - 1.f;
+        max.y = 1.f;
+    }
+
+
+    if (!_reverseDirection) {
+        
+        if (_vertexData.size() != 4)
+        {
+            _vertexData.resize(4);
+            _customCommand.createVertexBuffer(sizeof(_vertexData[0]),(unsigned int) _vertexData.size(), CustomCommand::BufferUsage::DYNAMIC);
+        }
+
+        //    TOPLEFT
+        _vertexData[0].texCoords = textureCoordFromAlphaPoint(Vec2(min.x,max.y));
+        _vertexData[0].vertices = vertexFromAlphaPoint(Vec2(min.x,max.y));
+
+        //    BOTLEFT
+        _vertexData[1].texCoords = textureCoordFromAlphaPoint(Vec2(min.x,min.y));
+        _vertexData[1].vertices = vertexFromAlphaPoint(Vec2(min.x,min.y));
+
+        //    TOPRIGHT
+        _vertexData[2].texCoords = textureCoordFromAlphaPoint(Vec2(max.x,max.y));
+        _vertexData[2].vertices = vertexFromAlphaPoint(Vec2(max.x,max.y));
+
+        //    BOTRIGHT
+        _vertexData[3].texCoords = textureCoordFromAlphaPoint(Vec2(max.x,min.y));
+        _vertexData[3].vertices = vertexFromAlphaPoint(Vec2(max.x,min.y));
+
+        updateColor();
+
+        _customCommand.updateVertexBuffer(_vertexData.data(), (unsigned int)(sizeof(_vertexData[0]) * _vertexData.size()));
+    } else {
+        if(_vertexData.size() != 8) {
+            _vertexData.resize(8);
+            _customCommand.createVertexBuffer(sizeof(_vertexData[0]), (unsigned int)(_vertexData.size() / 2), CustomCommand::BufferUsage::DYNAMIC);
+            _customCommand2.createVertexBuffer(sizeof(_vertexData[0]), (unsigned int)(_vertexData.size() / 2), CustomCommand::BufferUsage::DYNAMIC);
+            //    TOPLEFT 1
+            _vertexData[0].texCoords = textureCoordFromAlphaPoint(Vec2(0,1));
+            _vertexData[0].vertices = vertexFromAlphaPoint(Vec2(0,1));
+
+            //    BOTLEFT 1
+            _vertexData[1].texCoords = textureCoordFromAlphaPoint(Vec2(0,0));
+            _vertexData[1].vertices = vertexFromAlphaPoint(Vec2(0,0));
+
+            //    TOPRIGHT 2
+            _vertexData[6].texCoords = textureCoordFromAlphaPoint(Vec2(1,1));
+            _vertexData[6].vertices = vertexFromAlphaPoint(Vec2(1,1));
+
+            //    BOTRIGHT 2
+            _vertexData[7].texCoords = textureCoordFromAlphaPoint(Vec2(1,0));
+            _vertexData[7].vertices = vertexFromAlphaPoint(Vec2(1,0));
+        }
+
+        //    TOPRIGHT 1
+        _vertexData[2].texCoords = textureCoordFromAlphaPoint(Vec2(min.x,max.y));
+        _vertexData[2].vertices = vertexFromAlphaPoint(Vec2(min.x,max.y));
+
+        //    BOTRIGHT 1
+        _vertexData[3].texCoords = textureCoordFromAlphaPoint(Vec2(min.x,min.y));
+        _vertexData[3].vertices = vertexFromAlphaPoint(Vec2(min.x,min.y));
+
+        //    TOPLEFT 2
+        _vertexData[4].texCoords = textureCoordFromAlphaPoint(Vec2(max.x,max.y));
+        _vertexData[4].vertices = vertexFromAlphaPoint(Vec2(max.x,max.y));
+
+        //    BOTLEFT 2
+        _vertexData[5].texCoords = textureCoordFromAlphaPoint(Vec2(max.x,min.y));
+        _vertexData[5].vertices = vertexFromAlphaPoint(Vec2(max.x,min.y));
+
+        updateColor();
+
+        _customCommand.updateVertexBuffer(_vertexData.data(), (unsigned int)(sizeof(_vertexData[0]) * _vertexData.size() / 2));
+        _customCommand2.updateVertexBuffer((char*)_vertexData.data() + sizeof(_vertexData[0]) * _vertexData.size() / 2,
+                                           (unsigned int)(sizeof(_vertexData[0]) * _vertexData.size() / 2));
+    }
+}
+
+Vec2 ProgressTimer::boundaryTexCoord(char index)
+{
+    if (index < kProgressTextureCoordsCount)
+    {
+        if (_reverseDirection)
+            return Vec2((kProgressTextureCoords>>(7-(index<<1)))&1,(kProgressTextureCoords>>(7-((index<<1)+1)))&1);
+        else
+            return Vec2((kProgressTextureCoords>>((index<<1)+1))&1,(kProgressTextureCoords>>(index<<1))&1);
+    }
+    else
+        return Vec2::ZERO;
+}
+
+void ProgressTimer::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
+{
+    if( _vertexData.empty() || ! _sprite)
+        return;
+
+    const cocos2d::Mat4& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
+    Mat4 finalMat = projectionMat * transform;
+    _programState->setUniform(_locMVP1, finalMat.m, sizeof(finalMat.m));
+    _programState->setTexture(_locTex1, 0, _sprite->getTexture()->getBackendTexture());
+
+    if(_type == Type::BAR)
+    {
+        if (!_reverseDirection)
+        {
+            _customCommand.init(_globalZOrder, _sprite->getBlendFunc());
+            renderer->addCommand(&_customCommand);
+        }
+        else
+        {
+            _customCommand.init(_globalZOrder, _sprite->getBlendFunc());
+            renderer->addCommand(&_customCommand);
+
+            _customCommand2.init(_globalZOrder, _sprite->getBlendFunc());
+            _programState2->setUniform(_locMVP2, finalMat.m, sizeof(finalMat.m));
+            _programState2->setTexture(_locTex2, 0, _sprite->getTexture()->getBackendTexture());
+            renderer->addCommand(&_customCommand2);
+        }
+    }
+    else
+    {
+        _customCommand.init(_globalZOrder, _sprite->getBlendFunc());
+        renderer->addCommand(&_customCommand);
+    }
+}
+
+
+NS_CC_END

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů