Josh Engebretson 9 жил өмнө
parent
commit
47fd8f9abb
100 өөрчлөгдсөн 4912 нэмэгдсэн , 3331 устгасан
  1. 334 284
      Source/Atomic/Atomic2D/AnimatedSprite2D.cpp
  2. 61 59
      Source/Atomic/Atomic2D/AnimatedSprite2D.h
  3. 0 151
      Source/Atomic/Atomic2D/Animation2D.cpp
  4. 0 153
      Source/Atomic/Atomic2D/Animation2D.h
  5. 353 345
      Source/Atomic/Atomic2D/AnimationSet2D.cpp
  6. 67 24
      Source/Atomic/Atomic2D/AnimationSet2D.h
  7. 33 42
      Source/Atomic/Atomic2D/Atomic2D.cpp
  8. 4 4
      Source/Atomic/Atomic2D/Atomic2D.h
  9. 14 14
      Source/Atomic/Atomic2D/CollisionBox2D.cpp
  10. 5 5
      Source/Atomic/Atomic2D/CollisionBox2D.h
  11. 10 10
      Source/Atomic/Atomic2D/CollisionChain2D.cpp
  12. 5 5
      Source/Atomic/Atomic2D/CollisionChain2D.h
  13. 10 10
      Source/Atomic/Atomic2D/CollisionCircle2D.cpp
  14. 5 5
      Source/Atomic/Atomic2D/CollisionCircle2D.h
  15. 10 10
      Source/Atomic/Atomic2D/CollisionEdge2D.cpp
  16. 5 5
      Source/Atomic/Atomic2D/CollisionEdge2D.h
  17. 9 9
      Source/Atomic/Atomic2D/CollisionPolygon2D.cpp
  18. 5 5
      Source/Atomic/Atomic2D/CollisionPolygon2D.h
  19. 17 15
      Source/Atomic/Atomic2D/CollisionShape2D.cpp
  20. 4 4
      Source/Atomic/Atomic2D/CollisionShape2D.h
  21. 9 9
      Source/Atomic/Atomic2D/Constraint2D.cpp
  22. 4 4
      Source/Atomic/Atomic2D/Constraint2D.h
  23. 15 13
      Source/Atomic/Atomic2D/ConstraintDistance2D.cpp
  24. 5 5
      Source/Atomic/Atomic2D/ConstraintDistance2D.h
  25. 13 11
      Source/Atomic/Atomic2D/ConstraintFriction2D.cpp
  26. 5 5
      Source/Atomic/Atomic2D/ConstraintFriction2D.h
  27. 11 9
      Source/Atomic/Atomic2D/ConstraintGear2D.cpp
  28. 5 5
      Source/Atomic/Atomic2D/ConstraintGear2D.h
  29. 16 14
      Source/Atomic/Atomic2D/ConstraintMotor2D.cpp
  30. 5 5
      Source/Atomic/Atomic2D/ConstraintMotor2D.h
  31. 15 13
      Source/Atomic/Atomic2D/ConstraintMouse2D.cpp
  32. 5 5
      Source/Atomic/Atomic2D/ConstraintMouse2D.h
  33. 19 17
      Source/Atomic/Atomic2D/ConstraintPrismatic2D.cpp
  34. 5 5
      Source/Atomic/Atomic2D/ConstraintPrismatic2D.h
  35. 15 13
      Source/Atomic/Atomic2D/ConstraintPulley2D.cpp
  36. 5 5
      Source/Atomic/Atomic2D/ConstraintPulley2D.h
  37. 18 16
      Source/Atomic/Atomic2D/ConstraintRevolute2D.cpp
  38. 5 5
      Source/Atomic/Atomic2D/ConstraintRevolute2D.h
  39. 13 11
      Source/Atomic/Atomic2D/ConstraintRope2D.cpp
  40. 5 5
      Source/Atomic/Atomic2D/ConstraintRope2D.h
  41. 13 11
      Source/Atomic/Atomic2D/ConstraintWeld2D.cpp
  42. 5 5
      Source/Atomic/Atomic2D/ConstraintWeld2D.h
  43. 18 16
      Source/Atomic/Atomic2D/ConstraintWheel2D.cpp
  44. 5 5
      Source/Atomic/Atomic2D/ConstraintWheel2D.h
  45. 9 7
      Source/Atomic/Atomic2D/Drawable2D.cpp
  46. 10 5
      Source/Atomic/Atomic2D/Drawable2D.h
  47. 58 10
      Source/Atomic/Atomic2D/ParticleEffect2D.cpp
  48. 8 6
      Source/Atomic/Atomic2D/ParticleEffect2D.h
  49. 31 45
      Source/Atomic/Atomic2D/ParticleEmitter2D.cpp
  50. 8 8
      Source/Atomic/Atomic2D/ParticleEmitter2D.h
  51. 19 27
      Source/Atomic/Atomic2D/PhysicsEvents2D.h
  52. 2 2
      Source/Atomic/Atomic2D/PhysicsUtils2D.h
  53. 88 40
      Source/Atomic/Atomic2D/PhysicsWorld2D.cpp
  54. 35 9
      Source/Atomic/Atomic2D/PhysicsWorld2D.h
  55. 68 70
      Source/Atomic/Atomic2D/Renderer2D.cpp
  56. 12 16
      Source/Atomic/Atomic2D/Renderer2D.h
  57. 176 99
      Source/Atomic/Atomic2D/RigidBody2D.cpp
  58. 22 27
      Source/Atomic/Atomic2D/RigidBody2D.h
  59. 26 18
      Source/Atomic/Atomic2D/Sprite2D.cpp
  60. 12 4
      Source/Atomic/Atomic2D/Sprite2D.h
  61. 109 17
      Source/Atomic/Atomic2D/SpriteSheet2D.cpp
  62. 18 8
      Source/Atomic/Atomic2D/SpriteSheet2D.h
  63. 759 0
      Source/Atomic/Atomic2D/SpriterData2D.cpp
  64. 311 0
      Source/Atomic/Atomic2D/SpriterData2D.h
  65. 313 0
      Source/Atomic/Atomic2D/SpriterInstance2D.cpp
  66. 112 0
      Source/Atomic/Atomic2D/SpriterInstance2D.h
  67. 21 41
      Source/Atomic/Atomic2D/StaticSprite2D.cpp
  68. 5 7
      Source/Atomic/Atomic2D/StaticSprite2D.h
  69. 16 20
      Source/Atomic/Atomic2D/TileMap2D.cpp
  70. 5 8
      Source/Atomic/Atomic2D/TileMap2D.h
  71. 27 39
      Source/Atomic/Atomic2D/TileMapDefs2D.cpp
  72. 10 28
      Source/Atomic/Atomic2D/TileMapDefs2D.h
  73. 7 41
      Source/Atomic/Atomic2D/TileMapLayer2D.cpp
  74. 5 7
      Source/Atomic/Atomic2D/TileMapLayer2D.h
  75. 98 123
      Source/Atomic/Atomic2D/TmxFile2D.cpp
  76. 20 22
      Source/Atomic/Atomic2D/TmxFile2D.h
  77. 142 133
      Source/Atomic/Atomic3D/AnimatedModel.cpp
  78. 13 13
      Source/Atomic/Atomic3D/AnimatedModel.h
  79. 157 36
      Source/Atomic/Atomic3D/Animation.cpp
  80. 56 17
      Source/Atomic/Atomic3D/Animation.h
  81. 60 159
      Source/Atomic/Atomic3D/AnimationController.cpp
  82. 9 37
      Source/Atomic/Atomic3D/AnimationController.h
  83. 121 135
      Source/Atomic/Atomic3D/AnimationState.cpp
  84. 21 11
      Source/Atomic/Atomic3D/AnimationState.h
  85. 312 147
      Source/Atomic/Atomic3D/BillboardSet.cpp
  86. 26 41
      Source/Atomic/Atomic3D/BillboardSet.h
  87. 29 26
      Source/Atomic/Atomic3D/CustomGeometry.cpp
  88. 4 4
      Source/Atomic/Atomic3D/CustomGeometry.h
  89. 27 27
      Source/Atomic/Atomic3D/DecalSet.cpp
  90. 6 6
      Source/Atomic/Atomic3D/DecalSet.h
  91. 65 104
      Source/Atomic/Atomic3D/Model.cpp
  92. 7 28
      Source/Atomic/Atomic3D/Model.h
  93. 95 191
      Source/Atomic/Atomic3D/ParticleEffect.cpp
  94. 25 8
      Source/Atomic/Atomic3D/ParticleEffect.h
  95. 108 61
      Source/Atomic/Atomic3D/ParticleEmitter.cpp
  96. 8 6
      Source/Atomic/Atomic3D/ParticleEmitter.h
  97. 4 4
      Source/Atomic/Atomic3D/Skeleton.cpp
  98. 3 3
      Source/Atomic/Atomic3D/Skeleton.h
  99. 4 4
      Source/Atomic/Atomic3D/Skybox.cpp
  100. 5 5
      Source/Atomic/Atomic3D/Skybox.h

+ 334 - 284
Source/Atomic/Atomic2D/AnimatedSprite2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -27,16 +27,21 @@
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
 #include "../Scene/SceneEvents.h"
 #include "../Scene/SceneEvents.h"
-#include "../Atomic2D/AnimatedSprite2D.h"
-#include "../Atomic2D/AnimationSet2D.h"
-#include "../Atomic2D/Sprite2D.h"
+#include "../Urho2D/AnimatedSprite2D.h"
+#include "../Urho2D/AnimationSet2D.h"
+#include "../Urho2D/Sprite2D.h"
+#include "../Urho2D/SpriterInstance2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+#ifdef URHO3D_SPINE
+#include <spine/spine.h>
+#endif 
+
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 extern const char* blendModeNames[];
 extern const char* blendModeNames[];
 
 
 const char* loopModeNames[] =
 const char* loopModeNames[] =
@@ -49,29 +54,34 @@ const char* loopModeNames[] =
 
 
 AnimatedSprite2D::AnimatedSprite2D(Context* context) :
 AnimatedSprite2D::AnimatedSprite2D(Context* context) :
     StaticSprite2D(context),
     StaticSprite2D(context),
+#ifdef URHO3D_SPINE
+    skeleton_(0),
+    animationStateData_(0),
+    animationState_(0),
+#endif
+    spriterInstance_(0),
     speed_(1.0f),
     speed_(1.0f),
-    loopMode_(LM_DEFAULT),
-    looped_(false),
-    currentTime_(0.0f),
-    numTracks_(0)
+    loopMode_(LM_DEFAULT)
 {
 {
 }
 }
 
 
 AnimatedSprite2D::~AnimatedSprite2D()
 AnimatedSprite2D::~AnimatedSprite2D()
 {
 {
+    Dispose();
 }
 }
 
 
 void AnimatedSprite2D::RegisterObject(Context* context)
 void AnimatedSprite2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<AnimatedSprite2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<AnimatedSprite2D>(URHO2D_CATEGORY);
 
 
-    COPY_BASE_ATTRIBUTES(StaticSprite2D);
-    REMOVE_ATTRIBUTE("Sprite");
-    ACCESSOR_ATTRIBUTE("Speed", GetSpeed, SetSpeed, float, 1.0f, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Animation Set", GetAnimationSetAttr, SetAnimationSetAttr, ResourceRef,
+    URHO3D_COPY_BASE_ATTRIBUTES(StaticSprite2D);
+    URHO3D_REMOVE_ATTRIBUTE("Sprite");
+    URHO3D_ACCESSOR_ATTRIBUTE("Speed", GetSpeed, SetSpeed, float, 1.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Entity", GetEntity, SetEntity, String, String::EMPTY, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Animation Set", GetAnimationSetAttr, SetAnimationSetAttr, ResourceRef,
         ResourceRef(AnimatedSprite2D::GetTypeStatic()), AM_DEFAULT);
         ResourceRef(AnimatedSprite2D::GetTypeStatic()), AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Animation", GetAnimation, SetAnimationAttr, String, String::EMPTY, AM_DEFAULT);
-    ENUM_ACCESSOR_ATTRIBUTE("Loop Mode", GetLoopMode, SetLoopMode, LoopMode2D, loopModeNames, LM_DEFAULT, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Animation", GetAnimation, SetAnimationAttr, String, String::EMPTY, AM_DEFAULT);
+    URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Loop Mode", GetLoopMode, SetLoopMode, LoopMode2D, loopModeNames, LM_DEFAULT, AM_DEFAULT);
 }
 }
 
 
 void AnimatedSprite2D::OnSetEnabled()
 void AnimatedSprite2D::OnSetEnabled()
@@ -84,84 +94,109 @@ void AnimatedSprite2D::OnSetEnabled()
     if (scene)
     if (scene)
     {
     {
         if (enabled)
         if (enabled)
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(AnimatedSprite2D, HandleScenePostUpdate));
+            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(AnimatedSprite2D, HandleScenePostUpdate));
         else
         else
             UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
             UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
     }
     }
+}
+
+void AnimatedSprite2D::SetAnimationSet(AnimationSet2D* animationSet)
+{
+    if (animationSet == animationSet_) 
+        return;
+
+    Dispose();
+
+    animationSet_ = animationSet;
+    if (!animationSet_)
+        return;
 
 
-    for (unsigned i = 0; i < trackNodes_.Size(); ++i)
+    SetSprite(animationSet_->GetSprite());
+
+#ifdef URHO3D_SPINE
+    if (animationSet_->GetSkeletonData())
     {
     {
-        if (!trackNodes_[i])
-            continue;
+        spSkeletonData* skeletonData = animationSet->GetSkeletonData();
+
+        // Create skeleton
+        skeleton_ = spSkeleton_create(skeletonData);
+        skeleton_->flipX = flipX_;
+        skeleton_->flipY = flipY_;
+
+        if (skeleton_->data->skinsCount > 0)
+        {
+            // If entity is empty use first skin in spine
+            if (entity_.Empty())
+                entity_ = skeleton_->data->skins[0]->name;
+            spSkeleton_setSkinByName(skeleton_, entity_.CString());
+        }
 
 
-        StaticSprite2D* staticSprite = trackNodes_[i]->GetComponent<StaticSprite2D>();
-        if (staticSprite)
-            staticSprite->SetEnabled(enabled);
+        spSkeleton_updateWorldTransform(skeleton_);
     }
     }
-}
+#endif
+    if (animationSet_->GetSpriterData())
+    {
+        spriterInstance_ = new Spriter::SpriterInstance(this, animationSet_->GetSpriterData());
 
 
-void AnimatedSprite2D::SetSpeed(float speed)
-{
-    speed_ = speed;
-    MarkNetworkUpdate();
+        if (!animationSet_->GetSpriterData()->entities_.Empty())
+        {
+            // If entity is empty use first entity in spriter
+            if (entity_.Empty())
+                entity_ = animationSet_->GetSpriterData()->entities_[0]->name_;
+            spriterInstance_->SetEntity(entity_);
+        }
+    }
+
+    // Clear animation name
+    animationName_.Clear();
+    loopMode_ = LM_DEFAULT;
 }
 }
 
 
-void AnimatedSprite2D::SetAnimation(AnimationSet2D* animationSet, const String& name, LoopMode2D loopMode)
+void AnimatedSprite2D::SetEntity(const String& entity)
 {
 {
-    animationSet_ = animationSet;
+    if (entity == entity_)
+        return;
 
 
-    SetAnimation(name, loopMode);
+    entity_ = entity;
+
+#ifdef URHO3D_SPINE
+    if (skeleton_)
+        spSkeleton_setSkinByName(skeleton_, entity_.CString());
+#endif
+    if (spriterInstance_)
+        spriterInstance_->SetEntity(entity_.CString());
 }
 }
 
 
 void AnimatedSprite2D::SetAnimation(const String& name, LoopMode2D loopMode)
 void AnimatedSprite2D::SetAnimation(const String& name, LoopMode2D loopMode)
 {
 {
     animationName_ = name;
     animationName_ = name;
+    loopMode_ = loopMode;
 
 
-    if (animationSet_)
-        SetAnimation(animationSet_->GetAnimation(animationName_), loopMode);
-}
-
-void AnimatedSprite2D::SetAnimationSet(AnimationSet2D* animationSet)
-{
-    if (animationSet == animationSet_)
+    if (!animationSet_ || !animationSet_->HasAnimation(animationName_))
         return;
         return;
 
 
-    animationSet_ = animationSet;
-
-    SetAnimation(animationName_, loopMode_);
+#ifdef URHO3D_SPINE
+    if (skeleton_)
+        SetSpineAnimation();
+#endif
+    if (spriterInstance_)
+        SetSpriterAnimation();
 }
 }
 
 
 void AnimatedSprite2D::SetLoopMode(LoopMode2D loopMode)
 void AnimatedSprite2D::SetLoopMode(LoopMode2D loopMode)
 {
 {
-    if (!animation_)
-        return;
-
     loopMode_ = loopMode;
     loopMode_ = loopMode;
-
-    switch (loopMode_)
-    {
-    case LM_FORCE_LOOPED:
-        looped_ = true;
-        break;
-
-    case LM_FORCE_CLAMPED:
-        looped_ = false;
-        break;
-
-    default:
-        looped_ = animation_->IsLooped();
-        break;
-    }
 }
 }
 
 
-AnimationSet2D* AnimatedSprite2D::GetAnimationSet() const
+void AnimatedSprite2D::SetSpeed(float speed)
 {
 {
-    return animationSet_;
+    speed_ = speed;
+    MarkNetworkUpdate();
 }
 }
 
 
-Node* AnimatedSprite2D::GetRootNode() const
+AnimationSet2D* AnimatedSprite2D::GetAnimationSet() const
 {
 {
-    return rootNode_;
+    return animationSet_;
 }
 }
 
 
 void AnimatedSprite2D::SetAnimationSetAttr(const ResourceRef& value)
 void AnimatedSprite2D::SetAnimationSetAttr(const ResourceRef& value)
@@ -182,9 +217,9 @@ void AnimatedSprite2D::OnSceneSet(Scene* scene)
     if (scene)
     if (scene)
     {
     {
         if (scene == node_)
         if (scene == node_)
-            LOGWARNING(GetTypeName() + " should not be created to the root scene node");
+            URHO3D_LOGWARNING(GetTypeName() + " should not be created to the root scene node");
         if (IsEnabledEffective())
         if (IsEnabledEffective())
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(AnimatedSprite2D, HandleScenePostUpdate));
+            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(AnimatedSprite2D, HandleScenePostUpdate));
     }
     }
     else
     else
         UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
         UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
@@ -193,295 +228,310 @@ void AnimatedSprite2D::OnSceneSet(Scene* scene)
 void AnimatedSprite2D::SetAnimationAttr(const String& name)
 void AnimatedSprite2D::SetAnimationAttr(const String& name)
 {
 {
     animationName_ = name;
     animationName_ = name;
-
-    if (animationSet_)
-        SetAnimation(animationSet_->GetAnimation(animationName_), loopMode_);
+    SetAnimation(animationName_, loopMode_);
 }
 }
 
 
-void AnimatedSprite2D::OnWorldBoundingBoxUpdate()
+void AnimatedSprite2D::UpdateSourceBatches()
 {
 {
-    boundingBox_.Clear();
-    worldBoundingBox_.Clear();
-
-    for (unsigned i = 0; i < numTracks_; ++i)
-    {
-        if (!trackNodes_[i])
-            continue;
+#ifdef URHO3D_SPINE
+    if (skeleton_ && animationState_)
+        UpdateSourceBatchesSpine();
+#endif
+    if (spriterInstance_ && spriterInstance_->GetAnimation())
+        UpdateSourceBatchesSpriter();
 
 
-        StaticSprite2D* staticSprite = trackNodes_[i]->GetComponent<StaticSprite2D>();
-        if (staticSprite)
-            worldBoundingBox_.Merge(staticSprite->GetWorldBoundingBox());
-    }
-
-    boundingBox_ = worldBoundingBox_.Transformed(node_->GetWorldTransform().Inverse());
+    sourceBatchesDirty_ = false;
 }
 }
 
 
-void AnimatedSprite2D::OnDrawOrderChanged()
+void AnimatedSprite2D::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
-    for (unsigned i = 0; i < numTracks_; ++i)
-    {
-        if (!trackNodes_[i])
-            continue;
+    using namespace ScenePostUpdate;
+    float timeStep = eventData[P_TIMESTEP].GetFloat();
+    UpdateAnimation(timeStep);
+}
 
 
-        StaticSprite2D* staticSprite = trackNodes_[i]->GetComponent<StaticSprite2D>();
-        if (staticSprite)
-            staticSprite->SetLayer(layer_);
-    }
+void AnimatedSprite2D::UpdateAnimation(float timeStep)
+{
+#ifdef URHO3D_SPINE
+    if (skeleton_ && animationState_)
+        UpdateSpineAnimation(timeStep);
+#endif
+    if (spriterInstance_ && spriterInstance_->GetAnimation())
+        UpdateSpriterAnimation(timeStep);
 }
 }
 
 
-void AnimatedSprite2D::OnFlipChanged()
+#ifdef URHO3D_SPINE
+void AnimatedSprite2D::SetSpineAnimation()
 {
 {
-    for (unsigned i = 0; i < numTracks_; ++i)
+    if (!animationStateData_)
     {
     {
-        if (!trackNodes_[i])
-            continue;
+        animationStateData_ = spAnimationStateData_create(animationSet_->GetSkeletonData());
+        if (!animationStateData_)
+        {
+            URHO3D_LOGERROR("Create animation state data failed");
+            return;
+        }
+    }
 
 
-        StaticSprite2D* staticSprite = trackNodes_[i]->GetComponent<StaticSprite2D>();
-        if (staticSprite)
-            staticSprite->SetFlip(flipX_, flipY_);
+    if (!animationState_)
+    {
+        animationState_ = spAnimationState_create(animationStateData_);
+        if (!animationState_)
+        {
+            URHO3D_LOGERROR("Create animation state failed");
+            return;
+        }
     }
     }
+    
+    // Reset slots to setup pose, fix issue #932
+    spSkeleton_setSlotsToSetupPose(skeleton_);
+    spAnimationState_setAnimationByName(animationState_, 0, animationName_.CString(), loopMode_ != LM_FORCE_CLAMPED ? true : false);
 
 
-    // For editor paused mode
     UpdateAnimation(0.0f);
     UpdateAnimation(0.0f);
+    MarkNetworkUpdate();
 }
 }
 
 
-void AnimatedSprite2D::UpdateSourceBatches()
-{
-    sourceBatchesDirty_ = false;
-}
-
-void AnimatedSprite2D::SetAnimation(Animation2D* animation, LoopMode2D loopMode)
+void AnimatedSprite2D::UpdateSpineAnimation(float timeStep)
 {
 {
-    if (animation == animation_)
-    {
-        SetLoopMode(loopMode_);
+    timeStep *= speed_;
 
 
-        currentTime_ = 0.0f;
-        UpdateAnimation(0.0f);
-        return;
-    }
+    skeleton_->flipX = flipX_;
+    skeleton_->flipY = flipY_;
 
 
-    for (unsigned i = 0; i < numTracks_; ++i)
-    {
-        if (trackNodes_[i])
-            trackNodes_[i]->SetEnabled(false);
-    }
+    spSkeleton_update(skeleton_, timeStep); 
+    spAnimationState_update(animationState_, timeStep);
+    spAnimationState_apply(animationState_, skeleton_);
+    spSkeleton_updateWorldTransform(skeleton_);
 
 
-    numTracks_ = 0;
-    trackNodes_.Clear();
-    trackNodeInfos_.Clear();
+    sourceBatchesDirty_ = true;
+    worldBoundingBoxDirty_ = true;
+}
 
 
-    animation_ = animation;
+void AnimatedSprite2D::UpdateSourceBatchesSpine()
+{
+    const Matrix3x4& worldTransform = GetNode()->GetWorldTransform();
 
 
-    if (!animation_)
-        return;
+    SourceBatch2D& sourceBatch = sourceBatches_[0];
+    sourceBatches_[0].vertices_.Clear();
 
 
-    currentTime_ = 0.0f;
+    const int SLOT_VERTEX_COUNT_MAX = 1024;
+    float slotVertices[SLOT_VERTEX_COUNT_MAX];
 
 
-    if (!rootNode_)
+    for (int i = 0; i < skeleton_->slotsCount; ++i)
     {
     {
-        rootNode_ = GetNode()->CreateChild("_root_", LOCAL);
-        rootNode_->SetTemporary(true);
-    }
-
-    numTracks_ = animation_->GetNumTracks();
-    trackNodes_.Resize(numTracks_);
-    trackNodeInfos_.Resize(numTracks_);
+        spSlot* slot = skeleton_->drawOrder[i];
+        spAttachment* attachment = slot->attachment;
+        if (!attachment)
+            continue;
 
 
-    for (unsigned i = 0; i < numTracks_; ++i)
-    {
-        const AnimationTrack2D& track = animation->GetTrack(i);
-        SharedPtr<Node> trackNode(rootNode_->GetChild(track.name_));
+        unsigned color = Color(color_.r_ * slot->r, 
+            color_.g_ * slot->g, 
+            color_.b_ * slot->b, 
+            color_.a_ * slot->a).ToUInt();
 
 
-        StaticSprite2D* staticSprite = 0;
-        if (trackNode)
+        if (attachment->type == SP_ATTACHMENT_REGION)
         {
         {
-            // Enable track node
-            trackNode->SetEnabled(true);
-
-            // Get StaticSprite2D component
-            if (track.hasSprite_)
-                staticSprite = trackNode->GetComponent<StaticSprite2D>();
+            spRegionAttachment* region = (spRegionAttachment*)attachment;
+            spRegionAttachment_computeWorldVertices(region, slot->bone, slotVertices);
+
+            Vertex2D vertices[4];
+            vertices[0].position_ = worldTransform * Vector3(slotVertices[SP_VERTEX_X1], slotVertices[SP_VERTEX_Y1]);
+            vertices[1].position_ = worldTransform * Vector3(slotVertices[SP_VERTEX_X2], slotVertices[SP_VERTEX_Y2]);
+            vertices[2].position_ = worldTransform * Vector3(slotVertices[SP_VERTEX_X3], slotVertices[SP_VERTEX_Y3]);
+            vertices[3].position_ = worldTransform * Vector3(slotVertices[SP_VERTEX_X4], slotVertices[SP_VERTEX_Y4]);
+
+            vertices[0].color_ = color;
+            vertices[1].color_ = color;
+            vertices[2].color_ = color;
+            vertices[3].color_ = color;
+
+            vertices[0].uv_ = Vector2(region->uvs[SP_VERTEX_X1], region->uvs[SP_VERTEX_Y1]);
+            vertices[1].uv_ = Vector2(region->uvs[SP_VERTEX_X2], region->uvs[SP_VERTEX_Y2]);
+            vertices[2].uv_ = Vector2(region->uvs[SP_VERTEX_X3], region->uvs[SP_VERTEX_Y3]);
+            vertices[3].uv_ = Vector2(region->uvs[SP_VERTEX_X4], region->uvs[SP_VERTEX_Y4]);
+
+            sourceBatches_[0].vertices_.Push(vertices[0]);
+            sourceBatches_[0].vertices_.Push(vertices[1]);
+            sourceBatches_[0].vertices_.Push(vertices[2]);
+            sourceBatches_[0].vertices_.Push(vertices[3]);
         }
         }
-        else
+        else if (attachment->type == SP_ATTACHMENT_MESH)
         {
         {
-            // Create new track node
-            trackNode = rootNode_->CreateChild(track.name_, LOCAL);
-            trackNode->SetTemporary(true);
+            spMeshAttachment* mesh = (spMeshAttachment*)attachment;
+            if (mesh->verticesCount > SLOT_VERTEX_COUNT_MAX)
+                continue;
 
 
-            // Create StaticSprite2D component
-            if (track.hasSprite_)
+            spMeshAttachment_computeWorldVertices(mesh, slot, slotVertices);
+
+            Vertex2D vertex;
+            vertex.color_ = color;
+            for (int j = 0; j < mesh->trianglesCount; ++j)
             {
             {
-                staticSprite = trackNode->CreateComponent<StaticSprite2D>();
-                staticSprite->SetEnabled(IsEnabledEffective());
+                int index = mesh->triangles[j] << 1;
+                vertex.position_ = worldTransform * Vector3(slotVertices[index], slotVertices[index + 1]);
+                vertex.uv_ = Vector2(mesh->uvs[index], mesh->uvs[index + 1]);
+
+                sourceBatches_[0].vertices_.Push(vertex);
+                // Add padding vertex
+                if (j % 3 == 2)
+                    sourceBatches_[0].vertices_.Push(vertex);
             }
             }
         }
         }
-
-        if (staticSprite)
+        else if (attachment->type == SP_ATTACHMENT_SKINNED_MESH)
         {
         {
-            staticSprite->SetLayer(layer_);
-            staticSprite->SetBlendMode(blendMode_);
-            staticSprite->SetFlip(flipX_, flipY_);
-            //use color of that object, animations doesn't have color
-            staticSprite->SetColor(color_);
-            staticSprite->SetUseHotSpot(true);
+            spSkinnedMeshAttachment* skinnedMesh = (spSkinnedMeshAttachment*)attachment;
+            if (skinnedMesh->uvsCount > SLOT_VERTEX_COUNT_MAX)
+                continue;
+
+            spSkinnedMeshAttachment_computeWorldVertices(skinnedMesh, slot, slotVertices);
+
+            Vertex2D vertex;
+            vertex.color_ = color;
+            for (int j = 0; j < skinnedMesh->trianglesCount; ++j)
+            {
+                int index = skinnedMesh->triangles[j] << 1;
+                vertex.position_ = worldTransform * Vector3(slotVertices[index], slotVertices[index + 1]);
+                vertex.uv_ = Vector2(skinnedMesh->uvs[index], skinnedMesh->uvs[index + 1]);
+
+                sourceBatches_[0].vertices_.Push(vertex);
+                // Add padding vertex
+                if (j % 3 == 2)
+                    sourceBatches_[0].vertices_.Push(vertex);
+            }
         }
         }
+    }
+}
+#endif
+
+void AnimatedSprite2D::SetSpriterAnimation()
+{
+    if (!spriterInstance_)
+        spriterInstance_ = new Spriter::SpriterInstance(this, animationSet_->GetSpriterData());
 
 
-        trackNodes_[i] = trackNode;
+    // Use entity is empty first entity
+    if (entity_.Empty())
+        entity_ = animationSet_->GetSpriterData()->entities_[0]->name_;
 
 
-        trackNodeInfos_[i].hasSprite = track.hasSprite_;
+    if (!spriterInstance_->SetEntity(entity_.CString()))
+    {
+        URHO3D_LOGERROR("Set entity failed");
+        return;
     }
     }
 
 
-    SetLoopMode(loopMode);
-    UpdateAnimation(0.0f);
+    if (!spriterInstance_->SetAnimation(animationName_.CString(), (Spriter::LoopMode)loopMode_))
+    {
+        URHO3D_LOGERROR("Set animation failed");
+        return;
+    }
 
 
+    UpdateAnimation(0.0f);
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
-void AnimatedSprite2D::UpdateAnimation(float timeStep)
+void AnimatedSprite2D::UpdateSpriterAnimation(float timeStep)
 {
 {
-    if (!animation_)
-        return;
+    spriterInstance_->Update(timeStep * speed_);
+    sourceBatchesDirty_ = true;
+    worldBoundingBoxDirty_ = true;
+}
+
+void AnimatedSprite2D::UpdateSourceBatchesSpriter()
+{
+    const Matrix3x4& nodeWorldTransform = GetNode()->GetWorldTransform();
 
 
-    currentTime_ += timeStep * speed_;
+    Vector<Vertex2D>& vertices = sourceBatches_[0].vertices_;
+    vertices.Clear();
 
 
-    float time;
-    float animationLength = animation_->GetLength();
+    Rect drawRect;
+    Rect textureRect;
+    unsigned color = color_.ToUInt();
 
 
-    if (looped_)
-    {
-        time = fmodf(currentTime_, animationLength);
-        if (time < 0.0f)
-            time += animation_->GetLength();
-    }
-    else
-        time = Clamp(currentTime_, 0.0f, animationLength);
+    Vertex2D vertex0;
+    Vertex2D vertex1;
+    Vertex2D vertex2;
+    Vertex2D vertex3;
 
 
-    for (unsigned i = 0; i < numTracks_; ++i)
+    const PODVector<Spriter::SpatialTimelineKey*>& timelineKeys = spriterInstance_->GetTimelineKeys();
+    for (size_t i = 0; i < timelineKeys.Size(); ++i)
     {
     {
-        trackNodeInfos_[i].worldSpace = false;
+        if (timelineKeys[i]->GetObjectType() != Spriter::SPRITE)
+            continue;
 
 
-        const AnimationTrack2D& track = animation_->GetTrack(i);
-        const Vector<AnimationKeyFrame2D>& keyFrames = track.keyFrames_;
+        Spriter::SpriteTimelineKey* timelineKey = (Spriter::SpriteTimelineKey*)timelineKeys[i];
 
 
-        // Time out of range
-        if (time < keyFrames[0].time_ || time > keyFrames.Back().time_)
-            trackNodeInfos_[i].value.enabled_ = false;
-        else
-        {
-            unsigned index = keyFrames.Size() - 1;
-            for (unsigned j = 0; j < keyFrames.Size() - 1; ++j)
-            {
-                if (time <= keyFrames[j + 1].time_)
-                {
-                    index = j;
-                    break;
-                }
-            }
+        Spriter::SpatialInfo& info = timelineKey->info_;
+        Vector3 position(info.x_, info.y_, 0.0f);
+        if (flipX_)
+            position.x_ = -position.x_;
+        if (flipY_)
+            position.y_ = -position.y_;
 
 
-            const AnimationKeyFrame2D& currKey = keyFrames[index];
-            AnimationKeyFrame2D& value = trackNodeInfos_[i].value;
+        float angle = info.angle_;
+        if (flipX_ != flipY_)
+            angle = -angle;
 
 
-            value.enabled_ = currKey.enabled_;
-            value.parent_ = currKey.parent_;
+        Matrix3x4 localTransform(position * PIXEL_SIZE, 
+            Quaternion(angle), 
+            Vector3(info.scaleX_, info.scaleY_, 1.0f));
 
 
-            if (index < keyFrames.Size() - 1)
-            {
-                const AnimationKeyFrame2D& nextKey = keyFrames[index + 1];
-                float t = (time - currKey.time_) / (nextKey.time_ - currKey.time_);
-                value.transform_ = currKey.transform_.Lerp(nextKey.transform_, t, currKey.spin_);
+        Matrix3x4 worldTransform = nodeWorldTransform * localTransform;
+        Sprite2D* sprite = animationSet_->GetSpriterFileSprite(timelineKey->folderId_, timelineKey->fileId_);
+        if (!sprite)
+            return;
 
 
-                if (trackNodeInfos_[i].hasSprite)
-                    value.alpha_ = Atomic::Lerp(currKey.alpha_, nextKey.alpha_, t);
-            }
-            else
-            {
-                value.transform_ = currKey.transform_;
+        if (timelineKey->useDefaultPivot_)
+            sprite->GetDrawRectangle(drawRect, flipX_, flipY_);
+        else
+            sprite->GetDrawRectangle(drawRect, Vector2(timelineKey->pivotX_, timelineKey->pivotY_), flipX_, flipY_);
 
 
-                if (trackNodeInfos_[i].hasSprite)
-                    value.alpha_ = currKey.alpha_;
-            }
+        if (!sprite->GetTextureRectangle(textureRect, flipX_, flipY_))
+            return;
 
 
-            if (trackNodeInfos_[i].hasSprite)
-            {
-                value.zIndex_ = currKey.zIndex_;
-                value.sprite_ = currKey.sprite_;
-                value.useHotSpot_ = currKey.useHotSpot_;
-                value.hotSpot_ = currKey.hotSpot_;
-            }
-        }
-    }
+        vertex0.position_ = worldTransform * Vector3(drawRect.min_.x_, drawRect.min_.y_, 0.0f);
+        vertex1.position_ = worldTransform * Vector3(drawRect.min_.x_, drawRect.max_.y_, 0.0f);
+        vertex2.position_ = worldTransform * Vector3(drawRect.max_.x_, drawRect.max_.y_, 0.0f);
+        vertex3.position_ = worldTransform * Vector3(drawRect.max_.x_, drawRect.min_.y_, 0.0f);
 
 
-    for (unsigned i = 0; i < numTracks_; ++i)
-    {
-        Node* node = trackNodes_[i];
-        if (!node)
-            continue;
+        vertex0.uv_ = textureRect.min_;
+        vertex1.uv_ = Vector2(textureRect.min_.x_, textureRect.max_.y_);
+        vertex2.uv_ = textureRect.max_;
+        vertex3.uv_ = Vector2(textureRect.max_.x_, textureRect.min_.y_);
 
 
-        TrackNodeInfo& nodeInfo = trackNodeInfos_[i];
+        vertex0.color_ = vertex1.color_ = vertex2.color_ = vertex3.color_ = color;
 
 
-        if (!nodeInfo.value.enabled_)
-            node->SetEnabled(false);
-        else
-        {
-            node->SetEnabled(true);
-
-            // Calculate world transform.
-            CalculateTimelineWorldTransform(i);
-
-            // Update node's transform
-            Vector2 position = nodeInfo.value.transform_.position_ * PIXEL_SIZE;
-            if (flipX_)
-                position.x_ = -position.x_;
-            if (flipY_)
-                position.y_ = -position.y_;
-            node->SetPosition(position);
-
-            float angle = nodeInfo.value.transform_.angle_;
-            if (flipX_ != flipY_)
-                angle = -angle;
-            node->SetRotation(angle);
-            node->SetScale(nodeInfo.value.transform_.scale_);
-
-            if (nodeInfo.hasSprite)
-            {
-                StaticSprite2D* staticSprite = node->GetComponent<StaticSprite2D>();
-                if (staticSprite)
-                {
-                    staticSprite->SetOrderInLayer(orderInLayer_ + nodeInfo.value.zIndex_);
-                    staticSprite->SetSprite(nodeInfo.value.sprite_);
-                    staticSprite->SetAlpha(nodeInfo.value.alpha_);
-                    staticSprite->SetUseHotSpot(nodeInfo.value.useHotSpot_);
-                    staticSprite->SetHotSpot(nodeInfo.value.hotSpot_);
-                    //use color of that object, animations doesn't have color
-                    staticSprite->SetColor(color_);
-                }
-            }
-        }
+        vertices.Push(vertex0);
+        vertices.Push(vertex1);
+        vertices.Push(vertex2);
+        vertices.Push(vertex3);
     }
     }
 }
 }
 
 
-void AnimatedSprite2D::CalculateTimelineWorldTransform(int index)
+void AnimatedSprite2D::Dispose()
 {
 {
-    TrackNodeInfo& info = trackNodeInfos_[index];
-    if (info.worldSpace)
-        return;
-
-    info.worldSpace = true;
+#ifdef URHO3D_SPINE
+    if (animationState_)
+    {
+        spAnimationState_dispose(animationState_);
+        animationState_ = 0;
+    }
 
 
-    int parent = info.value.parent_;
-    if (parent != -1)
+    if (animationStateData_)
     {
     {
-        CalculateTimelineWorldTransform(parent);
-        info.value.transform_ = trackNodeInfos_[parent].value.transform_ * info.value.transform_;
+        spAnimationStateData_dispose(animationStateData_);
+        animationStateData_ = 0;
     }
     }
-}
 
 
-void AnimatedSprite2D::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
-{
-    using namespace ScenePostUpdate;
-    float timeStep = eventData[P_TIMESTEP].GetFloat();
-    UpdateAnimation(timeStep);
+    if (skeleton_)
+    {
+        spSkeleton_dispose(skeleton_);
+        skeleton_ = 0;
+    }
+#endif
+    if (spriterInstance_)
+    {
+        delete spriterInstance_;
+        spriterInstance_ = 0;
+    }
 }
 }
 
 
 }
 }

+ 61 - 59
Source/Atomic/Atomic2D/AnimatedSprite2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,13 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Animation2D.h"
-#include "../Atomic2D/StaticSprite2D.h"
+#include "../Urho2D/StaticSprite2D.h"
+
+#ifdef URHO3D_SPINE
+struct spAnimationState;
+struct spAnimationStateData;
+struct spSkeleton;
+#endif
 
 
 /// Loop mode.
 /// Loop mode.
 enum LoopMode2D
 enum LoopMode2D
@@ -36,15 +41,20 @@ enum LoopMode2D
     LM_FORCE_CLAMPED
     LM_FORCE_CLAMPED
 };
 };
 
 
-namespace Atomic
+namespace Urho3D
+{
+
+namespace Spriter
 {
 {
+    class SpriterInstance;
+}
 
 
 class AnimationSet2D;
 class AnimationSet2D;
 
 
-/// Animated sprite component, it uses to play animation created by Spriter (http://www.brashmonkey.com/).
-class ATOMIC_API AnimatedSprite2D : public StaticSprite2D
+/// Animated sprite component, it uses to play animation created by Spine (http://www.esotericsoftware.com) and Spriter (http://www.brashmonkey.com/).
+class URHO3D_API AnimatedSprite2D : public StaticSprite2D
 {
 {
-    OBJECT(AnimatedSprite2D);
+    URHO3D_OBJECT(AnimatedSprite2D, StaticSprite2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -57,31 +67,27 @@ public:
     /// Handle enabled/disabled state change.
     /// Handle enabled/disabled state change.
     virtual void OnSetEnabled();
     virtual void OnSetEnabled();
 
 
-    /// Set speed.
-    void SetSpeed(float speed);
-    /// Set animation by animation set, name and loop mode.
-    void SetAnimation(AnimationSet2D* animationSet, const String& name, LoopMode2D loopMode = LM_DEFAULT);
-    /// Set animation by name and loop mode.
-    void SetAnimation(const String& name, LoopMode2D loopMode = LM_DEFAULT);
     /// Set animation set.
     /// Set animation set.
     void SetAnimationSet(AnimationSet2D* animationSet);
     void SetAnimationSet(AnimationSet2D* animationSet);
+    /// Set entity name (skin name for spine, entity name for spriter).
+    void SetEntity(const String& name);
+    /// Set animation by name and loop mode.
+    void SetAnimation(const String& name, LoopMode2D loopMode = LM_DEFAULT);
     /// Set loop mode.
     /// Set loop mode.
     void SetLoopMode(LoopMode2D loopMode);
     void SetLoopMode(LoopMode2D loopMode);
-
-    /// Return speed.
-    float GetSpeed() const { return speed_; }
-
-    /// Return animation name.
-    const String& GetAnimation() const { return animationName_; }
+    /// Set speed.
+    void SetSpeed(float speed);
 
 
     /// Return animation.
     /// Return animation.
     AnimationSet2D* GetAnimationSet() const;
     AnimationSet2D* GetAnimationSet() const;
-
+    /// Return entity name.
+    const String& GetEntity() const { return entity_; }
+    /// Return animation name.
+    const String& GetAnimation() const { return animationName_; }
     /// Return loop mode.
     /// Return loop mode.
     LoopMode2D GetLoopMode() const { return loopMode_; }
     LoopMode2D GetLoopMode() const { return loopMode_; }
-
-    /// Return root node.
-    Node* GetRootNode() const;
+    /// Return speed.
+    float GetSpeed() const { return speed_; }
 
 
     /// Set animation set attribute.
     /// Set animation set attribute.
     void SetAnimationSetAttr(const ResourceRef& value);
     void SetAnimationSetAttr(const ResourceRef& value);
@@ -93,55 +99,51 @@ public:
 protected:
 protected:
     /// Handle scene being assigned.
     /// Handle scene being assigned.
     virtual void OnSceneSet(Scene* scene);
     virtual void OnSceneSet(Scene* scene);
-    /// Recalculate the world-space bounding box.
-    virtual void OnWorldBoundingBoxUpdate();
-    /// Handle draw order changed.
-    virtual void OnDrawOrderChanged();
     /// Handle update vertices.
     /// Handle update vertices.
     virtual void UpdateSourceBatches();
     virtual void UpdateSourceBatches();
-    /// Handle flip changed.
-    virtual void OnFlipChanged();
-    /// Set animation.
-    void SetAnimation(Animation2D* animation, LoopMode2D loopMode);
-    /// Update animation.
-    void UpdateAnimation(float timeStep);
-    /// Calculate time line world world transform.
-    void CalculateTimelineWorldTransform(int index);
     /// Handle scene post update.
     /// Handle scene post update.
     void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
     void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
+    /// Update animation.
+    void UpdateAnimation(float timeStep);
+#ifdef URHO3D_SPINE
+    /// Handle set spine animation.
+    void SetSpineAnimation();
+    /// Update spine animation.
+    void UpdateSpineAnimation(float timeStep);
+    /// Update vertices for spine animation;
+    void UpdateSourceBatchesSpine();
+#endif
+    /// Handle set spriter animation.
+    void SetSpriterAnimation();
+    /// Update spriter animation.
+    void UpdateSpriterAnimation(float timeStep);
+    /// Update vertices for spriter animation.
+    void UpdateSourceBatchesSpriter();
+    /// Dispose.
+    void Dispose();
 
 
     /// Speed.
     /// Speed.
     float speed_;
     float speed_;
+    /// Entity name.
+    String entity_;
     /// Animation set.
     /// Animation set.
     SharedPtr<AnimationSet2D> animationSet_;
     SharedPtr<AnimationSet2D> animationSet_;
     /// Animation name.
     /// Animation name.
     String animationName_;
     String animationName_;
-    /// Animation.
-    SharedPtr<Animation2D> animation_;
     /// Loop mode.
     /// Loop mode.
     LoopMode2D loopMode_;
     LoopMode2D loopMode_;
-    /// Looped.
-    bool looped_;
-    /// Current time.
-    float currentTime_;
-    /// Root node.
-    SharedPtr<Node> rootNode_;
-    /// Number of tracks.
-    unsigned numTracks_;
-    /// Track nodes.
-    Vector<SharedPtr<Node> > trackNodes_;
-    /// Track node info.
-    struct TrackNodeInfo
-    {
-        /// Has sprite.
-        bool hasSprite;
-        /// World space.
-        bool worldSpace;
-        /// Current value.
-        AnimationKeyFrame2D value;
-    };
-    /// Track node infos.
-    Vector<TrackNodeInfo> trackNodeInfos_;
+
+#ifdef URHO3D_SPINE
+    /// Skeleton.
+    spSkeleton* skeleton_;
+    /// Animation state data.
+    spAnimationStateData* animationStateData_;
+    /// Animation state.
+    spAnimationState* animationState_;
+#endif
+    
+    /// Spriter instance.
+    Spriter::SpriterInstance* spriterInstance_;
 };
 };
 
 
 }
 }

+ 0 - 151
Source/Atomic/Atomic2D/Animation2D.cpp

@@ -1,151 +0,0 @@
-//
-// Copyright (c) 2008-2015 the Urho3D project.
-//
-// 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 "../Precompiled.h"
-
-#include "../IO/Log.h"
-#include "../Atomic2D/Animation2D.h"
-#include "../Atomic2D/AnimationSet2D.h"
-#include "../Atomic2D/Sprite2D.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-Transform2D::Transform2D() :
-    position_(Vector2::ZERO),
-    angle_(0.0f),
-    scale_(1.0f, 1.0f)
-{
-}
-
-Transform2D::Transform2D(const Vector2& position, float angle, const Vector2& scale) :
-    position_(position),
-    angle_(angle),
-    scale_(scale)
-{
-}
-
-Transform2D::Transform2D(const Transform2D& other) :
-    position_(other.position_),
-    angle_(other.angle_),
-    scale_(other.scale_)
-{
-}
-
-Transform2D& Transform2D::operator =(const Transform2D& other)
-{
-    position_ = other.position_;
-    angle_ = other.angle_;
-    scale_ = other.scale_;
-    return *this;
-}
-
-Transform2D Transform2D::operator *(const Transform2D& other) const
-{
-    float x = scale_.x_ * other.position_.x_;
-    float y = scale_.y_ * other.position_.y_;
-    float s = Sin(angle_);
-    float c = Cos(angle_);
-
-    Vector2 position;
-    position.x_ = (x * c) - (y * s);
-    position.y_ = (x * s) + (y * c);
-    position = position_ + position;
-
-    float angle = angle_ + other.angle_;
-    Vector2 scale = scale_ * other.scale_;
-
-    return Transform2D(position, angle, scale);
-}
-
-Transform2D Transform2D::Lerp(const Transform2D& other, float t, int spin) const
-{
-    Transform2D ret;
-    ret.position_ = position_.Lerp(other.position_, t);
-
-    if (spin > 0 && angle_ > other.angle_)
-        ret.angle_ = Atomic::Lerp(angle_, other.angle_ + 360.0f, t);
-    else if (spin < 0 && angle_ < other.angle_)
-        ret.angle_ = Atomic::Lerp(angle_, other.angle_ - 360.0f, t);
-    else
-        ret.angle_ = Atomic::Lerp(angle_, other.angle_, t);
-
-    ret.scale_ = scale_.Lerp(other.scale_, t);
-    return ret;
-}
-
-AnimationKeyFrame2D::AnimationKeyFrame2D() :
-    time_(0.0f),
-    enabled_(false),
-    parent_(-1),
-    spin_(1),
-    zIndex_(0),
-    alpha_(1.0f),
-    useHotSpot_(false)
-{
-}
-
-Animation2D::Animation2D(AnimationSet2D* animationSet) :
-    animationSet_(animationSet),
-    length_(0.0f),
-    looped_(true)
-{
-}
-
-Animation2D::~Animation2D()
-{
-}
-
-void Animation2D::SetName(const String& name)
-{
-    name_ = name;
-}
-
-void Animation2D::SetLength(float length)
-{
-    length_ = Max(0.0f, length);
-}
-
-void Animation2D::SetLooped(bool looped)
-{
-    looped_ = looped;
-}
-
-AnimationSet2D* Animation2D::GetAnimationSet() const
-{
-    return animationSet_;
-}
-
-const AnimationTrack2D& Animation2D::GetTrack(unsigned index) const
-{
-    if (index >= tracks_.Size())
-    {
-        LOGWARNING("Index out of range");
-        index = 0;
-    }
-
-    return tracks_[index];
-}
-
-}

+ 0 - 153
Source/Atomic/Atomic2D/Animation2D.h

@@ -1,153 +0,0 @@
-//
-// Copyright (c) 2008-2015 the Urho3D project.
-//
-// 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 "../Container/Ptr.h"
-#include "../Container/RefCounted.h"
-#include "../Math/Vector2.h"
-
-namespace Atomic
-{
-
-class AnimationSet2D;
-class Sprite2D;
-
-/// 2D Transform class for spriter animation.
-struct Transform2D
-{
-    /// Construct.
-    Transform2D();
-    /// Construct from position, angle, scale.
-    Transform2D(const Vector2& position, float angle, const Vector2& scale);
-    /// Copy-construct from another transform.
-    Transform2D(const Transform2D& other);
-
-    /// Assign from another transform.
-    Transform2D& operator =(const Transform2D& other);
-    /// Multiply a transform.
-    Transform2D operator *(const Transform2D& other) const;
-    /// Linear interpolation with another transform.
-    Transform2D Lerp(const Transform2D& other, float t, int spin) const;
-
-    /// Position.
-    Vector2 position_;
-    /// Angle.
-    float angle_;
-    /// Scale.
-    Vector2 scale_;
-};
-
-/// 2D animation key frame.
-struct AnimationKeyFrame2D
-{
-    /// Construct.
-    AnimationKeyFrame2D();
-
-    /// Time.
-    float time_;
-
-    /// Enabled (2D animation may disable node on fly).
-    bool enabled_;
-    /// Parent (2D animation may change parent on fly).
-    int parent_;
-    /// Transform.
-    Transform2D transform_;
-    /// Spin direction.
-    int spin_;
-
-    /// Draw order.
-    int zIndex_;
-    /// Sprite.
-    SharedPtr<Sprite2D> sprite_;
-    /// Alpha.
-    float alpha_;
-    /// Use hot spot.
-    bool useHotSpot_;
-    /// Hot spot.
-    Vector2 hotSpot_;
-};
-
-/// 2D animation track.
-struct AnimationTrack2D
-{
-    /// Name.
-    String name_;
-    /// Is sprite track.
-    bool hasSprite_;
-    /// Animation key frames.
-    Vector<AnimationKeyFrame2D> keyFrames_;
-};
-
-/// 2D Animation.
-class ATOMIC_API Animation2D : public RefCounted
-{
-    REFCOUNTED(Animation2D)
-
-public:
-    /// Construct.
-    Animation2D(AnimationSet2D* animationSet);
-    /// Destruct
-    virtual ~Animation2D();
-
-    /// Set name.
-    void SetName(const String& name);
-    /// Set length.
-    void SetLength(float length);
-    /// Set looped.
-    void SetLooped(bool looped);
-
-    /// Return animation set.
-    AnimationSet2D* GetAnimationSet() const;
-
-    /// Return name.
-    const String& GetName() const { return name_; }
-
-    /// Return length.
-    float GetLength() const { return length_; }
-
-    /// Return looped.
-    bool IsLooped() const { return looped_; }
-
-    /// Return number of animation tracks.
-    unsigned GetNumTracks() const { return tracks_.Size(); }
-
-    /// Return animation track.
-    const AnimationTrack2D& GetTrack(unsigned index) const;
-
-    /// Return all animation tracks (internal use only).
-    Vector<AnimationTrack2D>& GetAllTracks() { return tracks_; }
-
-private:
-    /// Animation set.
-    WeakPtr<AnimationSet2D> animationSet_;
-    /// Name.
-    String name_;
-    /// Length.
-    float length_;
-    /// Looped.
-    bool looped_;
-    /// Animation tracks.
-    Vector<AnimationTrack2D> tracks_;
-};
-
-}

+ 353 - 345
Source/Atomic/Atomic2D/AnimationSet2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,28 +22,102 @@
 
 
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
+#include "../Container/ArrayPtr.h"
 #include "../Core/Context.h"
 #include "../Core/Context.h"
+#include "../Graphics/Graphics.h"
+#include "../Graphics/Texture2D.h"
 #include "../IO/FileSystem.h"
 #include "../IO/FileSystem.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
+#include "../Math/AreaAllocator.h"
+#include "../Resource/Image.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
-#include "../Atomic2D/Animation2D.h"
-#include "../Atomic2D/AnimationSet2D.h"
-#include "../Atomic2D/Sprite2D.h"
-#include "../Atomic2D/SpriteSheet2D.h"
 #include "../Resource/XMLFile.h"
 #include "../Resource/XMLFile.h"
+#include "../Urho2D/AnimationSet2D.h"
+#include "../Urho2D/Sprite2D.h"
+#include "../Urho2D/SpriterData2D.h"
+#include "../Urho2D/SpriteSheet2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+#ifdef URHO3D_SPINE
+#include <spine/spine.h>
+#include <spine/extension.h>
+#endif
+
+#ifdef URHO3D_SPINE
+// Current animation set
+static Urho3D::AnimationSet2D* currentAnimationSet = 0;
+
+void _spAtlasPage_createTexture(spAtlasPage* self, const char* path)
+{
+    using namespace Urho3D;
+    if (!currentAnimationSet)
+        return;
+
+    ResourceCache* cache = currentAnimationSet->GetSubsystem<ResourceCache>();
+    Sprite2D* sprite = cache->GetResource<Sprite2D>(path);
+    // Add reference
+    if (sprite)
+        sprite->AddRef();
+
+    self->width = sprite->GetTexture()->GetWidth();
+    self->height = sprite->GetTexture()->GetHeight();
+
+    self->rendererObject = sprite;
+}
+
+void _spAtlasPage_disposeTexture(spAtlasPage* self)
+{
+    using namespace Urho3D;
+    Sprite2D* sprite = static_cast<Sprite2D*>(self->rendererObject);
+    if (sprite)
+        sprite->ReleaseRef();
+
+    self->rendererObject = 0;
+}
+
+char* _spUtil_readFile(const char* path, int* length)
+{
+    using namespace Urho3D;
+    
+    if (!currentAnimationSet)
+        return 0;
+
+    ResourceCache* cache = currentAnimationSet->GetSubsystem<ResourceCache>();
+    SharedPtr<File> file = cache->GetFile(path);
+    if (!file)
+        return 0;
+
+    unsigned size = file->GetSize();
+
+    char* data = MALLOC(char, size + 1);
+    file->Read(data, size);
+    data[size] = '\0';
+    
+    file.Reset();
+    *length = size;
+
+    return data;
+}
+#endif
+
+namespace Urho3D
 {
 {
 
 
 AnimationSet2D::AnimationSet2D(Context* context) :
 AnimationSet2D::AnimationSet2D(Context* context) :
-    Resource(context)
+    Resource(context),
+#ifdef URHO3D_SPINE
+    skeletonData_(0),
+    atlas_(0),
+#endif
+    spriterData_(0),
+    hasSpriteSheet_(false)
 {
 {
 }
 }
 
 
 AnimationSet2D::~AnimationSet2D()
 AnimationSet2D::~AnimationSet2D()
 {
 {
+    Dispose();
 }
 }
 
 
 void AnimationSet2D::RegisterObject(Context* context)
 void AnimationSet2D::RegisterObject(Context* context)
@@ -53,21 +127,31 @@ void AnimationSet2D::RegisterObject(Context* context)
 
 
 bool AnimationSet2D::BeginLoad(Deserializer& source)
 bool AnimationSet2D::BeginLoad(Deserializer& source)
 {
 {
+    Dispose();
+
     if (GetName().Empty())
     if (GetName().Empty())
         SetName(source.GetName());
         SetName(source.GetName());
 
 
     String extension = GetExtension(source.GetName());
     String extension = GetExtension(source.GetName());
+#ifdef URHO3D_SPINE
+    if (extension == ".json")
+        return BeginLoadSpine(source);
+#endif
     if (extension == ".scml")
     if (extension == ".scml")
         return BeginLoadSpriter(source);
         return BeginLoadSpriter(source);
 
 
-    LOGERROR("Unsupport animation set file: " + source.GetName());
+    URHO3D_LOGERROR("Unsupport animation set file: " + source.GetName());
 
 
     return false;
     return false;
 }
 }
 
 
 bool AnimationSet2D::EndLoad()
 bool AnimationSet2D::EndLoad()
 {
 {
-    if (spriterFile_)
+#ifdef URHO3D_SPINE
+    if (jsonData_)
+        return EndLoadSpine();
+#endif
+    if (spriterData_)
         return EndLoadSpriter();
         return EndLoadSpriter();
 
 
     return false;
     return false;
@@ -75,168 +159,229 @@ bool AnimationSet2D::EndLoad()
 
 
 unsigned AnimationSet2D::GetNumAnimations() const
 unsigned AnimationSet2D::GetNumAnimations() const
 {
 {
-    return animations_.Size();
+#ifdef URHO3D_SPINE
+    if (skeletonData_)
+        return (unsigned)skeletonData_->animationsCount;
+#endif
+    if (spriterData_ && !spriterData_->entities_.Empty())
+        return (unsigned)spriterData_->entities_[0]->animations_.Size();
+    return 0;
 }
 }
 
 
-Animation2D* AnimationSet2D::GetAnimation(unsigned index) const
+String AnimationSet2D::GetAnimation(unsigned index) const
 {
 {
-    if (index < animations_.Size())
-        return animations_[index];
-    return 0;
+    if (index >= GetNumAnimations())
+        return String::EMPTY;
+
+#ifdef URHO3D_SPINE
+    if (skeletonData_)
+        return skeletonData_->animations[index]->name;
+#endif
+    if (spriterData_ && !spriterData_->entities_.Empty())
+        return spriterData_->entities_[0]->animations_[index]->name_;
+
+    return String::EMPTY;
 }
 }
 
 
-Animation2D* AnimationSet2D::GetAnimation(const String& name) const
+bool AnimationSet2D::HasAnimation(const String& animationName) const
 {
 {
-    for (unsigned i = 0; i < animations_.Size(); ++i)
+#ifdef URHO3D_SPINE
+    if (skeletonData_)
     {
     {
-        if (animations_[i]->GetName() == name)
-            return animations_[i];
+        for (int i = 0; i < skeletonData_->animationsCount; ++i)
+        {
+            if (animationName == skeletonData_->animations[i]->name)
+                return true;
+        }
     }
     }
-    return 0;
+#endif    
+    if (spriterData_ && !spriterData_->entities_.Empty())
+    {
+        const PODVector<Spriter::Animation*>& animations = spriterData_->entities_[0]->animations_;
+        for (size_t i = 0; i < animations.Size(); ++i)
+        {
+            if (animationName == animations[i]->name_)
+                return true;
+        }
+    }
+
+    return false;
 }
 }
 
 
-Sprite2D* AnimationSet2D::GetSprite(const StringHash& hash) const
+Sprite2D* AnimationSet2D::GetSprite() const
 {
 {
-    HashMap<StringHash, SharedPtr<Sprite2D> >::ConstIterator i = sprites_.Find(hash);
-    if (i != sprites_.End())
+    return sprite_;
+}
+
+Sprite2D* AnimationSet2D::GetSpriterFileSprite(int folderId, int fileId) const
+{
+    int key = (folderId << 16) + fileId;
+    HashMap<int, SharedPtr<Sprite2D> >::ConstIterator i = spriterFileSprites_.Find(key);
+    if (i != spriterFileSprites_.End())
         return i->second_;
         return i->second_;
+
     return 0;
     return 0;
 }
 }
 
 
-bool AnimationSet2D::BeginLoadSpriter(Deserializer& source)
+#ifdef URHO3D_SPINE
+bool AnimationSet2D::BeginLoadSpine(Deserializer& source)
 {
 {
-    spriterFile_ = new XMLFile(context_);
-    if (!spriterFile_->Load(source))
+    if (GetName().Empty())
+        SetName(source.GetName());
+
+    unsigned size = source.GetSize();
+    jsonData_ = new char[size + 1];
+    source.Read(jsonData_, size);
+    jsonData_[size] = '\0';
+    SetMemoryUse(size);
+    return true;
+}
+
+bool AnimationSet2D::EndLoadSpine()
+{
+    currentAnimationSet = this;
+
+    String atlasFileName = ReplaceExtension(GetName(), ".atlas");
+    atlas_ = spAtlas_createFromFile(atlasFileName.CString(), 0);
+    if (!atlas_)
     {
     {
-        LOGERROR("Load XML failed " + source.GetName());
-        spriterFile_.Reset();
+        URHO3D_LOGERROR("Create spine atlas failed");
         return false;
         return false;
     }
     }
 
 
-    XMLElement rootElem = spriterFile_->GetRoot("spriter_data");
-    if (!rootElem)
+    int numAtlasPages = 0;
+    spAtlasPage* atlasPage = atlas_->pages;
+    while (atlasPage)
+    {
+        ++numAtlasPages;
+        atlasPage = atlasPage->next;
+    }
+
+    if (numAtlasPages > 1)
     {
     {
-        LOGERROR("Invalid spriter file " + source.GetName());
-        spriterFile_.Reset();
+        URHO3D_LOGERROR("Only one page is supported in Urho3D");
         return false;
         return false;
     }
     }
 
 
-    // When async loading, preprocess folders for spritesheet / sprite files and request them for background loading
-    if (GetAsyncLoadState() == ASYNC_LOADING)
+    sprite_ = static_cast<Sprite2D*>(atlas_->pages->rendererObject);
+
+    spSkeletonJson* skeletonJson = spSkeletonJson_create(atlas_);
+    if (!skeletonJson)
     {
     {
-        if (!LoadSpriterFolders(rootElem))
-        {
-            spriterFile_.Reset();
-            return false;
-        }
+        URHO3D_LOGERROR("Create skeleton Json failed");
+        return false;
     }
     }
 
 
+    skeletonJson->scale = 0.01f; // PIXEL_SIZE;
+    skeletonData_ = spSkeletonJson_readSkeletonData(skeletonJson, &jsonData_[0]);
+
+    spSkeletonJson_dispose(skeletonJson);
+    jsonData_.Reset();
+
+    currentAnimationSet = 0;
+
     return true;
     return true;
 }
 }
+#endif
 
 
-bool AnimationSet2D::EndLoadSpriter()
+bool AnimationSet2D::BeginLoadSpriter(Deserializer& source)
 {
 {
-    XMLElement rootElem = spriterFile_->GetRoot("spriter_data");
-    if (!LoadSpriterFolders(rootElem))
+    unsigned dataSize = source.GetSize();
+    if (!dataSize && !source.GetName().Empty())
     {
     {
-        spriterFile_.Reset();
+        URHO3D_LOGERROR("Zero sized XML data in " + source.GetName());
         return false;
         return false;
     }
     }
 
 
-    XMLElement entityElem = rootElem.GetChild("entity");
-    if (!entityElem)
-    {
-        LOGERROR("Could not find entity");
-        spriterFile_.Reset();
+    SharedArrayPtr<char> buffer(new char[dataSize]);
+    if (source.Read(buffer.Get(), dataSize) != dataSize)
         return false;
         return false;
-    }
 
 
-    for (XMLElement animationElem = entityElem.GetChild("animation"); animationElem;
-         animationElem = animationElem.GetNext("animation"))
+    spriterData_ = new Spriter::SpriterData();
+    if (!spriterData_->Load(buffer.Get(), dataSize))
     {
     {
-        if (!LoadSpriterAnimation(animationElem))
-        {
-            spriterFile_.Reset();
-            return false;
-        }
+        URHO3D_LOGERROR("Could not spriter data from " + source.GetName());
+        return false;
     }
     }
 
 
-    spriterFile_.Reset();
-    return true;
-}
-
-bool AnimationSet2D::LoadSpriterFolders(const XMLElement& rootElem)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-
-    bool async = GetAsyncLoadState() == ASYNC_LOADING;
-
+    // Check has sprite sheet
     String parentPath = GetParentPath(GetName());
     String parentPath = GetParentPath(GetName());
-    String spriteSheetFilePathPList = parentPath + GetFileName(GetName()) + ".plist";
-    String spriteSheetFilePathXML = parentPath + GetFileName(GetName()) + ".xml";
-    SpriteSheet2D* spriteSheet = 0;
-    bool hasSpriteSheet = false;
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
 
 
-    // When async loading, request the sprite sheet for background loading but do not actually get it
-    if (!async)
+    spriteSheetFilePath_ = parentPath + GetFileName(GetName()) + ".xml";
+    hasSpriteSheet_ = cache->Exists(spriteSheetFilePath_);
+    if (!hasSpriteSheet_)
     {
     {
-        spriteSheet = cache->GetResource<SpriteSheet2D>(spriteSheetFilePathPList, false);
-        if (!spriteSheet)
-            spriteSheet = cache->GetResource<SpriteSheet2D>(spriteSheetFilePathXML, false);
+        spriteSheetFilePath_ = parentPath + GetFileName(GetName()) + ".plist";
+        hasSpriteSheet_ = cache->Exists(spriteSheetFilePath_);
     }
     }
-    else
+
+    if (GetAsyncLoadState() == ASYNC_LOADING)
     {
     {
-        hasSpriteSheet = cache->Exists(spriteSheetFilePathPList);
-        if (hasSpriteSheet)
-            cache->BackgroundLoadResource<SpriteSheet2D>(spriteSheetFilePathPList, false, this);
+        if (hasSpriteSheet_)
+            cache->BackgroundLoadResource<SpriteSheet2D>(spriteSheetFilePath_, true, this);
         else
         else
         {
         {
-            hasSpriteSheet = cache->Exists(spriteSheetFilePathXML);
-            if (hasSpriteSheet)
-                cache->BackgroundLoadResource<SpriteSheet2D>(spriteSheetFilePathXML, false, this);
+            for (size_t i = 0; i < spriterData_->folders_.Size(); ++i)
+            {
+                Spriter::Folder* folder = spriterData_->folders_[i];
+                for (size_t j = 0; j < folder->files_.Size(); ++j)
+                {
+                    Spriter::File* file = folder->files_[j];
+                    String imagePath = parentPath + file->name_;
+                    cache->BackgroundLoadResource<Image>(imagePath, true, this);
+                }
+            }
         }
         }
     }
     }
 
 
-    for (XMLElement folderElem = rootElem.GetChild("folder"); folderElem; folderElem = folderElem.GetNext("folder"))
-    {
-        unsigned folderId = folderElem.GetUInt("id");
+    // Note: this probably does not reflect internal data structure size accurately
+    SetMemoryUse(dataSize);
 
 
-        for (XMLElement fileElem = folderElem.GetChild("file"); fileElem; fileElem = fileElem.GetNext("file"))
-        {
-            unsigned fileId = fileElem.GetUInt("id");
-            String fileName = fileElem.GetAttribute("name");
+    return true;
+}
 
 
-            // When async loading, request the sprites for background loading but do not actually get them
-            if (!async)
-            {
-                SharedPtr<Sprite2D> sprite;
+struct SpriteInfo
+{
+    int x;
+    int y;
+    Spriter::File* file_;
+    SharedPtr<Image> image_;
+};
 
 
-                if (spriteSheet)
-                    sprite = spriteSheet->GetSprite(GetFileName(fileName));
-                else
-                    sprite = (cache->GetResource<Sprite2D>(parentPath + fileName));
+bool AnimationSet2D::EndLoadSpriter()
+{
+    if (!spriterData_)
+        return false;
+    
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    if (hasSpriteSheet_)
+    {
+        spriteSheet_ = cache->GetResource<SpriteSheet2D>(spriteSheetFilePath_);
+        if (!spriteSheet_)
+            return false;
 
 
+        for (unsigned i = 0; i < spriterData_->folders_.Size(); ++i)
+        {
+            Spriter::Folder* folder = spriterData_->folders_[i];
+            for (unsigned j = 0; j < folder->files_.Size(); ++j)
+            {
+                Spriter::File* file = folder->files_[j];
+                SharedPtr<Sprite2D> sprite(spriteSheet_->GetSprite(GetFileName(file->name_)));
                 if (!sprite)
                 if (!sprite)
                 {
                 {
-                    LOGERROR("Could not load sprite " + fileName);
+                    URHO3D_LOGERROR("Could not load sprite " + file->name_);
                     return false;
                     return false;
                 }
                 }
 
 
-                Vector2 hotSpot(0.0f, 1.0f);
-                if (fileElem.HasAttribute("pivot_x"))
-                    hotSpot.x_ = fileElem.GetFloat("pivot_x");
-                if (fileElem.HasAttribute("pivot_y"))
-                    hotSpot.y_ = fileElem.GetFloat("pivot_y");
+                Vector2 hotSpot(file->pivotX_, file->pivotY_);
 
 
                 // If sprite is trimmed, recalculate hot spot
                 // If sprite is trimmed, recalculate hot spot
                 const IntVector2& offset = sprite->GetOffset();
                 const IntVector2& offset = sprite->GetOffset();
                 if (offset != IntVector2::ZERO)
                 if (offset != IntVector2::ZERO)
                 {
                 {
-                    int width = fileElem.GetInt("width");
-                    int height = fileElem.GetInt("height");
-
-                    float pivotX = width * hotSpot.x_;
-                    float pivotY = height * (1.0f - hotSpot.y_);
+                    float pivotX = file->width_ * hotSpot.x_;
+                    float pivotY = file->height_ * (1.0f - hotSpot.y_);
 
 
                     const IntRect& rectangle = sprite->GetRectangle();
                     const IntRect& rectangle = sprite->GetRectangle();
                     hotSpot.x_ = (offset.x_ + pivotX) / rectangle.Width();
                     hotSpot.x_ = (offset.x_ + pivotX) / rectangle.Width();
@@ -245,287 +390,150 @@ bool AnimationSet2D::LoadSpriterFolders(const XMLElement& rootElem)
 
 
                 sprite->SetHotSpot(hotSpot);
                 sprite->SetHotSpot(hotSpot);
 
 
-                sprites_[StringHash((folderId << 16) + fileId)] = sprite;
+                if (!sprite_)
+                    sprite_ = sprite;
+
+                int key = (folder->id_ << 16) + file->id_;
+                spriterFileSprites_[key] = sprite;
             }
             }
-            else if (!hasSpriteSheet)
-                cache->BackgroundLoadResource<Sprite2D>(parentPath + fileName, true, this);
         }
         }
     }
     }
-
-    return true;
-}
-
-// Spriter object type.
-enum SpriterObjectType2D
-{
-    SOT_BONE = 0,
-    SOT_SPRITE,
-};
-
-// Spriter timeline key.
-struct SpriterTimelineKey2D
-{
-    SpriterTimelineKey2D() :
-        time_(0.0f),
-        angle_(0.0f),
-        spin_(1),
-        scale_(1.0f, 1.0f),
-        alpha_(1.0f),
-        useHotSpot_(false)
-    {
-    }
-
-    float time_;
-    Vector2 position_;
-    float angle_;
-    int spin_;
-    Vector2 scale_;
-    SharedPtr<Sprite2D> sprite_;
-    float alpha_;
-    bool useHotSpot_;
-    Vector2 hotSpot_;
-};
-
-// Spriter timeline.
-struct SpriterTimeline2D
-{
-    SpriterTimeline2D() :
-        parent_(-1),
-        type_(SOT_BONE)
-    {
-    }
-
-    String name_;
-    int parent_;
-    SpriterObjectType2D type_;
-    Vector<SpriterTimelineKey2D> timelineKeys_;
-};
-
-// Spriter reference.
-struct SpriterReference2D
-{
-    SpriterReference2D() :
-        type_(SOT_BONE),
-        timeline_(-1),
-        key_(-1),
-        zIndex_(0)
-    {
-    }
-
-    SpriterObjectType2D type_;
-    int timeline_;
-    int key_;
-    int zIndex_;
-};
-
-// Spriter mainline Key.
-struct SpriterMainlineKey2D
-{
-    float time_;
-    PODVector<SpriterReference2D> references_;
-};
-
-bool AnimationSet2D::LoadSpriterAnimation(const XMLElement& animationElem)
-{
-    String name = animationElem.GetAttribute("name");
-    float length = animationElem.GetFloat("length") * 0.001f;
-    bool looped = true;
-    if (animationElem.HasAttribute("looping"))
-        looped = animationElem.GetBool("looping");
-
-    float highestKeyTime = 0.0f;
-
-    // Load timelines
-    Vector<SpriterTimeline2D> timelines;
-    for (XMLElement timelineElem = animationElem.GetChild("timeline"); timelineElem;
-         timelineElem = timelineElem.GetNext("timeline"))
+    else
     {
     {
-        SpriterTimeline2D timeline;
-        timeline.name_ = timelineElem.GetAttribute("name");
-        if (timelineElem.GetAttribute("object_type") == "bone")
-            timeline.type_ = SOT_BONE;
-        else
-            timeline.type_ = SOT_SPRITE;
+        Vector<SpriteInfo> spriteInfos;
+        String parentPath = GetParentPath(GetName());
 
 
-        for (XMLElement keyElem = timelineElem.GetChild("key"); keyElem; keyElem = keyElem.GetNext("key"))
+        for (unsigned i = 0; i < spriterData_->folders_.Size(); ++i)
         {
         {
-            SpriterTimelineKey2D key;
-            key.time_ = keyElem.GetFloat("time") * 0.001f;
-            if (keyElem.HasAttribute("spin"))
-                key.spin_ = keyElem.GetInt("spin");
-
-            XMLElement childElem = keyElem.GetChild();
-
-            key.position_.x_ = childElem.GetFloat("x");
-            key.position_.y_ = childElem.GetFloat("y");
-
-            key.angle_ = childElem.GetFloat("angle");
-
-            if (childElem.HasAttribute("scale_x"))
-                key.scale_.x_ = childElem.GetFloat("scale_x");
-
-            if (childElem.HasAttribute("scale_y"))
-                key.scale_.y_ = childElem.GetFloat("scale_y");
-
-            if (timeline.type_ == SOT_SPRITE)
+            Spriter::Folder* folder = spriterData_->folders_[i];
+            for (unsigned j = 0; j < folder->files_.Size(); ++j)
             {
             {
-                int folder = childElem.GetUInt("folder");
-                int file = childElem.GetUInt("file");
-                key.sprite_ = GetSprite(StringHash((unsigned)((folder << 16) + file)));
-                if (!key.sprite_)
+                Spriter::File* file = folder->files_[j];
+                String imagePath = parentPath + file->name_;
+                SharedPtr<Image> image(cache->GetResource<Image>(imagePath));
+                if (!image)
                 {
                 {
-                    LOGERROR("Could not find sprite");
+                    URHO3D_LOGERROR("Could not load image");
                     return false;
                     return false;
                 }
                 }
-
-                if (childElem.HasAttribute("pivot_x") && childElem.HasAttribute("pivot_y"))
+                if (image->IsCompressed())
                 {
                 {
-                    key.useHotSpot_ = true;
-                    key.hotSpot_.x_ = childElem.GetFloat("pivot_x");
-                    key.hotSpot_.y_ = childElem.GetFloat("pivot_y");
+                    URHO3D_LOGERROR("Compressed image is not support");
+                    return false;
+                }
+                if (image->GetComponents() != 4)
+                {
+                    URHO3D_LOGERROR("Only support image with 4 components");
+                    return false;
                 }
                 }
 
 
-                if (childElem.HasAttribute("a"))
-                    key.alpha_ = childElem.GetFloat("a");
+                SpriteInfo def;
+                def.x = 0;
+                def.y = 0;
+                def.file_ = file;
+                def.image_ = image;
+                spriteInfos.Push(def);
             }
             }
-
-            timeline.timelineKeys_.Push(key);
         }
         }
 
 
-        timelines.Push(timeline);
-    }
-
-    // Load main line
-    Vector<SpriterMainlineKey2D> mainlineKeys;
-    XMLElement mainlineElem = animationElem.GetChild("mainline");
-    for (XMLElement keyElem = mainlineElem.GetChild("key"); keyElem; keyElem = keyElem.GetNext("key"))
-    {
-        SpriterMainlineKey2D mainlineKey;
-        mainlineKey.time_ = keyElem.GetFloat("time") * 0.001f;
+        if (spriteInfos.Empty())
+            return false;
 
 
-        for (XMLElement refElem = keyElem.GetChild(); refElem; refElem = refElem.GetNext())
+        if (spriteInfos.Size() > 1)
         {
         {
-            SpriterReference2D ref;
-            if (refElem.GetName() == "bone_ref")
-                ref.type_ = SOT_BONE;
-            else
-                ref.type_ = SOT_SPRITE;
-
-            ref.timeline_ = refElem.GetInt("timeline");
-            ref.key_ = refElem.GetInt("key");
-
-            if (refElem.HasAttribute("parent"))
+            AreaAllocator allocator(128, 128, 2048, 2048);
+            for (unsigned i = 0; i < spriteInfos.Size(); ++i)
             {
             {
-                int parent = refElem.GetInt("parent");
-                int parentTimeline = mainlineKey.references_[parent].timeline_;
-                timelines[ref.timeline_].parent_ = parentTimeline;
+                SpriteInfo& info = spriteInfos[i];
+                Image* image = info.image_;
+                if (!allocator.Allocate(image->GetWidth() + 1, image->GetHeight() + 1, info.x, info.y))
+                {
+                    URHO3D_LOGERROR("Could not allocate area");
+                    return false;
+                }
             }
             }
 
 
-            if (refElem.GetName() == "object_ref")
-                ref.zIndex_ = refElem.GetInt("z_index");
+            SharedPtr<Texture2D> texture(new Texture2D(context_));
+            texture->SetMipsToSkip(QUALITY_LOW, 0);
+            texture->SetNumLevels(1);
+            texture->SetSize(allocator.GetWidth(), allocator.GetHeight(), Graphics::GetRGBAFormat());
 
 
-            mainlineKey.references_.Push(ref);
-        }
+            unsigned textureDataSize = allocator.GetWidth() * allocator.GetHeight() * 4;
+            SharedArrayPtr<unsigned char> textureData(new unsigned char[textureDataSize]);
+            memset(textureData.Get(), 0, textureDataSize);
 
 
-        mainlineKeys.Push(mainlineKey);
-    }
+            sprite_ = new Sprite2D(context_);
+            sprite_->SetTexture(texture);
 
 
-    unsigned numTimelines = timelines.Size();
-    unsigned numMainlineKeys = mainlineKeys.Size();
-    if (numTimelines == 0 || numMainlineKeys == 0)
-    {
-        LOGERROR("Invalid animation");
-        return false;
-    }
-
-    // Create animation
-    SharedPtr<Animation2D> animation(new Animation2D(this));
-    animation->SetName(name);
-    animation->SetLength(length);
-    animation->SetLooped(looped);
-
-    Vector<AnimationTrack2D>& tracks = animation->GetAllTracks();
-    tracks.Resize(numTimelines);
+            for (unsigned i = 0; i < spriteInfos.Size(); ++i)
+            {
+                SpriteInfo& info = spriteInfos[i];
+                Image* image = info.image_;
 
 
-    // Setup animation track key frames
-    for (unsigned i = 0; i < numTimelines; ++i)
-    {
-        SpriterTimeline2D& timeline = timelines[i];
-        AnimationTrack2D& track = tracks[i];
+                for (int y = 0; y < image->GetHeight(); ++y)
+                {
+                    memcpy(textureData.Get() + ((info.y + y) * allocator.GetWidth() + info.x) * 4,
+                        image->GetData() + y * image->GetWidth() * 4, image->GetWidth() * 4);
+                }
 
 
-        track.name_ = timeline.name_;
-        track.hasSprite_ = timeline.type_ == SOT_SPRITE;
+                SharedPtr<Sprite2D> sprite(new Sprite2D(context_));
+                sprite->SetTexture(texture);
+                sprite->SetRectangle(IntRect(info.x, info.y, info.x + image->GetWidth(), info.y + image->GetHeight()));
+                sprite->SetHotSpot(Vector2(info.file_->pivotX_, info.file_->pivotY_));
 
 
-        unsigned numTimelineKeys = timeline.timelineKeys_.Size();
-        tracks[i].keyFrames_.Resize(numTimelineKeys);
+                int key = (info.file_->folder_->id_ << 16) + info.file_->id_;
+                spriterFileSprites_[key] = sprite;
+            }
 
 
-        for (unsigned j = 0; j < numTimelineKeys; ++j)
+            texture->SetData(0, 0, 0, allocator.GetWidth(), allocator.GetHeight(), textureData.Get());
+        }
+        else
         {
         {
-            SpriterTimelineKey2D& timelineKey = timeline.timelineKeys_[j];
-            AnimationKeyFrame2D& keyFrame = track.keyFrames_[j];
+            SharedPtr<Texture2D> texture(new Texture2D(context_));
+            texture->SetMipsToSkip(QUALITY_LOW, 0);
+            texture->SetNumLevels(1);
 
 
-            keyFrame.time_ = timelineKey.time_;
-            highestKeyTime = Max(highestKeyTime, keyFrame.time_);
+            SpriteInfo& info = spriteInfos[0];        
+            texture->SetData(info.image_, true);
 
 
-            // Set disabled
-            keyFrame.enabled_ = false;
-            keyFrame.parent_ = timeline.parent_;
-            keyFrame.transform_ = Transform2D(timelineKey.position_, timelineKey.angle_, timelineKey.scale_);
-            keyFrame.spin_ = timelineKey.spin_;
+            sprite_ = new Sprite2D(context_);
+            sprite_->SetTexture(texture);
+            sprite_->SetRectangle(IntRect(info.x, info.y, info.x + info.image_->GetWidth(), info.y + info.image_->GetHeight()));
+            sprite_->SetHotSpot(Vector2(info.file_->pivotX_, info.file_->pivotY_));
 
 
-            if (track.hasSprite_)
-            {
-                keyFrame.sprite_ = timelineKey.sprite_;
-                keyFrame.alpha_ = timelineKey.alpha_;
-                keyFrame.useHotSpot_ = timelineKey.useHotSpot_;
-                if (timelineKey.useHotSpot_)
-                    keyFrame.hotSpot_ = timelineKey.hotSpot_;
-            }
+            int key = (info.file_->folder_->id_ << 16) + info.file_->id_;
+            spriterFileSprites_[key] = sprite_;
         }
         }
     }
     }
 
 
-    // Set animation key frame enabled and set draw order
-    for (unsigned i = 0; i < numMainlineKeys; ++i)
-    {
-        SpriterMainlineKey2D& mainlineKey = mainlineKeys[i];
-        PODVector<SpriterReference2D>& references = mainlineKey.references_;
-        for (unsigned j = 0; j < references.Size(); ++j)
-        {
-            SpriterReference2D& ref = references[j];
-            AnimationKeyFrame2D& keyFrame = tracks[ref.timeline_].keyFrames_[ref.key_];
-
-            // Set enabled
-            keyFrame.enabled_ = true;
+    return true;
+}
 
 
-            // Set draw order
-            keyFrame.zIndex_ = ref.zIndex_;
-        }
+void AnimationSet2D::Dispose()
+{
+#ifdef URHO3D_SPINE
+    if (skeletonData_)
+    {
+        spSkeletonData_dispose(skeletonData_);
+        skeletonData_ = 0;
     }
     }
 
 
-    // Fix looped animation
-    if (looped)
+    if (atlas_)
     {
     {
-        for (unsigned i = 0; i < numTimelines; ++i)
-        {
-            Vector<AnimationKeyFrame2D>& keyFrames = tracks[i].keyFrames_;
-            if (keyFrames.Front().time_ < 0.01f && !Equals(keyFrames.Back().time_, length))
-            {
-                AnimationKeyFrame2D keyFrame = keyFrames.Front();
-                keyFrame.time_ = length;
-                keyFrames.Push(keyFrame);
-            }
-        }
+        spAtlas_dispose(atlas_);
+        atlas_ = 0;
     }
     }
-    else
+#endif
+
+    if (spriterData_)
     {
     {
-        // Crop non-looped animation length if longer than the last keyframe
-        if (length > highestKeyTime)
-            animation->SetLength(highestKeyTime);
+        delete spriterData_;
+        spriterData_ = 0;
     }
     }
 
 
-    animations_.Push(animation);
-
-    return true;
+    sprite_.Reset();
+    spriteSheet_.Reset();
+    spriterFileSprites_.Clear();
 }
 }
 
 
 }
 }

+ 67 - 24
Source/Atomic/Atomic2D/AnimationSet2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,20 +22,30 @@
 
 
 #pragma once
 #pragma once
 
 
+#include "../Container/ArrayPtr.h"
 #include "../Resource/Resource.h"
 #include "../Resource/Resource.h"
 
 
-namespace Atomic
+#ifdef URHO3D_SPINE
+struct spAtlas;
+struct spSkeletonData;
+struct spAnimationStateData;
+#endif
+
+namespace Urho3D
+{
+
+namespace Spriter
 {
 {
+    struct SpriterData;
+}
 
 
-class Animation2D;
 class Sprite2D;
 class Sprite2D;
-class XMLElement;
-class XMLFile;
+class SpriteSheet2D;
 
 
-/// Spriter animation set, it includes one or more animations, for more information please refer to http://www.brashmonkey.com/spriter.htm.
-class ATOMIC_API AnimationSet2D : public Resource
+/// Spriter animation set, it includes one or more animations, for more information please refer to http://www.esotericsoftware.com and http://www.brashmonkey.com/spriter.htm.
+class URHO3D_API AnimationSet2D : public Resource
 {
 {
-    OBJECT(AnimationSet2D);
+    URHO3D_OBJECT(AnimationSet2D, Resource);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -52,29 +62,62 @@ public:
 
 
     /// Get number of animations.
     /// Get number of animations.
     unsigned GetNumAnimations() const;
     unsigned GetNumAnimations() const;
-    /// Return animation by index.
-    Animation2D* GetAnimation(unsigned index) const;
-    /// Return animation by name.
-    Animation2D* GetAnimation(const String& name) const;
+    /// Return animation name.
+    String GetAnimation(unsigned index) const;
+    /// Check has animation.
+    bool HasAnimation(const String& animation) const;
+    
+    /// Return sprite.
+    Sprite2D* GetSprite() const;
+
+#ifdef URHO3D_SPINE
+    /// Return spine skeleton data.
+    spSkeletonData* GetSkeletonData() const { return skeletonData_; }
+#endif
+
+    /// Return spriter data.
+    Spriter::SpriterData* GetSpriterData() const { return spriterData_; }
+    /// Return spriter file sprite.
+    Sprite2D* GetSpriterFileSprite(int folderId, int fileId) const;
 
 
 private:
 private:
     /// Return sprite by hash.
     /// Return sprite by hash.
-    Sprite2D* GetSprite(const StringHash& hash) const;
+    Sprite2D* GetSpriterFileSprite(const StringHash& hash) const;
+#ifdef URHO3D_SPINE
+    /// Begin load spine.
+    bool BeginLoadSpine(Deserializer& source);
+    /// Finish load spine.
+    bool EndLoadSpine();
+#endif
     /// Begin load scml.
     /// Begin load scml.
     bool BeginLoadSpriter(Deserializer& source);
     bool BeginLoadSpriter(Deserializer& source);
     /// Finish load scml.
     /// Finish load scml.
     bool EndLoadSpriter();
     bool EndLoadSpriter();
-    /// Load spriter folders.
-    bool LoadSpriterFolders(const XMLElement& rootElem);
-    /// Load spriter animation.
-    bool LoadSpriterAnimation(const XMLElement& animationElem);
-
-    /// Sprites.
-    HashMap<StringHash, SharedPtr<Sprite2D> > sprites_;
-    /// Animations.
-    Vector<SharedPtr<Animation2D> > animations_;
-    /// Spriter file.
-    SharedPtr<XMLFile> spriterFile_;
+    /// Dispose all data.
+    void Dispose();
+    
+    /// Spine sprite.
+    SharedPtr<Sprite2D> sprite_;
+    
+#ifdef URHO3D_SPINE
+    /// Spine json data.
+    SharedArrayPtr<char> jsonData_;
+    /// Spine skeleton data.
+    spSkeletonData* skeletonData_;
+    /// Spine atlas.
+    spAtlas* atlas_;
+#endif
+    
+    /// Spriter data.
+    Spriter::SpriterData* spriterData_;
+    /// Has sprite sheet.
+    bool hasSpriteSheet_;
+    /// Sprite sheet file path.
+    String spriteSheetFilePath_;
+    /// Sprite sheet.
+    SharedPtr<SpriteSheet2D> spriteSheet_;
+    /// Spriter sprites.
+    HashMap<int, SharedPtr<Sprite2D> > spriterFileSprites_;
 };
 };
 
 
 }
 }

+ 33 - 42
Source/Atomic/Atomic2D/Atomic2D.cpp

@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,46 +22,44 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/AnimatedSprite2D.h"
-#include "../Atomic2D/AnimationSet2D.h"
-#include "../Atomic2D/CollisionBox2D.h"
-#include "../Atomic2D/CollisionChain2D.h"
-#include "../Atomic2D/CollisionCircle2D.h"
-#include "../Atomic2D/CollisionEdge2D.h"
-#include "../Atomic2D/CollisionPolygon2D.h"
-#include "../Atomic2D/Constraint2D.h"
-#include "../Atomic2D/ConstraintDistance2D.h"
-#include "../Atomic2D/ConstraintFriction2D.h"
-#include "../Atomic2D/ConstraintGear2D.h"
-#include "../Atomic2D/ConstraintMotor2D.h"
-#include "../Atomic2D/ConstraintMouse2D.h"
-#include "../Atomic2D/ConstraintPrismatic2D.h"
-#include "../Atomic2D/ConstraintPulley2D.h"
-#include "../Atomic2D/ConstraintRevolute2D.h"
-#include "../Atomic2D/ConstraintRope2D.h"
-#include "../Atomic2D/ConstraintWeld2D.h"
-#include "../Atomic2D/ConstraintWheel2D.h"
-#include "../Atomic2D/ParticleEffect2D.h"
-#include "../Atomic2D/ParticleEmitter2D.h"
-#include "../Atomic2D/PhysicsWorld2D.h"
-#include "../Atomic2D/Renderer2D.h"
-#include "../Atomic2D/RigidBody2D.h"
-#include "../Atomic2D/Sprite2D.h"
-#include "../Atomic2D/SpriteSheet2D.h"
-#include "../Atomic2D/TileMap2D.h"
-#include "../Atomic2D/TileMapLayer2D.h"
-#include "../Atomic2D/TmxFile2D.h"
-
-#include "../Atomic2D/Light2D.h"
+#include "../Urho2D/AnimatedSprite2D.h"
+#include "../Urho2D/AnimationSet2D.h"
+#include "../Urho2D/CollisionBox2D.h"
+#include "../Urho2D/CollisionChain2D.h"
+#include "../Urho2D/CollisionCircle2D.h"
+#include "../Urho2D/CollisionEdge2D.h"
+#include "../Urho2D/CollisionPolygon2D.h"
+#include "../Urho2D/Constraint2D.h"
+#include "../Urho2D/ConstraintDistance2D.h"
+#include "../Urho2D/ConstraintFriction2D.h"
+#include "../Urho2D/ConstraintGear2D.h"
+#include "../Urho2D/ConstraintMotor2D.h"
+#include "../Urho2D/ConstraintMouse2D.h"
+#include "../Urho2D/ConstraintPrismatic2D.h"
+#include "../Urho2D/ConstraintPulley2D.h"
+#include "../Urho2D/ConstraintRevolute2D.h"
+#include "../Urho2D/ConstraintRope2D.h"
+#include "../Urho2D/ConstraintWeld2D.h"
+#include "../Urho2D/ConstraintWheel2D.h"
+#include "../Urho2D/ParticleEffect2D.h"
+#include "../Urho2D/ParticleEmitter2D.h"
+#include "../Urho2D/PhysicsWorld2D.h"
+#include "../Urho2D/Renderer2D.h"
+#include "../Urho2D/RigidBody2D.h"
+#include "../Urho2D/Sprite2D.h"
+#include "../Urho2D/SpriteSheet2D.h"
+#include "../Urho2D/TileMap2D.h"
+#include "../Urho2D/TileMapLayer2D.h"
+#include "../Urho2D/TmxFile2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-const char* ATOMIC2D_CATEGORY = "Atomic2D";
+const char* URHO2D_CATEGORY = "Urho2D";
 
 
-void RegisterAtomic2DLibrary(Context* context)
+void RegisterUrho2DLibrary(Context* context)
 {
 {
     Renderer2D::RegisterObject(context);
     Renderer2D::RegisterObject(context);
 
 
@@ -104,13 +102,6 @@ void RegisterAtomic2DLibrary(Context* context)
     ConstraintRope2D::RegisterObject(context);
     ConstraintRope2D::RegisterObject(context);
     ConstraintWeld2D::RegisterObject(context);
     ConstraintWeld2D::RegisterObject(context);
     ConstraintWheel2D::RegisterObject(context);
     ConstraintWheel2D::RegisterObject(context);
-
-    Light2DGroup::RegisterObject(context);
-    Light2D::RegisterObject(context);
-    DirectionalLight2D::RegisterObject(context);
-    PositionalLight2D::RegisterObject(context);
-    PointLight2D::RegisterObject(context);
-
 }
 }
 
 
 }
 }

+ 4 - 4
Source/Atomic/Atomic2D/Atomic2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,10 +22,10 @@
 
 
 #pragma once
 #pragma once
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-/// Register Atomic2D library objects.
-void ATOMIC_API RegisterAtomic2DLibrary(Context* context);
+/// Register Urho2D library objects.
+void URHO3D_API RegisterUrho2DLibrary(Context* context);
 
 
 }
 }

+ 14 - 14
Source/Atomic/Atomic2D/CollisionBox2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,15 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/CollisionBox2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
+#include "../Urho2D/CollisionBox2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 static const Vector2 DEFAULT_BOX_SIZE(0.01f, 0.01f);
 static const Vector2 DEFAULT_BOX_SIZE(0.01f, 0.01f);
 
 
 CollisionBox2D::CollisionBox2D(Context* context) :
 CollisionBox2D::CollisionBox2D(Context* context) :
@@ -52,13 +52,13 @@ CollisionBox2D::~CollisionBox2D()
 
 
 void CollisionBox2D::RegisterObject(Context* context)
 void CollisionBox2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<CollisionBox2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<CollisionBox2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Size", GetSize, SetSize, Vector2, DEFAULT_BOX_SIZE, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Center", GetCenter, SetCenter, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Angle", GetAngle, SetAngle, float, 0.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(CollisionShape2D);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Size", GetSize, SetSize, Vector2, DEFAULT_BOX_SIZE, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Center", GetCenter, SetCenter, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Angle", GetAngle, SetAngle, float, 0.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(CollisionShape2D);
 }
 }
 
 
 void CollisionBox2D::SetSize(const Vector2& size)
 void CollisionBox2D::SetSize(const Vector2& size)
@@ -113,11 +113,11 @@ void CollisionBox2D::RecreateFixture()
 {
 {
     ReleaseFixture();
     ReleaseFixture();
 
 
-    float worlsScaleX = cachedWorldScale_.x_;
+    float worldScaleX = cachedWorldScale_.x_;
     float worldScaleY = cachedWorldScale_.y_;
     float worldScaleY = cachedWorldScale_.y_;
-    float halfWidth = size_.x_ * 0.5f * worlsScaleX;
+    float halfWidth = size_.x_ * 0.5f * worldScaleX;
     float halfHeight = size_.y_ * 0.5f * worldScaleY;
     float halfHeight = size_.y_ * 0.5f * worldScaleY;
-    Vector2 scaledCenter = center_ * Vector2(worlsScaleX, worldScaleY);
+    Vector2 scaledCenter = center_ * Vector2(worldScaleX, worldScaleY);
 
 
     if (scaledCenter == Vector2::ZERO && angle_ == 0.0f)
     if (scaledCenter == Vector2::ZERO && angle_ == 0.0f)
         boxShape_.SetAsBox(halfWidth, halfHeight);
         boxShape_.SetAsBox(halfWidth, halfHeight);

+ 5 - 5
Source/Atomic/Atomic2D/CollisionBox2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/CollisionShape2D.h"
+#include "../Urho2D/CollisionShape2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D box collision component.
 /// 2D box collision component.
-class ATOMIC_API CollisionBox2D : public CollisionShape2D
+class URHO3D_API CollisionBox2D : public CollisionShape2D
 {
 {
-    OBJECT(CollisionBox2D);
+    URHO3D_OBJECT(CollisionBox2D, CollisionShape2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 10 - 10
Source/Atomic/Atomic2D/CollisionChain2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,15 +25,15 @@
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../IO/MemoryBuffer.h"
 #include "../IO/MemoryBuffer.h"
 #include "../IO/VectorBuffer.h"
 #include "../IO/VectorBuffer.h"
-#include "../Atomic2D/CollisionChain2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
+#include "../Urho2D/CollisionChain2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 
 
 CollisionChain2D::CollisionChain2D(Context* context) :
 CollisionChain2D::CollisionChain2D(Context* context) :
     CollisionShape2D(context),
     CollisionShape2D(context),
@@ -48,12 +48,12 @@ CollisionChain2D::~CollisionChain2D()
 
 
 void CollisionChain2D::RegisterObject(Context* context)
 void CollisionChain2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<CollisionChain2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<CollisionChain2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Loop", GetLoop, SetLoop, bool, false, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(CollisionShape2D);
-    MIXED_ACCESSOR_ATTRIBUTE("Vertices", GetVerticesAttr, SetVerticesAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Loop", GetLoop, SetLoop, bool, false, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(CollisionShape2D);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Vertices", GetVerticesAttr, SetVerticesAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE);
 }
 }
 
 
 void CollisionChain2D::SetLoop(bool loop)
 void CollisionChain2D::SetLoop(bool loop)

+ 5 - 5
Source/Atomic/Atomic2D/CollisionChain2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,14 +22,14 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/CollisionShape2D.h"
+#include "../Urho2D/CollisionShape2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 /// 2D chain collision component.
 /// 2D chain collision component.
-class ATOMIC_API CollisionChain2D : public CollisionShape2D
+class URHO3D_API CollisionChain2D : public CollisionShape2D
 {
 {
-    OBJECT(CollisionChain2D);
+    URHO3D_OBJECT(CollisionChain2D, CollisionShape2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 10 - 10
Source/Atomic/Atomic2D/CollisionCircle2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,15 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/CollisionCircle2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
+#include "../Urho2D/CollisionCircle2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 static const float DEFAULT_CLRCLE_RADIUS(0.01f);
 static const float DEFAULT_CLRCLE_RADIUS(0.01f);
 
 
 CollisionCircle2D::CollisionCircle2D(Context* context) :
 CollisionCircle2D::CollisionCircle2D(Context* context) :
@@ -49,12 +49,12 @@ CollisionCircle2D::~CollisionCircle2D()
 
 
 void CollisionCircle2D::RegisterObject(Context* context)
 void CollisionCircle2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<CollisionCircle2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<CollisionCircle2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Radius", GetRadius, SetRadius, float, DEFAULT_CLRCLE_RADIUS, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Center", GetCenter, SetCenter, Vector2, Vector2::ZERO, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(CollisionShape2D);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Radius", GetRadius, SetRadius, float, DEFAULT_CLRCLE_RADIUS, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Center", GetCenter, SetCenter, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(CollisionShape2D);
 }
 }
 
 
 void CollisionCircle2D::SetRadius(float radius)
 void CollisionCircle2D::SetRadius(float radius)

+ 5 - 5
Source/Atomic/Atomic2D/CollisionCircle2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/CollisionShape2D.h"
+#include "../Urho2D/CollisionShape2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D circle collision component.
 /// 2D circle collision component.
-class ATOMIC_API CollisionCircle2D : public CollisionShape2D
+class URHO3D_API CollisionCircle2D : public CollisionShape2D
 {
 {
-    OBJECT(CollisionCircle2D);
+    URHO3D_OBJECT(CollisionCircle2D, CollisionShape2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 10 - 10
Source/Atomic/Atomic2D/CollisionEdge2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,15 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/CollisionEdge2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
+#include "../Urho2D/CollisionEdge2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 static const Vector2 DEFAULT_VERTEX1(-0.01f, 0.0f);
 static const Vector2 DEFAULT_VERTEX1(-0.01f, 0.0f);
 static const Vector2 DEFAULT_VERTEX2(0.01f, 0.0f);
 static const Vector2 DEFAULT_VERTEX2(0.01f, 0.0f);
 
 
@@ -52,12 +52,12 @@ CollisionEdge2D::~CollisionEdge2D()
 
 
 void CollisionEdge2D::RegisterObject(Context* context)
 void CollisionEdge2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<CollisionEdge2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<CollisionEdge2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Vertex 1", GetVertex1, SetVertex1, Vector2, DEFAULT_VERTEX1, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Vertex 2", GetVertex2, SetVertex2, Vector2, DEFAULT_VERTEX2, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(CollisionShape2D);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Vertex 1", GetVertex1, SetVertex1, Vector2, DEFAULT_VERTEX1, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Vertex 2", GetVertex2, SetVertex2, Vector2, DEFAULT_VERTEX2, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(CollisionShape2D);
 }
 }
 
 
 void CollisionEdge2D::SetVertex1(const Vector2& vertex)
 void CollisionEdge2D::SetVertex1(const Vector2& vertex)

+ 5 - 5
Source/Atomic/Atomic2D/CollisionEdge2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/CollisionShape2D.h"
+#include "../Urho2D/CollisionShape2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D edge collision component.
 /// 2D edge collision component.
-class ATOMIC_API CollisionEdge2D : public CollisionShape2D
+class URHO3D_API CollisionEdge2D : public CollisionShape2D
 {
 {
-    OBJECT(CollisionEdge2D);
+    URHO3D_OBJECT(CollisionEdge2D, CollisionShape2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 9 - 9
Source/Atomic/Atomic2D/CollisionPolygon2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,15 +25,15 @@
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../IO/MemoryBuffer.h"
 #include "../IO/MemoryBuffer.h"
 #include "../IO/VectorBuffer.h"
 #include "../IO/VectorBuffer.h"
-#include "../Atomic2D/CollisionPolygon2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
+#include "../Urho2D/CollisionPolygon2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 
 
 CollisionPolygon2D::CollisionPolygon2D(Context* context) :
 CollisionPolygon2D::CollisionPolygon2D(Context* context) :
     CollisionShape2D(context)
     CollisionShape2D(context)
@@ -47,11 +47,11 @@ CollisionPolygon2D::~CollisionPolygon2D()
 
 
 void CollisionPolygon2D::RegisterObject(Context* context)
 void CollisionPolygon2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<CollisionPolygon2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<CollisionPolygon2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(CollisionShape2D);
-    MIXED_ACCESSOR_ATTRIBUTE("Vertices", GetVerticesAttr, SetVerticesAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(CollisionShape2D);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Vertices", GetVerticesAttr, SetVerticesAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE);
 }
 }
 
 
 void CollisionPolygon2D::SetVertexCount(unsigned count)
 void CollisionPolygon2D::SetVertexCount(unsigned count)

+ 5 - 5
Source/Atomic/Atomic2D/CollisionPolygon2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/CollisionShape2D.h"
+#include "../Urho2D/CollisionShape2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D polygon collision component.
 /// 2D polygon collision component.
-class ATOMIC_API CollisionPolygon2D : public CollisionShape2D
+class URHO3D_API CollisionPolygon2D : public CollisionShape2D
 {
 {
-    OBJECT(CollisionPolygon2D);
+    URHO3D_OBJECT(CollisionPolygon2D, CollisionShape2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 17 - 15
Source/Atomic/Atomic2D/CollisionShape2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,13 +26,13 @@
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
-#include "../Atomic2D/CollisionShape2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/CollisionShape2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 CollisionShape2D::CollisionShape2D(Context* context) :
 CollisionShape2D::CollisionShape2D(Context* context) :
@@ -53,13 +53,13 @@ CollisionShape2D::~CollisionShape2D()
 
 
 void CollisionShape2D::RegisterObject(Context* context)
 void CollisionShape2D::RegisterObject(Context* context)
 {
 {
-    ACCESSOR_ATTRIBUTE("Trigger", IsTrigger, SetTrigger, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Category Bits", GetCategoryBits, SetCategoryBits, int, 0, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Mask Bits", GetMaskBits, SetMaskBits, int, 0, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Group Index", GetGroupIndex, SetGroupIndex, int, 0, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Density", GetDensity, SetDensity, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Friction", GetFriction, SetFriction, float, 0.2f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Restitution", GetRestitution, SetRestitution, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Trigger", IsTrigger, SetTrigger, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Category Bits", GetCategoryBits, SetCategoryBits, int, 0, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Mask Bits", GetMaskBits, SetMaskBits, int, 0, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Group Index", GetGroupIndex, SetGroupIndex, int, 0, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Density", GetDensity, SetDensity, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Friction", GetFriction, SetFriction, float, 0.2f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Restitution", GetRestitution, SetRestitution, float, 0.0f, AM_DEFAULT);
 }
 }
 
 
 void CollisionShape2D::OnSetEnabled()
 void CollisionShape2D::OnSetEnabled()
@@ -210,7 +210,11 @@ void CollisionShape2D::CreateFixture()
         return;
         return;
 
 
     if (!rigidBody_)
     if (!rigidBody_)
-        return;
+    {
+        rigidBody_ = node_->GetComponent<RigidBody2D>(); // RigidBody2D can be created after CollisionShape2D
+        if (!rigidBody_)
+            return;
+    }
 
 
     b2Body* body = rigidBody_->GetBody();
     b2Body* body = rigidBody_->GetBody();
     if (!body)
     if (!body)
@@ -286,8 +290,6 @@ void CollisionShape2D::OnNodeSet(Node* node)
             CreateFixture();
             CreateFixture();
             rigidBody_->AddCollisionShape2D(this);
             rigidBody_->AddCollisionShape2D(this);
         }
         }
-        else
-            LOGERROR("No rigid body component in node, can not create collision shape");
     }
     }
 }
 }
 
 

+ 4 - 4
Source/Atomic/Atomic2D/CollisionShape2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,15 +26,15 @@
 
 
 #include <Box2D/Box2D.h>
 #include <Box2D/Box2D.h>
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class RigidBody2D;
 class RigidBody2D;
 
 
 /// 2D collision shape component.
 /// 2D collision shape component.
-class ATOMIC_API CollisionShape2D : public Component
+class URHO3D_API CollisionShape2D : public Component
 {
 {
-    OBJECT(CollisionShape2D);
+    URHO3D_OBJECT(CollisionShape2D, Component);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 9 - 9
Source/Atomic/Atomic2D/Constraint2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,17 +26,17 @@
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
-#include "../Atomic2D/Constraint2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
-#include "../Atomic2D/PhysicsWorld2D.h"
+#include "../Urho2D/Constraint2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
+#include "../Urho2D/PhysicsWorld2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 
 
 Constraint2D::Constraint2D(Context* context) :
 Constraint2D::Constraint2D(Context* context) :
     Component(context),
     Component(context),
@@ -59,7 +59,7 @@ Constraint2D::~Constraint2D()
 
 
 void Constraint2D::RegisterObject(Context* context)
 void Constraint2D::RegisterObject(Context* context)
 {
 {
-    ACCESSOR_ATTRIBUTE("Collide Connected", GetCollideConnected, SetCollideConnected, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Collide Connected", GetCollideConnected, SetCollideConnected, bool, false, AM_DEFAULT);
 }
 }
 
 
 void Constraint2D::OnSetEnabled()
 void Constraint2D::OnSetEnabled()
@@ -130,7 +130,7 @@ void Constraint2D::OnNodeSet(Node* node)
         ownerBody_ = node->GetComponent<RigidBody2D>();
         ownerBody_ = node->GetComponent<RigidBody2D>();
         if (!ownerBody_)
         if (!ownerBody_)
         {
         {
-            LOGERROR("No rigid body component in node, can not create constraint");
+            URHO3D_LOGERROR("No right body component in node, can not create constraint");
             return;
             return;
         }
         }
     }
     }

+ 4 - 4
Source/Atomic/Atomic2D/Constraint2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,16 +26,16 @@
 
 
 #include <Box2D/Box2D.h>
 #include <Box2D/Box2D.h>
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class RigidBody2D;
 class RigidBody2D;
 class PhysicsWorld2D;
 class PhysicsWorld2D;
 
 
 /// 2D physics constraint component.
 /// 2D physics constraint component.
-class ATOMIC_API Constraint2D : public Component
+class URHO3D_API Constraint2D : public Component
 {
 {
-    OBJECT(Constraint2D);
+    URHO3D_OBJECT(Constraint2D, Component);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 15 - 13
Source/Atomic/Atomic2D/ConstraintDistance2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintDistance2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintDistance2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintDistance2D::ConstraintDistance2D(Context* context) :
 ConstraintDistance2D::ConstraintDistance2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     ownerBodyAnchor_(Vector2::ZERO),
     ownerBodyAnchor_(Vector2::ZERO),
@@ -46,14 +48,14 @@ ConstraintDistance2D::~ConstraintDistance2D()
 
 
 void ConstraintDistance2D::RegisterObject(Context* context)
 void ConstraintDistance2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintDistance2D>();
-
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Owner Body Anchor", GetOwnerBodyAnchor, SetOwnerBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Other Body Anchor", GetOtherBodyAnchor, SetOtherBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Frequency Hz", GetFrequencyHz, SetFrequencyHz, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Damping Ratio", GetDampingRatio, SetDampingRatio, float, 0.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    context->RegisterFactory<ConstraintDistance2D>(URHO2D_CATEGORY);
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Owner Body Anchor", GetOwnerBodyAnchor, SetOwnerBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Other Body Anchor", GetOtherBodyAnchor, SetOtherBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Frequency Hz", GetFrequencyHz, SetFrequencyHz, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Damping Ratio", GetDampingRatio, SetDampingRatio, float, 0.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintDistance2D::SetOwnerBodyAnchor(const Vector2& anchor)
 void ConstraintDistance2D::SetOwnerBodyAnchor(const Vector2& anchor)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintDistance2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D distance constraint component.
 /// 2D distance constraint component.
-class ATOMIC_API ConstraintDistance2D : public Constraint2D
+class URHO3D_API ConstraintDistance2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintDistance2D);
+    URHO3D_OBJECT(ConstraintDistance2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 13 - 11
Source/Atomic/Atomic2D/ConstraintFriction2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintFriction2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintFriction2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintFriction2D::ConstraintFriction2D(Context* context) :
 ConstraintFriction2D::ConstraintFriction2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     anchor_(Vector2::ZERO)
     anchor_(Vector2::ZERO)
@@ -45,13 +47,13 @@ ConstraintFriction2D::~ConstraintFriction2D()
 
 
 void ConstraintFriction2D::RegisterObject(Context* context)
 void ConstraintFriction2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintFriction2D>();
+    context->RegisterFactory<ConstraintFriction2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Force", GetMaxForce, SetMaxForce, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Torque", GetMaxTorque, SetMaxTorque, float, 0.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Force", GetMaxForce, SetMaxForce, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Torque", GetMaxTorque, SetMaxTorque, float, 0.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintFriction2D::SetAnchor(const Vector2& anchor)
 void ConstraintFriction2D::SetAnchor(const Vector2& anchor)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintFriction2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D friction constraint component.
 /// 2D friction constraint component.
-class ATOMIC_API ConstraintFriction2D : public Constraint2D
+class URHO3D_API ConstraintFriction2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintFriction2D);
+    URHO3D_OBJECT(ConstraintFriction2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 11 - 9
Source/Atomic/Atomic2D/ConstraintGear2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintGear2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintGear2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintGear2D::ConstraintGear2D(Context* context) :
 ConstraintGear2D::ConstraintGear2D(Context* context) :
     Constraint2D(context)
     Constraint2D(context)
 {
 {
@@ -43,11 +45,11 @@ ConstraintGear2D::~ConstraintGear2D()
 
 
 void ConstraintGear2D::RegisterObject(Context* context)
 void ConstraintGear2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintGear2D>();
+    context->RegisterFactory<ConstraintGear2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Ratio", GetRatio, SetRatio, float, 0.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Ratio", GetRatio, SetRatio, float, 0.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintGear2D::SetOwnerConstraint(Constraint2D* constraint)
 void ConstraintGear2D::SetOwnerConstraint(Constraint2D* constraint)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintGear2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D gear constraint component.
 /// 2D gear constraint component.
-class ATOMIC_API ConstraintGear2D : public Constraint2D
+class URHO3D_API ConstraintGear2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintGear2D);
+    URHO3D_OBJECT(ConstraintGear2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 16 - 14
Source/Atomic/Atomic2D/ConstraintMotor2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintMotor2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintMotor2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintMotor2D::ConstraintMotor2D(Context* context) :
 ConstraintMotor2D::ConstraintMotor2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     linearOffset_(Vector2::ZERO)
     linearOffset_(Vector2::ZERO)
@@ -45,15 +47,15 @@ ConstraintMotor2D::~ConstraintMotor2D()
 
 
 void ConstraintMotor2D::RegisterObject(Context* context)
 void ConstraintMotor2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintMotor2D>();
-
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Linear Offset", GetLinearOffset, SetLinearOffset, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Angular Offset", GetAngularOffset, SetAngularOffset, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Force", GetMaxForce, SetMaxForce, float, 1.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Torque", GetMaxTorque, SetMaxTorque, float, 1.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Correction Factor", GetCorrectionFactor, SetCorrectionFactor, float, 0.3f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    context->RegisterFactory<ConstraintMotor2D>(URHO2D_CATEGORY);
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Linear Offset", GetLinearOffset, SetLinearOffset, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Angular Offset", GetAngularOffset, SetAngularOffset, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Force", GetMaxForce, SetMaxForce, float, 1.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Torque", GetMaxTorque, SetMaxTorque, float, 1.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Correction Factor", GetCorrectionFactor, SetCorrectionFactor, float, 0.3f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintMotor2D::SetLinearOffset(const Vector2& linearOffset)
 void ConstraintMotor2D::SetLinearOffset(const Vector2& linearOffset)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintMotor2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D motor constraint component.
 /// 2D motor constraint component.
-class ATOMIC_API ConstraintMotor2D : public Constraint2D
+class URHO3D_API ConstraintMotor2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintMotor2D);
+    URHO3D_OBJECT(ConstraintMotor2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 15 - 13
Source/Atomic/Atomic2D/ConstraintMouse2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintMouse2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintMouse2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintMouse2D::ConstraintMouse2D(Context* context) :
 ConstraintMouse2D::ConstraintMouse2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     target_(Vector2::ZERO),
     target_(Vector2::ZERO),
@@ -45,14 +47,14 @@ ConstraintMouse2D::~ConstraintMouse2D()
 
 
 void ConstraintMouse2D::RegisterObject(Context* context)
 void ConstraintMouse2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintMouse2D>();
-
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Target", GetTarget, SetTarget, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Force", GetMaxForce, SetMaxForce, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Frequency Hz", GetFrequencyHz, SetFrequencyHz, float, 5.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Damping Ratio", GetDampingRatio, SetDampingRatio, float, 0.7f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    context->RegisterFactory<ConstraintMouse2D>(URHO2D_CATEGORY);
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Target", GetTarget, SetTarget, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Force", GetMaxForce, SetMaxForce, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Frequency Hz", GetFrequencyHz, SetFrequencyHz, float, 5.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Damping Ratio", GetDampingRatio, SetDampingRatio, float, 0.7f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintMouse2D::SetTarget(const Vector2& target)
 void ConstraintMouse2D::SetTarget(const Vector2& target)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintMouse2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D mouse constraint component.
 /// 2D mouse constraint component.
-class ATOMIC_API ConstraintMouse2D : public Constraint2D
+class URHO3D_API ConstraintMouse2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintMouse2D);
+    URHO3D_OBJECT(ConstraintMouse2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 19 - 17
Source/Atomic/Atomic2D/ConstraintPrismatic2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintPrismatic2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintPrismatic2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintPrismatic2D::ConstraintPrismatic2D(Context* context) :
 ConstraintPrismatic2D::ConstraintPrismatic2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     anchor_(Vector2::ZERO),
     anchor_(Vector2::ZERO),
@@ -45,18 +47,18 @@ ConstraintPrismatic2D::~ConstraintPrismatic2D()
 
 
 void ConstraintPrismatic2D::RegisterObject(Context* context)
 void ConstraintPrismatic2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintPrismatic2D>();
-
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Axis", GetAxis, SetAxis, Vector2, Vector2::RIGHT, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Enable Limit", GetEnableLimit, SetEnableLimit, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Lower translation", GetLowerTranslation, SetLowerTranslation, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Upper translation", GetUpperTranslation, SetUpperTranslation, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Enable Motor", GetEnableMotor, SetEnableMotor, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Motor Force", GetMaxMotorForce, SetMaxMotorForce, float, 2.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Motor Speed", GetMotorSpeed, SetMotorSpeed, float, 0.7f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    context->RegisterFactory<ConstraintPrismatic2D>(URHO2D_CATEGORY);
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Axis", GetAxis, SetAxis, Vector2, Vector2::RIGHT, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Enable Limit", GetEnableLimit, SetEnableLimit, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Lower translation", GetLowerTranslation, SetLowerTranslation, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Upper translation", GetUpperTranslation, SetUpperTranslation, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Enable Motor", GetEnableMotor, SetEnableMotor, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Motor Force", GetMaxMotorForce, SetMaxMotorForce, float, 2.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Motor Speed", GetMotorSpeed, SetMotorSpeed, float, 0.7f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintPrismatic2D::SetAnchor(const Vector2& anchor)
 void ConstraintPrismatic2D::SetAnchor(const Vector2& anchor)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintPrismatic2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D prismatic constraint component.
 /// 2D prismatic constraint component.
-class ATOMIC_API ConstraintPrismatic2D : public Constraint2D
+class URHO3D_API ConstraintPrismatic2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintPrismatic2D);
+    URHO3D_OBJECT(ConstraintPrismatic2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 15 - 13
Source/Atomic/Atomic2D/ConstraintPulley2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintPulley2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintPulley2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintPulley2D::ConstraintPulley2D(Context* context) :
 ConstraintPulley2D::ConstraintPulley2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     ownerBodyGroundAnchor_(-1.0f, 1.0f),
     ownerBodyGroundAnchor_(-1.0f, 1.0f),
@@ -48,17 +50,17 @@ ConstraintPulley2D::~ConstraintPulley2D()
 
 
 void ConstraintPulley2D::RegisterObject(Context* context)
 void ConstraintPulley2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintPulley2D>();
+    context->RegisterFactory<ConstraintPulley2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Owner Body Ground Anchor", GetOwnerBodyGroundAnchor, SetOwnerBodyGroundAnchor, Vector2, Vector2::ZERO,
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Owner Body Ground Anchor", GetOwnerBodyGroundAnchor, SetOwnerBodyGroundAnchor, Vector2, Vector2::ZERO,
         AM_DEFAULT);
         AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Other Body Ground Anchor", GetOtherBodyGroundAnchor, SetOtherBodyGroundAnchor, Vector2, Vector2::ZERO,
+    URHO3D_ACCESSOR_ATTRIBUTE("Other Body Ground Anchor", GetOtherBodyGroundAnchor, SetOtherBodyGroundAnchor, Vector2, Vector2::ZERO,
         AM_DEFAULT);
         AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Owner Body Anchor", GetOwnerBodyAnchor, SetOwnerBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Other Body Anchor", GetOtherBodyAnchor, SetOtherBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Ratio", GetRatio, SetRatio, float, 0.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    URHO3D_ACCESSOR_ATTRIBUTE("Owner Body Anchor", GetOwnerBodyAnchor, SetOwnerBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Other Body Anchor", GetOtherBodyAnchor, SetOtherBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Ratio", GetRatio, SetRatio, float, 0.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintPulley2D::SetOwnerBodyGroundAnchor(const Vector2& groundAnchor)
 void ConstraintPulley2D::SetOwnerBodyGroundAnchor(const Vector2& groundAnchor)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintPulley2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D pulley constraint component.
 /// 2D pulley constraint component.
-class ATOMIC_API ConstraintPulley2D : public Constraint2D
+class URHO3D_API ConstraintPulley2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintPulley2D);
+    URHO3D_OBJECT(ConstraintPulley2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 18 - 16
Source/Atomic/Atomic2D/ConstraintRevolute2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintRevolute2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintRevolute2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintRevolute2D::ConstraintRevolute2D(Context* context) :
 ConstraintRevolute2D::ConstraintRevolute2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     anchor_(Vector2::ZERO)
     anchor_(Vector2::ZERO)
@@ -44,17 +46,17 @@ ConstraintRevolute2D::~ConstraintRevolute2D()
 
 
 void ConstraintRevolute2D::RegisterObject(Context* context)
 void ConstraintRevolute2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintRevolute2D>();
-
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Enable Limit", GetEnableLimit, SetEnableLimit, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Lower Angle", GetLowerAngle, SetLowerAngle, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Upper Angle", GetUpperAngle, SetUpperAngle, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Enable Motor", GetEnableMotor, SetEnableMotor, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Motor Speed", GetMotorSpeed, SetMotorSpeed, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Motor Torque", GetMaxMotorTorque, SetMaxMotorTorque, float, 0.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    context->RegisterFactory<ConstraintRevolute2D>(URHO2D_CATEGORY);
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Enable Limit", GetEnableLimit, SetEnableLimit, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Lower Angle", GetLowerAngle, SetLowerAngle, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Upper Angle", GetUpperAngle, SetUpperAngle, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Enable Motor", GetEnableMotor, SetEnableMotor, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Motor Speed", GetMotorSpeed, SetMotorSpeed, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Motor Torque", GetMaxMotorTorque, SetMaxMotorTorque, float, 0.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintRevolute2D::SetAnchor(const Vector2& anchor)
 void ConstraintRevolute2D::SetAnchor(const Vector2& anchor)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintRevolute2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D revolute constraint component.
 /// 2D revolute constraint component.
-class ATOMIC_API ConstraintRevolute2D : public Constraint2D
+class URHO3D_API ConstraintRevolute2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintRevolute2D);
+    URHO3D_OBJECT(ConstraintRevolute2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 13 - 11
Source/Atomic/Atomic2D/ConstraintRope2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintRope2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintRope2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintRope2D::ConstraintRope2D(Context* context) :
 ConstraintRope2D::ConstraintRope2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     ownerBodyAnchor_(Vector2::ZERO),
     ownerBodyAnchor_(Vector2::ZERO),
@@ -46,13 +48,13 @@ ConstraintRope2D::~ConstraintRope2D()
 
 
 void ConstraintRope2D::RegisterObject(Context* context)
 void ConstraintRope2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintRope2D>();
+    context->RegisterFactory<ConstraintRope2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Owner Body Anchor", GetOwnerBodyAnchor, SetOwnerBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Other Body Anchor", GetOtherBodyAnchor, SetOtherBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Length", GetMaxLength, SetMaxLength, float, 0.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Owner Body Anchor", GetOwnerBodyAnchor, SetOwnerBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Other Body Anchor", GetOtherBodyAnchor, SetOtherBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Length", GetMaxLength, SetMaxLength, float, 0.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintRope2D::SetOwnerBodyAnchor(const Vector2& anchor)
 void ConstraintRope2D::SetOwnerBodyAnchor(const Vector2& anchor)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintRope2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D rope constraint component.
 /// 2D rope constraint component.
-class ATOMIC_API ConstraintRope2D : public Constraint2D
+class URHO3D_API ConstraintRope2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintRope2D);
+    URHO3D_OBJECT(ConstraintRope2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 13 - 11
Source/Atomic/Atomic2D/ConstraintWeld2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintWeld2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintWeld2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintWeld2D::ConstraintWeld2D(Context* context) :
 ConstraintWeld2D::ConstraintWeld2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     anchor_(Vector2::ZERO)
     anchor_(Vector2::ZERO)
@@ -44,13 +46,13 @@ ConstraintWeld2D::~ConstraintWeld2D()
 
 
 void ConstraintWeld2D::RegisterObject(Context* context)
 void ConstraintWeld2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintWeld2D>();
+    context->RegisterFactory<ConstraintWeld2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Frequency Hz", GetFrequencyHz, SetFrequencyHz, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Damping Ratio", GetDampingRatio, SetDampingRatio, float, 0.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Frequency Hz", GetFrequencyHz, SetFrequencyHz, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Damping Ratio", GetDampingRatio, SetDampingRatio, float, 0.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintWeld2D::SetAnchor(const Vector2& anchor)
 void ConstraintWeld2D::SetAnchor(const Vector2& anchor)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintWeld2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D weld constraint component.
 /// 2D weld constraint component.
-class ATOMIC_API ConstraintWeld2D : public Constraint2D
+class URHO3D_API ConstraintWeld2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintWeld2D);
+    URHO3D_OBJECT(ConstraintWeld2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 18 - 16
Source/Atomic/Atomic2D/ConstraintWheel2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Atomic2D/ConstraintWheel2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/ConstraintWheel2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* URHO2D_CATEGORY;
+
 ConstraintWheel2D::ConstraintWheel2D(Context* context) :
 ConstraintWheel2D::ConstraintWheel2D(Context* context) :
     Constraint2D(context),
     Constraint2D(context),
     anchor_(Vector2::ZERO),
     anchor_(Vector2::ZERO),
@@ -45,17 +47,17 @@ ConstraintWheel2D::~ConstraintWheel2D()
 
 
 void ConstraintWheel2D::RegisterObject(Context* context)
 void ConstraintWheel2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ConstraintWheel2D>();
-
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Axis", GetAxis, SetAxis, Vector2, Vector2::RIGHT, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Enable Motor", GetEnableMotor, SetEnableMotor, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Motor Torque", GetMaxMotorTorque, SetMaxMotorTorque, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Motor Speed", GetMotorSpeed, SetMotorSpeed, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Frequency Hz", GetFrequencyHz, SetFrequencyHz, float, 2.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Damping Ratio", GetDampingRatio, SetDampingRatio, float, 0.7f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Constraint2D);
+    context->RegisterFactory<ConstraintWheel2D>(URHO2D_CATEGORY);
+
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Anchor", GetAnchor, SetAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Axis", GetAxis, SetAxis, Vector2, Vector2::RIGHT, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Enable Motor", GetEnableMotor, SetEnableMotor, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Motor Torque", GetMaxMotorTorque, SetMaxMotorTorque, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Motor Speed", GetMotorSpeed, SetMotorSpeed, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Frequency Hz", GetFrequencyHz, SetFrequencyHz, float, 2.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Damping Ratio", GetDampingRatio, SetDampingRatio, float, 0.7f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 }
 
 
 void ConstraintWheel2D::SetAnchor(const Vector2& anchor)
 void ConstraintWheel2D::SetAnchor(const Vector2& anchor)

+ 5 - 5
Source/Atomic/Atomic2D/ConstraintWheel2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Constraint2D.h"
+#include "../Urho2D/Constraint2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// 2D wheel constraint component.
 /// 2D wheel constraint component.
-class ATOMIC_API ConstraintWheel2D : public Constraint2D
+class URHO3D_API ConstraintWheel2D : public Constraint2D
 {
 {
-    OBJECT(ConstraintWheel2D);
+    URHO3D_OBJECT(ConstraintWheel2D, Constraint2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 9 - 7
Source/Atomic/Atomic2D/Drawable2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -27,17 +27,18 @@
 #include "../Graphics/Material.h"
 #include "../Graphics/Material.h"
 #include "../Graphics/Texture2D.h"
 #include "../Graphics/Texture2D.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
-#include "../Atomic2D/Drawable2D.h"
-#include "../Atomic2D/Renderer2D.h"
+#include "../Urho2D/Drawable2D.h"
+#include "../Urho2D/Renderer2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 const float PIXEL_SIZE = 0.01f;
 const float PIXEL_SIZE = 0.01f;
 
 
 SourceBatch2D::SourceBatch2D() :
 SourceBatch2D::SourceBatch2D() :
+    distance_(0.0f),
     drawOrder_(0)
     drawOrder_(0)
 {
 {
 }
 }
@@ -58,8 +59,9 @@ Drawable2D::~Drawable2D()
 
 
 void Drawable2D::RegisterObject(Context* context)
 void Drawable2D::RegisterObject(Context* context)
 {
 {
-    ACCESSOR_ATTRIBUTE("Layer", GetLayer, SetLayer, int, 0, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Order in Layer", GetOrderInLayer, SetOrderInLayer, int, 0, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Layer", GetLayer, SetLayer, int, 0, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Order in Layer", GetOrderInLayer, SetOrderInLayer, int, 0, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("View Mask", int, viewMask_, DEFAULT_VIEWMASK, AM_DEFAULT);
 }
 }
 
 
 void Drawable2D::OnSetEnabled()
 void Drawable2D::OnSetEnabled()
@@ -127,4 +129,4 @@ void Drawable2D::OnMarkedDirty(Node* node)
     sourceBatchesDirty_ = true;
     sourceBatchesDirty_ = true;
 }
 }
 
 
-}
+}

+ 10 - 5
Source/Atomic/Atomic2D/Drawable2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,9 +25,10 @@
 #include "../Graphics/Drawable.h"
 #include "../Graphics/Drawable.h"
 #include "../Graphics/GraphicsDefs.h"
 #include "../Graphics/GraphicsDefs.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+class Drawable2D; 
 class Renderer2D;
 class Renderer2D;
 class Texture2D;
 class Texture2D;
 class VertexBuffer;
 class VertexBuffer;
@@ -49,6 +50,10 @@ struct SourceBatch2D
     /// Construct.
     /// Construct.
     SourceBatch2D();
     SourceBatch2D();
 
 
+    /// Owner.
+    WeakPtr<Drawable2D> owner_;
+    /// Distance to camera.
+    mutable float distance_;
     /// Draw order.
     /// Draw order.
     int drawOrder_;
     int drawOrder_;
     /// Material.
     /// Material.
@@ -58,12 +63,12 @@ struct SourceBatch2D
 };
 };
 
 
 /// Pixel size (equal 0.01f).
 /// Pixel size (equal 0.01f).
-extern ATOMIC_API const float PIXEL_SIZE;
+extern URHO3D_API const float PIXEL_SIZE;
 
 
 /// Base class for 2D visible components.
 /// Base class for 2D visible components.
-class ATOMIC_API Drawable2D : public Drawable
+class URHO3D_API Drawable2D : public Drawable
 {
 {
-    OBJECT(Drawable2D);
+    URHO3D_OBJECT(Drawable2D, Drawable);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 58 - 10
Source/Atomic/Atomic2D/ParticleEffect2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -27,12 +27,12 @@
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/XMLFile.h"
 #include "../Resource/XMLFile.h"
-#include "../Atomic2D/ParticleEffect2D.h"
-#include "../Atomic2D/Sprite2D.h"
+#include "../Urho2D/ParticleEffect2D.h"
+#include "../Urho2D/Sprite2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 static const int srcBlendFuncs[] =
 static const int srcBlendFuncs[] =
@@ -79,7 +79,7 @@ ParticleEffect2D::ParticleEffect2D(Context* context) :
     startParticleSize_(60.0f),
     startParticleSize_(60.0f),
     startParticleSizeVariance_(40.0f),
     startParticleSizeVariance_(40.0f),
     finishParticleSize_(5.0f),
     finishParticleSize_(5.0f),
-    FinishParticleSizeVariance_(5.0f),
+    finishParticleSizeVariance_(5.0f),
     duration_(-1.0f),
     duration_(-1.0f),
     emitterType_(EMITTER_TYPE_GRAVITY),
     emitterType_(EMITTER_TYPE_GRAVITY),
     maxRadius_(100.0f),
     maxRadius_(100.0f),
@@ -158,7 +158,7 @@ bool ParticleEffect2D::BeginLoad(Deserializer& source)
 
 
     finishParticleSize_ = ReadFloat(rootElem, "finishParticleSize");
     finishParticleSize_ = ReadFloat(rootElem, "finishParticleSize");
     // Typo in pex file
     // Typo in pex file
-    FinishParticleSizeVariance_ = ReadFloat(rootElem, "FinishParticleSizeVariance");
+    finishParticleSizeVariance_ = ReadFloat(rootElem, "FinishParticleSizeVariance");
 
 
     duration_ = M_INFINITY;
     duration_ = M_INFINITY;
     if (rootElem.HasChild("duration"))
     if (rootElem.HasChild("duration"))
@@ -197,6 +197,8 @@ bool ParticleEffect2D::BeginLoad(Deserializer& source)
     rotationEnd_ = ReadFloat(rootElem, "rotationEnd");
     rotationEnd_ = ReadFloat(rootElem, "rotationEnd");
     rotationEndVariance_ = ReadFloat(rootElem, "rotationEndVariance");
     rotationEndVariance_ = ReadFloat(rootElem, "rotationEndVariance");
 
 
+    // Note: not accurate
+    SetMemoryUse(source.GetSize());
     return true;
     return true;
 }
 }
 
 
@@ -208,7 +210,7 @@ bool ParticleEffect2D::EndLoad()
         ResourceCache* cache = GetSubsystem<ResourceCache>();
         ResourceCache* cache = GetSubsystem<ResourceCache>();
         sprite_ = cache->GetResource<Sprite2D>(loadSpriteName_);
         sprite_ = cache->GetResource<Sprite2D>(loadSpriteName_);
         if (!sprite_)
         if (!sprite_)
-            LOGERROR("Could not load sprite " + loadSpriteName_ + " for particle effect");
+            URHO3D_LOGERROR("Could not load sprite " + loadSpriteName_ + " for particle effect");
 
 
         loadSpriteName_.Clear();
         loadSpriteName_.Clear();
     }
     }
@@ -260,7 +262,7 @@ bool ParticleEffect2D::Save(Serializer& dest) const
 
 
     WriteFloat(rootElem, "finishParticleSize", finishParticleSize_);
     WriteFloat(rootElem, "finishParticleSize", finishParticleSize_);
     // Typo in pex file
     // Typo in pex file
-    WriteFloat(rootElem, "FinishParticleSizeVariance", FinishParticleSizeVariance_);
+    WriteFloat(rootElem, "FinishParticleSizeVariance", finishParticleSizeVariance_);
 
 
     float duration = duration_;
     float duration = duration_;
     if (duration == M_INFINITY)
     if (duration == M_INFINITY)
@@ -393,9 +395,9 @@ void ParticleEffect2D::SetFinishParticleSize(float finishParticleSize)
     finishParticleSize_ = finishParticleSize;
     finishParticleSize_ = finishParticleSize;
 }
 }
 
 
-void ParticleEffect2D::SetFinishParticleSizeVariance(float FinishParticleSizeVariance)
+void ParticleEffect2D::SetFinishParticleSizeVariance(float finishParticleSizeVariance)
 {
 {
-    FinishParticleSizeVariance_ = FinishParticleSizeVariance;
+    finishParticleSizeVariance_ = finishParticleSizeVariance;
 }
 }
 
 
 void ParticleEffect2D::SetDuration(float duration)
 void ParticleEffect2D::SetDuration(float duration)
@@ -463,6 +465,52 @@ void ParticleEffect2D::SetRotationEndVariance(float rotationEndVariance)
     rotationEndVariance_ = rotationEndVariance;
     rotationEndVariance_ = rotationEndVariance;
 }
 }
 
 
+SharedPtr<ParticleEffect2D> ParticleEffect2D::Clone(const String& cloneName) const
+{
+    SharedPtr<ParticleEffect2D> ret(new ParticleEffect2D(context_));
+
+    ret->SetName(cloneName);
+    ret->sprite_ = sprite_;
+    ret->sourcePositionVariance_ = sourcePositionVariance_;
+    ret->speed_ = speed_;
+    ret->speedVariance_ = speedVariance_;
+    ret->particleLifeSpan_ = particleLifeSpan_;
+    ret->particleLifespanVariance_ = particleLifespanVariance_;
+    ret->angle_ = angle_;
+    ret->angleVariance_ = angleVariance_;
+    ret->gravity_ = gravity_;
+    ret->radialAcceleration_ = radialAcceleration_;
+    ret->tangentialAcceleration_ = tangentialAcceleration_;
+    ret->radialAccelVariance_ = radialAccelVariance_;
+    ret->tangentialAccelVariance_ = tangentialAccelVariance_;
+    ret->startColor_ = startColor_;
+    ret->startColorVariance_ = startColorVariance_;
+    ret->finishColor_ = finishColor_;
+    ret->finishColorVariance_ = finishColorVariance_;
+    ret->maxParticles_ = maxParticles_;
+    ret->startParticleSize_ = startParticleSize_;
+    ret->startParticleSizeVariance_ = startParticleSizeVariance_;
+    ret->finishParticleSize_ = finishParticleSize_;
+    ret->finishParticleSizeVariance_ = finishParticleSizeVariance_;
+    ret->duration_ = duration_;
+    ret->emitterType_ = emitterType_;
+    ret->maxRadius_ = maxRadius_;
+    ret->maxRadiusVariance_ = maxRadiusVariance_;
+    ret->minRadius_ = minRadius_;
+    ret->minRadiusVariance_ = minRadiusVariance_;
+    ret->rotatePerSecond_ = rotatePerSecond_;
+    ret->rotatePerSecondVariance_ = rotatePerSecondVariance_;
+    ret->blendMode_ = blendMode_;
+    ret->rotationStart_ = rotationStart_;
+    ret->rotationStartVariance_ = rotationStartVariance_;
+    ret->rotationEnd_ = rotationEnd_;
+    ret->rotationEndVariance_ = rotationEndVariance_;
+    /// \todo Zero if source was created programmatically
+    ret->SetMemoryUse(GetMemoryUse());
+    
+    return ret;
+}
+
 int ParticleEffect2D::ReadInt(const XMLElement& element, const String& name) const
 int ParticleEffect2D::ReadInt(const XMLElement& element, const String& name) const
 {
 {
     return element.GetChild(name).GetInt("value");
     return element.GetChild(name).GetInt("value");

+ 8 - 6
Source/Atomic/Atomic2D/ParticleEffect2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,7 @@
 #include "../Graphics/GraphicsDefs.h"
 #include "../Graphics/GraphicsDefs.h"
 #include "../Resource/Resource.h"
 #include "../Resource/Resource.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class XMLElement;
 class XMLElement;
@@ -39,9 +39,9 @@ enum EmitterType2D
 };
 };
 
 
 /// 2D particle effect resource.
 /// 2D particle effect resource.
-class ATOMIC_API ParticleEffect2D : public Resource
+class URHO3D_API ParticleEffect2D : public Resource
 {
 {
-    OBJECT(ParticleEffect2D);
+    URHO3D_OBJECT(ParticleEffect2D, Resource);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -128,6 +128,8 @@ public:
     void SetRotationEnd(float rotationEnd);
     void SetRotationEnd(float rotationEnd);
     /// Set rotation end variance.
     /// Set rotation end variance.
     void SetRotationEndVariance(float rotationEndVariance);
     void SetRotationEndVariance(float rotationEndVariance);
+    /// Clone the 2D particle effect.
+    SharedPtr<ParticleEffect2D> Clone(const String& cloneName = String::EMPTY) const;
 
 
     /// Return sprite.
     /// Return sprite.
     Sprite2D* GetSprite() const { return sprite_; }
     Sprite2D* GetSprite() const { return sprite_; }
@@ -193,7 +195,7 @@ public:
     float GetFinishParticleSize() const { return finishParticleSize_; }
     float GetFinishParticleSize() const { return finishParticleSize_; }
 
 
     /// Return finish particle size variance.
     /// Return finish particle size variance.
-    float GetFinishParticleSizeVariance() const { return FinishParticleSizeVariance_; }
+    float GetFinishParticleSizeVariance() const { return finishParticleSizeVariance_; }
 
 
     /// Return duration.
     /// Return duration.
     float GetDuration() const { return duration_; }
     float GetDuration() const { return duration_; }
@@ -295,7 +297,7 @@ private:
     /// Finish particle size.
     /// Finish particle size.
     float finishParticleSize_;
     float finishParticleSize_;
     /// Finish particle size variance.
     /// Finish particle size variance.
-    float FinishParticleSizeVariance_;
+    float finishParticleSizeVariance_;
     /// Duration.
     /// Duration.
     float duration_;
     float duration_;
     /// Emitter type.
     /// Emitter type.

+ 31 - 45
Source/Atomic/Atomic2D/ParticleEmitter2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -28,17 +28,17 @@
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
 #include "../Scene/SceneEvents.h"
 #include "../Scene/SceneEvents.h"
-#include "../Atomic2D/ParticleEffect2D.h"
-#include "../Atomic2D/ParticleEmitter2D.h"
-#include "../Atomic2D/Renderer2D.h"
-#include "../Atomic2D/Sprite2D.h"
+#include "../Urho2D/ParticleEffect2D.h"
+#include "../Urho2D/ParticleEmitter2D.h"
+#include "../Urho2D/Renderer2D.h"
+#include "../Urho2D/Sprite2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 extern const char* blendModeNames[];
 extern const char* blendModeNames[];
 
 
 ParticleEmitter2D::ParticleEmitter2D(Context* context) :
 ParticleEmitter2D::ParticleEmitter2D(Context* context) :
@@ -51,6 +51,7 @@ ParticleEmitter2D::ParticleEmitter2D(Context* context) :
     boundingBoxMaxPoint_(Vector3::ZERO)
     boundingBoxMaxPoint_(Vector3::ZERO)
 {
 {
     sourceBatches_.Resize(1);
     sourceBatches_.Resize(1);
+    sourceBatches_[0].owner_ = this;
 }
 }
 
 
 ParticleEmitter2D::~ParticleEmitter2D()
 ParticleEmitter2D::~ParticleEmitter2D()
@@ -59,15 +60,15 @@ ParticleEmitter2D::~ParticleEmitter2D()
 
 
 void ParticleEmitter2D::RegisterObject(Context* context)
 void ParticleEmitter2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<ParticleEmitter2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<ParticleEmitter2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Drawable2D);
-    MIXED_ACCESSOR_ATTRIBUTE("Particle Effect", GetParticleEffectAttr, SetParticleEffectAttr, ResourceRef,
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Drawable2D);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Particle Effect", GetParticleEffectAttr, SetParticleEffectAttr, ResourceRef,
         ResourceRef(ParticleEffect2D::GetTypeStatic()), AM_DEFAULT);
         ResourceRef(ParticleEffect2D::GetTypeStatic()), AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Sprite ", GetSpriteAttr, SetSpriteAttr, ResourceRef, ResourceRef(Sprite2D::GetTypeStatic()),
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Sprite ", GetSpriteAttr, SetSpriteAttr, ResourceRef, ResourceRef(Sprite2D::GetTypeStatic()),
         AM_DEFAULT);
         AM_DEFAULT);
-    ENUM_ACCESSOR_ATTRIBUTE("Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModeNames, BLEND_ALPHA, AM_DEFAULT);
+    URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModeNames, BLEND_ALPHA, AM_DEFAULT);
 }
 }
 
 
 void ParticleEmitter2D::OnSetEnabled()
 void ParticleEmitter2D::OnSetEnabled()
@@ -78,7 +79,7 @@ void ParticleEmitter2D::OnSetEnabled()
     if (scene)
     if (scene)
     {
     {
         if (IsEnabledEffective())
         if (IsEnabledEffective())
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter2D, HandleScenePostUpdate));
+            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(ParticleEmitter2D, HandleScenePostUpdate));
         else
         else
             UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
             UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
     }
     }
@@ -127,7 +128,7 @@ void ParticleEmitter2D::SetBlendMode(BlendMode blendMode)
 
 
 void ParticleEmitter2D::SetMaxParticles(unsigned maxParticles)
 void ParticleEmitter2D::SetMaxParticles(unsigned maxParticles)
 {
 {
-    maxParticles = (unsigned)Max(maxParticles, 1);
+    maxParticles = Max(maxParticles, 1U);
 
 
     particles_.Resize(maxParticles);
     particles_.Resize(maxParticles);
     sourceBatches_[0].vertices_.Reserve(maxParticles * 4);
     sourceBatches_[0].vertices_.Reserve(maxParticles * 4);
@@ -173,7 +174,7 @@ void ParticleEmitter2D::OnSceneSet(Scene* scene)
     Drawable2D::OnSceneSet(scene);
     Drawable2D::OnSceneSet(scene);
 
 
     if (scene && IsEnabledEffective())
     if (scene && IsEnabledEffective())
-        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter2D, HandleScenePostUpdate));
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(ParticleEmitter2D, HandleScenePostUpdate));
     else if (!scene)
     else if (!scene)
         UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
         UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
 }
 }
@@ -227,7 +228,7 @@ void ParticleEmitter2D::UpdateSourceBatches()
     vertex2.uv_ = textureRect.max_;
     vertex2.uv_ = textureRect.max_;
     vertex3.uv_ = Vector2(textureRect.max_.x_, textureRect.min_.y_);
     vertex3.uv_ = Vector2(textureRect.max_.x_, textureRect.min_.y_);
 
 
-    for (int i = 0; i < numParticles_; ++i)
+    for (unsigned i = 0; i < numParticles_; ++i)
     {
     {
         Particle2D& p = particles_[i];
         Particle2D& p = particles_[i];
 
 
@@ -237,10 +238,10 @@ void ParticleEmitter2D::UpdateSourceBatches()
         float add = (c + s) * p.size_ * 0.5f;
         float add = (c + s) * p.size_ * 0.5f;
         float sub = (c - s) * p.size_ * 0.5f;
         float sub = (c - s) * p.size_ * 0.5f;
 
 
-        vertex0.position_ = Vector3(p.position_.x_ - sub, p.position_.y_ - add, 0.0f);
-        vertex1.position_ = Vector3(p.position_.x_ - add, p.position_.y_ + sub, 0.0f);
-        vertex2.position_ = Vector3(p.position_.x_ + sub, p.position_.y_ + add, 0.0f);
-        vertex3.position_ = Vector3(p.position_.x_ + add, p.position_.y_ - sub, 0.0f);
+        vertex0.position_ = Vector3(p.position_.x_ - sub, p.position_.y_ - add, p.position_.z_);
+        vertex1.position_ = Vector3(p.position_.x_ - add, p.position_.y_ + sub, p.position_.z_);
+        vertex2.position_ = Vector3(p.position_.x_ + sub, p.position_.y_ + add, p.position_.z_);
+        vertex3.position_ = Vector3(p.position_.x_ + add, p.position_.y_ - sub, p.position_.z_);
 
 
         vertex0.color_ = vertex1.color_ = vertex2.color_ = vertex3.color_ = p.color_.ToUInt();
         vertex0.color_ = vertex1.color_ = vertex2.color_ = vertex3.color_ = p.color_.ToUInt();
 
 
@@ -276,10 +277,10 @@ void ParticleEmitter2D::Update(float timeStep)
     Vector3 worldPosition = GetNode()->GetWorldPosition();
     Vector3 worldPosition = GetNode()->GetWorldPosition();
     float worldScale = GetNode()->GetWorldScale().x_ * PIXEL_SIZE;
     float worldScale = GetNode()->GetWorldScale().x_ * PIXEL_SIZE;
 
 
-    boundingBoxMinPoint_ = Vector3(M_INFINITY, M_INFINITY, 0.0f);
-    boundingBoxMaxPoint_ = Vector3(-M_INFINITY, -M_INFINITY, 0.0f);
+    boundingBoxMinPoint_ = Vector3(M_INFINITY, M_INFINITY, M_INFINITY);
+    boundingBoxMaxPoint_ = Vector3(-M_INFINITY, -M_INFINITY, -M_INFINITY);
 
 
-    int particleIndex = 0;
+    unsigned particleIndex = 0;
     while (particleIndex < numParticles_)
     while (particleIndex < numParticles_)
     {
     {
         Particle2D& particle = particles_[particleIndex];
         Particle2D& particle = particles_[particleIndex];
@@ -322,7 +323,7 @@ void ParticleEmitter2D::Update(float timeStep)
 
 
 bool ParticleEmitter2D::EmitParticle(const Vector3& worldPosition, float worldAngle, float worldScale)
 bool ParticleEmitter2D::EmitParticle(const Vector3& worldPosition, float worldAngle, float worldScale)
 {
 {
-    if (numParticles_ >= effect_->GetMaxParticles())
+    if (numParticles_ >= (unsigned)effect_->GetMaxParticles() || numParticles_ >= particles_.Size())
         return false;
         return false;
 
 
     float lifespan = effect_->GetParticleLifeSpan() + effect_->GetParticleLifespanVariance() * Random(-1.0f, 1.0f);
     float lifespan = effect_->GetParticleLifeSpan() + effect_->GetParticleLifespanVariance() * Random(-1.0f, 1.0f);
@@ -336,6 +337,7 @@ bool ParticleEmitter2D::EmitParticle(const Vector3& worldPosition, float worldAn
 
 
     particle.position_.x_ = worldPosition.x_ + worldScale * effect_->GetSourcePositionVariance().x_ * Random(-1.0f, 1.0f);
     particle.position_.x_ = worldPosition.x_ + worldScale * effect_->GetSourcePositionVariance().x_ * Random(-1.0f, 1.0f);
     particle.position_.y_ = worldPosition.y_ + worldScale * effect_->GetSourcePositionVariance().y_ * Random(-1.0f, 1.0f);
     particle.position_.y_ = worldPosition.y_ + worldScale * effect_->GetSourcePositionVariance().y_ * Random(-1.0f, 1.0f);
+    particle.position_.z_ = worldPosition.z_;
     particle.startPos_.x_ = worldPosition.x_;
     particle.startPos_.x_ = worldPosition.x_;
     particle.startPos_.y_ = worldPosition.y_;
     particle.startPos_.y_ = worldPosition.y_;
 
 
@@ -362,26 +364,8 @@ bool ParticleEmitter2D::EmitParticle(const Vector3& worldPosition, float worldAn
     particle.size_ = startSize;
     particle.size_ = startSize;
     particle.sizeDelta_ = (finishSize - startSize) * invLifespan;
     particle.sizeDelta_ = (finishSize - startSize) * invLifespan;
 
 
-    // ATOMIC BEGIN
-
-    const Color& startColor = effect_->GetStartColor();
-    const Color& startColorVariance = effect_->GetStartColorVariance();
-    const Color& finishColor = effect_->GetFinishColor();
-    const Color& finishColorVariance = effect_->GetFinishColorVariance();
-
-    particle.color_.r_  = startColor.r_ + startColorVariance.r_ * Random(-1.0f, 1.0f);
-    particle.color_.g_  = startColor.g_ + startColorVariance.g_ * Random(-1.0f, 1.0f);
-    particle.color_.b_  = startColor.b_ + startColorVariance.b_ * Random(-1.0f, 1.0f);
-    particle.color_.a_  = startColor.a_ + startColorVariance.a_ * Random(-1.0f, 1.0f);
-
-    Color endColor;
-    endColor.r_ = finishColor.r_ + finishColorVariance.r_ * Random(-1.0f, 1.0f);
-    endColor.g_ = finishColor.g_ + finishColorVariance.g_ * Random(-1.0f, 1.0f);
-    endColor.b_ = finishColor.b_ + finishColorVariance.b_ * Random(-1.0f, 1.0f);
-    endColor.a_ = finishColor.a_ + finishColorVariance.a_ * Random(-1.0f, 1.0f);
-
-    // ATOMIC END
-
+    particle.color_ = effect_->GetStartColor() + effect_->GetStartColorVariance() * Random(-1.0f, 1.0f);
+    Color endColor = effect_->GetFinishColor() + effect_->GetFinishColorVariance() * Random(-1.0f, 1.0f);
     particle.colorDelta_ = (endColor - particle.color_) * invLifespan;
     particle.colorDelta_ = (endColor - particle.color_) * invLifespan;
 
 
     particle.rotation_ = worldAngle + effect_->GetRotationStart() + effect_->GetRotationStartVariance() * Random(-1.0f, 1.0f);
     particle.rotation_ = worldAngle + effect_->GetRotationStart() + effect_->GetRotationStartVariance() * Random(-1.0f, 1.0f);
@@ -441,8 +425,10 @@ void ParticleEmitter2D::UpdateParticle(Particle2D& particle, float timeStep, con
     float halfSize = particle.size_ * 0.5f;
     float halfSize = particle.size_ * 0.5f;
     boundingBoxMinPoint_.x_ = Min(boundingBoxMinPoint_.x_, particle.position_.x_ - halfSize);
     boundingBoxMinPoint_.x_ = Min(boundingBoxMinPoint_.x_, particle.position_.x_ - halfSize);
     boundingBoxMinPoint_.y_ = Min(boundingBoxMinPoint_.y_, particle.position_.y_ - halfSize);
     boundingBoxMinPoint_.y_ = Min(boundingBoxMinPoint_.y_, particle.position_.y_ - halfSize);
+    boundingBoxMinPoint_.z_ = Min(boundingBoxMinPoint_.z_, particle.position_.z_);
     boundingBoxMaxPoint_.x_ = Max(boundingBoxMaxPoint_.x_, particle.position_.x_ + halfSize);
     boundingBoxMaxPoint_.x_ = Max(boundingBoxMaxPoint_.x_, particle.position_.x_ + halfSize);
     boundingBoxMaxPoint_.y_ = Max(boundingBoxMaxPoint_.y_, particle.position_.y_ + halfSize);
     boundingBoxMaxPoint_.y_ = Max(boundingBoxMaxPoint_.y_, particle.position_.y_ + halfSize);
+    boundingBoxMaxPoint_.z_ = Max(boundingBoxMaxPoint_.z_, particle.position_.z_);
 }
 }
 
 
 }
 }

+ 8 - 8
Source/Atomic/Atomic2D/ParticleEmitter2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,9 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Drawable2D.h"
+#include "../Urho2D/Drawable2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class ParticleEffect2D;
 class ParticleEffect2D;
@@ -37,7 +37,7 @@ struct Particle2D
     float timeToLive_;
     float timeToLive_;
 
 
     /// Position.
     /// Position.
-    Vector2 position_;
+    Vector3 position_;
     /// Size.
     /// Size.
     float size_;
     float size_;
     /// Size delta.
     /// Size delta.
@@ -73,9 +73,9 @@ struct Particle2D
 };
 };
 
 
 /// 2D particle emitter component.
 /// 2D particle emitter component.
-class ATOMIC_API ParticleEmitter2D : public Drawable2D
+class URHO3D_API ParticleEmitter2D : public Drawable2D
 {
 {
-    OBJECT(ParticleEmitter2D);
+    URHO3D_OBJECT(ParticleEmitter2D, Drawable2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -143,8 +143,8 @@ private:
     SharedPtr<Sprite2D> sprite_;
     SharedPtr<Sprite2D> sprite_;
     /// Blend mode.
     /// Blend mode.
     BlendMode blendMode_;
     BlendMode blendMode_;
-    /// Num particles.
-    int numParticles_;
+    /// Nummber of particles.
+    unsigned numParticles_;
     /// Emission time.
     /// Emission time.
     float emissionTime_;
     float emissionTime_;
     /// Emit particle time
     /// Emit particle time

+ 19 - 27
Source/Atomic/Atomic2D/PhysicsEvents2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,41 +24,33 @@
 
 
 #include "../Core/Object.h"
 #include "../Core/Object.h"
 
 
-namespace Atomic
-{
+// For prestep / poststep events, which are the same for 2D and 3D physics. The events themselves don't depend
+// on whether 3D physics support or Bullet has been compiled in.
+#include "../Physics/PhysicsEvents.h"
 
 
-/// Physics world is about to be stepped.
-EVENT(E_PHYSICSPRESTEP2D, PhysicsPreStep2D)
+namespace Urho3D
 {
 {
-    PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
-    PARAM(P_TIMESTEP, TimeStep);            // float
-}
-
-/// Physics world has been stepped.
-EVENT(E_PHYSICSPOSTSTEP2D, PhysicsPostStep2D)
-{
-    PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
-    PARAM(P_TIMESTEP, TimeStep);            // float
-}
 
 
 /// Physics begin contact.
 /// Physics begin contact.
-EVENT(E_PHYSICSBEGINCONTACT2D, PhysicsBeginContact2D)
+URHO3D_EVENT(E_PHYSICSBEGINCONTACT2D, PhysicsBeginContact2D)
 {
 {
-    PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
-    PARAM(P_BODYA, BodyA);                  // RigidBody2D pointer
-    PARAM(P_BODYB, BodyB);                  // RigidBody2D pointer
-    PARAM(P_NODEA, NodeA);                  // Node pointer
-    PARAM(P_NODEB, NodeB);                  // Node pointer
+    URHO3D_PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
+    URHO3D_PARAM(P_BODYA, BodyA);                  // RigidBody2D pointer
+    URHO3D_PARAM(P_BODYB, BodyB);                  // RigidBody2D pointer
+    URHO3D_PARAM(P_NODEA, NodeA);                  // Node pointer
+    URHO3D_PARAM(P_NODEB, NodeB);                  // Node pointer
+    URHO3D_PARAM(P_CONTACT, Contact);              // b2Contact pointer
 }
 }
 
 
 /// Physics end contact.
 /// Physics end contact.
-EVENT(E_PHYSICSENDCONTACT2D, PhysicsEndContact2D)
+URHO3D_EVENT(E_PHYSICSENDCONTACT2D, PhysicsEndContact2D)
 {
 {
-    PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
-    PARAM(P_BODYA, BodyA);                  // RigidBody2D pointer
-    PARAM(P_BODYB, BodyB);                  // RigidBody2D pointer
-    PARAM(P_NODEA, NodeA);                  // Node pointer
-    PARAM(P_NODEB, NodeB);                  // Node pointer
+    URHO3D_PARAM(P_WORLD, World);                  // PhysicsWorld2D pointer
+    URHO3D_PARAM(P_BODYA, BodyA);                  // RigidBody2D pointer
+    URHO3D_PARAM(P_BODYB, BodyB);                  // RigidBody2D pointer
+    URHO3D_PARAM(P_NODEA, NodeA);                  // Node pointer
+    URHO3D_PARAM(P_NODEB, NodeB);                  // Node pointer
+    URHO3D_PARAM(P_CONTACT, Contact);              // b2Contact pointer
 }
 }
 
 
 }
 }

+ 2 - 2
Source/Atomic/Atomic2D/PhysicsUtils2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -28,7 +28,7 @@
 
 
 #include <Box2D/Box2D.h>
 #include <Box2D/Box2D.h>
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 inline Color ToColor(const b2Color& color)
 inline Color ToColor(const b2Color& color)

+ 88 - 40
Source/Atomic/Atomic2D/PhysicsWorld2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -30,14 +30,14 @@
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
 #include "../Scene/SceneEvents.h"
 #include "../Scene/SceneEvents.h"
-#include "../Atomic2D/PhysicsEvents2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/PhysicsWorld2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/PhysicsEvents2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/PhysicsWorld2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const char* SUBSYSTEM_CATEGORY;
 extern const char* SUBSYSTEM_CATEGORY;
@@ -52,8 +52,9 @@ PhysicsWorld2D::PhysicsWorld2D(Context* context) :
     velocityIterations_(DEFAULT_VELOCITY_ITERATIONS),
     velocityIterations_(DEFAULT_VELOCITY_ITERATIONS),
     positionIterations_(DEFAULT_POSITION_ITERATIONS),
     positionIterations_(DEFAULT_POSITION_ITERATIONS),
     debugRenderer_(0),
     debugRenderer_(0),
-    physicsSteping_(false),
-    applyingTransforms_(false)
+    physicsStepping_(false),
+    applyingTransforms_(false),
+    updateEnabled_(true)
 {
 {
     // Set default debug draw flags
     // Set default debug draw flags
     m_drawFlags = e_shapeBit;
     m_drawFlags = e_shapeBit;
@@ -65,10 +66,8 @@ PhysicsWorld2D::PhysicsWorld2D(Context* context) :
     // Set debug draw
     // Set debug draw
     world_->SetDebugDraw(this);
     world_->SetDebugDraw(this);
 
 
-    // BEGIN ATOMIC: These should be false, as per the attribute defaults
-    world_->SetContinuousPhysics(false);
-    world_->SetSubStepping(false);
-    // END ATOMIC
+    world_->SetContinuousPhysics(true);
+    world_->SetSubStepping(true);
 }
 }
 
 
 PhysicsWorld2D::~PhysicsWorld2D()
 PhysicsWorld2D::~PhysicsWorld2D()
@@ -85,20 +84,20 @@ void PhysicsWorld2D::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<PhysicsWorld2D>(SUBSYSTEM_CATEGORY);
     context->RegisterFactory<PhysicsWorld2D>(SUBSYSTEM_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Draw Shape", GetDrawShape, SetDrawShape, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Draw Joint", GetDrawJoint, SetDrawJoint, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Draw Aabb", GetDrawAabb, SetDrawAabb, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Draw Pair", GetDrawPair, SetDrawPair, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Draw CenterOfMass", GetDrawCenterOfMass, SetDrawCenterOfMass, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Allow Sleeping", GetAllowSleeping, SetAllowSleeping, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Warm Starting", GetWarmStarting, SetWarmStarting, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Continuous Physics", GetContinuousPhysics, SetContinuousPhysics, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Sub Stepping", GetSubStepping, SetSubStepping, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Gravity", GetGravity, SetGravity, Vector2, DEFAULT_GRAVITY, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Auto Clear Forces", GetAutoClearForces, SetAutoClearForces, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Velocity Iterations", GetVelocityIterations, SetVelocityIterations, int, DEFAULT_VELOCITY_ITERATIONS,
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw Shape", GetDrawShape, SetDrawShape, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw Joint", GetDrawJoint, SetDrawJoint, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw Aabb", GetDrawAabb, SetDrawAabb, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw Pair", GetDrawPair, SetDrawPair, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw CenterOfMass", GetDrawCenterOfMass, SetDrawCenterOfMass, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Allow Sleeping", GetAllowSleeping, SetAllowSleeping, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Warm Starting", GetWarmStarting, SetWarmStarting, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Continuous Physics", GetContinuousPhysics, SetContinuousPhysics, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Sub Stepping", GetSubStepping, SetSubStepping, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Gravity", GetGravity, SetGravity, Vector2, DEFAULT_GRAVITY, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Auto Clear Forces", GetAutoClearForces, SetAutoClearForces, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Velocity Iterations", GetVelocityIterations, SetVelocityIterations, int, DEFAULT_VELOCITY_ITERATIONS,
         AM_DEFAULT);
         AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Position Iterations", GetPositionIterations, SetPositionIterations, int, DEFAULT_POSITION_ITERATIONS,
+    URHO3D_ACCESSOR_ATTRIBUTE("Position Iterations", GetPositionIterations, SetPositionIterations, int, DEFAULT_POSITION_ITERATIONS,
         AM_DEFAULT);
         AM_DEFAULT);
 }
 }
 
 
@@ -106,7 +105,7 @@ void PhysicsWorld2D::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 {
 {
     if (debug)
     if (debug)
     {
     {
-        PROFILE(Physics2DDrawDebug);
+        URHO3D_PROFILE(Physics2DDrawDebug);
 
 
         debugRenderer_ = debug;
         debugRenderer_ = debug;
         debugDepthTest_ = depthTest;
         debugDepthTest_ = depthTest;
@@ -117,8 +116,8 @@ void PhysicsWorld2D::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 
 
 void PhysicsWorld2D::BeginContact(b2Contact* contact)
 void PhysicsWorld2D::BeginContact(b2Contact* contact)
 {
 {
-    // Only handle contact event when physics steping
-    if (!physicsSteping_)
+    // Only handle contact event while stepping the physics simulation
+    if (!physicsStepping_)
         return;
         return;
 
 
     b2Fixture* fixtureA = contact->GetFixtureA();
     b2Fixture* fixtureA = contact->GetFixtureA();
@@ -131,8 +130,7 @@ void PhysicsWorld2D::BeginContact(b2Contact* contact)
 
 
 void PhysicsWorld2D::EndContact(b2Contact* contact)
 void PhysicsWorld2D::EndContact(b2Contact* contact)
 {
 {
-    // Only handle contact event when physics steping
-    if (!physicsSteping_)
+    if (!physicsStepping_)
         return;
         return;
 
 
     b2Fixture* fixtureA = contact->GetFixtureA();
     b2Fixture* fixtureA = contact->GetFixtureA();
@@ -228,25 +226,58 @@ void PhysicsWorld2D::DrawTransform(const b2Transform& xf)
 
 
 void PhysicsWorld2D::Update(float timeStep)
 void PhysicsWorld2D::Update(float timeStep)
 {
 {
-    using namespace PhysicsPreStep2D;
+    URHO3D_PROFILE(UpdatePhysics2D);
+
+    using namespace PhysicsPreStep;
 
 
     VariantMap& eventData = GetEventDataMap();
     VariantMap& eventData = GetEventDataMap();
     eventData[P_WORLD] = this;
     eventData[P_WORLD] = this;
     eventData[P_TIMESTEP] = timeStep;
     eventData[P_TIMESTEP] = timeStep;
-    SendEvent(E_PHYSICSPRESTEP2D, eventData);
+    SendEvent(E_PHYSICSPRESTEP, eventData);
 
 
-    physicsSteping_ = true;
+    physicsStepping_ = true;
     world_->Step(timeStep, velocityIterations_, positionIterations_);
     world_->Step(timeStep, velocityIterations_, positionIterations_);
-    physicsSteping_ = false;
+    physicsStepping_ = false;
 
 
-    for (unsigned i = 0; i < rigidBodies_.Size(); ++i)
-        rigidBodies_[i]->ApplyWorldTransform();
+    // Apply world transforms. Unparented transforms first
+    for (unsigned i = 0; i < rigidBodies_.Size();)
+    {
+        if (rigidBodies_[i])
+        {
+            rigidBodies_[i]->ApplyWorldTransform();
+            ++i;
+        }
+        else
+        {
+            // Erase possible stale weak pointer
+            rigidBodies_.Erase(i);
+        }
+    }
+
+    // Apply delayed (parented) world transforms now, if any
+    while (!delayedWorldTransforms_.Empty())
+    {
+        for (HashMap<RigidBody2D*, DelayedWorldTransform2D>::Iterator i = delayedWorldTransforms_.Begin();
+            i != delayedWorldTransforms_.End();)
+        {
+            const DelayedWorldTransform2D& transform = i->second_;
+
+            // If parent's transform has already been assigned, can proceed
+            if (!delayedWorldTransforms_.Contains(transform.parentRigidBody_))
+            {
+                transform.rigidBody_->ApplyWorldTransform(transform.worldPosition_, transform.worldRotation_);
+                i = delayedWorldTransforms_.Erase(i);
+            }
+            else
+                ++i;
+        }
+    }
 
 
     SendBeginContactEvents();
     SendBeginContactEvents();
     SendEndContactEvents();
     SendEndContactEvents();
 
 
-    using namespace PhysicsPostStep2D;
-    SendEvent(E_PHYSICSPOSTSTEP2D, eventData);
+    using namespace PhysicsPostStep;
+    SendEvent(E_PHYSICSPOSTSTEP, eventData);
 }
 }
 
 
 void PhysicsWorld2D::DrawDebugGeometry()
 void PhysicsWorld2D::DrawDebugGeometry()
@@ -256,6 +287,11 @@ void PhysicsWorld2D::DrawDebugGeometry()
         DrawDebugGeometry(debug, false);
         DrawDebugGeometry(debug, false);
 }
 }
 
 
+void PhysicsWorld2D::SetUpdateEnabled(bool enable)
+{
+    updateEnabled_ = enable;
+}
+
 void PhysicsWorld2D::SetDrawShape(bool drawShape)
 void PhysicsWorld2D::SetDrawShape(bool drawShape)
 {
 {
     if (drawShape)
     if (drawShape)
@@ -360,6 +396,11 @@ void PhysicsWorld2D::RemoveRigidBody(RigidBody2D* rigidBody)
     rigidBodies_.Remove(rigidBodyPtr);
     rigidBodies_.Remove(rigidBodyPtr);
 }
 }
 
 
+void PhysicsWorld2D::AddDelayedWorldTransform(const DelayedWorldTransform2D& transform)
+{
+    delayedWorldTransforms_[transform.rigidBody_] = transform;
+}
+
 // Ray cast call back class.
 // Ray cast call back class.
 class RayCastCallback : public b2RayCastCallback
 class RayCastCallback : public b2RayCastCallback
 {
 {
@@ -613,13 +654,16 @@ void PhysicsWorld2D::OnSceneSet(Scene* scene)
 {
 {
     // Subscribe to the scene subsystem update, which will trigger the physics simulation step
     // Subscribe to the scene subsystem update, which will trigger the physics simulation step
     if (scene)
     if (scene)
-        SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, HANDLER(PhysicsWorld2D, HandleSceneSubsystemUpdate));
+        SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, URHO3D_HANDLER(PhysicsWorld2D, HandleSceneSubsystemUpdate));
     else
     else
         UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
         UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
 }
 }
 
 
 void PhysicsWorld2D::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
 void PhysicsWorld2D::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
+    if (!updateEnabled_)
+        return;
+
     using namespace SceneSubsystemUpdate;
     using namespace SceneSubsystemUpdate;
     Update(eventData[P_TIMESTEP].GetFloat());
     Update(eventData[P_TIMESTEP].GetFloat());
 }
 }
@@ -640,6 +684,7 @@ void PhysicsWorld2D::SendBeginContactEvents()
         eventData[P_BODYB] = contactInfo.bodyB_.Get();
         eventData[P_BODYB] = contactInfo.bodyB_.Get();
         eventData[P_NODEA] = contactInfo.nodeA_.Get();
         eventData[P_NODEA] = contactInfo.nodeA_.Get();
         eventData[P_NODEB] = contactInfo.nodeB_.Get();
         eventData[P_NODEB] = contactInfo.nodeB_.Get();
+        eventData[P_CONTACT] = (void*)contactInfo.contact_;
 
 
         SendEvent(E_PHYSICSBEGINCONTACT2D, eventData);
         SendEvent(E_PHYSICSBEGINCONTACT2D, eventData);
     }
     }
@@ -663,6 +708,7 @@ void PhysicsWorld2D::SendEndContactEvents()
         eventData[P_BODYB] = contactInfo.bodyB_.Get();
         eventData[P_BODYB] = contactInfo.bodyB_.Get();
         eventData[P_NODEA] = contactInfo.nodeA_.Get();
         eventData[P_NODEA] = contactInfo.nodeA_.Get();
         eventData[P_NODEB] = contactInfo.nodeB_.Get();
         eventData[P_NODEB] = contactInfo.nodeB_.Get();
+        eventData[P_CONTACT] = (void*)contactInfo.contact_;
 
 
         SendEvent(E_PHYSICSENDCONTACT2D, eventData);
         SendEvent(E_PHYSICSENDCONTACT2D, eventData);
     }
     }
@@ -682,13 +728,15 @@ PhysicsWorld2D::ContactInfo::ContactInfo(b2Contact* contact)
     bodyB_ = (RigidBody2D*)(fixtureB->GetBody()->GetUserData());
     bodyB_ = (RigidBody2D*)(fixtureB->GetBody()->GetUserData());
     nodeA_ = bodyA_->GetNode();
     nodeA_ = bodyA_->GetNode();
     nodeB_ = bodyB_->GetNode();
     nodeB_ = bodyB_->GetNode();
+    contact_ = contact;
 }
 }
 
 
 PhysicsWorld2D::ContactInfo::ContactInfo(const ContactInfo& other) :
 PhysicsWorld2D::ContactInfo::ContactInfo(const ContactInfo& other) :
     bodyA_(other.bodyA_),
     bodyA_(other.bodyA_),
     bodyB_(other.bodyB_),
     bodyB_(other.bodyB_),
     nodeA_(other.nodeA_),
     nodeA_(other.nodeA_),
-    nodeB_(other.nodeB_)
+    nodeB_(other.nodeB_),
+    contact_(other.contact_)
 {
 {
 }
 }
 
 

+ 35 - 9
Source/Atomic/Atomic2D/PhysicsWorld2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,14 +26,14 @@
 
 
 #include <Box2D/Box2D.h>
 #include <Box2D/Box2D.h>
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class Camera;
 class Camera;
 class RigidBody2D;
 class RigidBody2D;
 
 
 /// 2D Physics raycast hit.
 /// 2D Physics raycast hit.
-struct ATOMIC_API PhysicsRaycastResult2D
+struct URHO3D_API PhysicsRaycastResult2D
 {
 {
     /// Construct with defaults.
     /// Construct with defaults.
     PhysicsRaycastResult2D() :
     PhysicsRaycastResult2D() :
@@ -57,10 +57,23 @@ struct ATOMIC_API PhysicsRaycastResult2D
     RigidBody2D* body_;
     RigidBody2D* body_;
 };
 };
 
 
+/// Delayed world transform assignment for parented 2D rigidbodies.
+struct DelayedWorldTransform2D
+{
+    /// Rigid body.
+    RigidBody2D* rigidBody_;
+    /// Parent rigid body.
+    RigidBody2D* parentRigidBody_;
+    /// New world position.
+    Vector3 worldPosition_;
+    /// New world rotation.
+    Quaternion worldRotation_;
+};
+
 /// 2D physics simulation world component. Should be added only to the root scene node.
 /// 2D physics simulation world component. Should be added only to the root scene node.
-class ATOMIC_API PhysicsWorld2D : public Component, public b2ContactListener, public b2Draw
+class URHO3D_API PhysicsWorld2D : public Component, public b2ContactListener, public b2Draw
 {
 {
-    OBJECT(PhysicsWorld2D);
+    URHO3D_OBJECT(PhysicsWorld2D, Component);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -73,13 +86,13 @@ public:
     /// Visualize the component as debug geometry.
     /// Visualize the component as debug geometry.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
 
 
-    // Implement b2ContactListener.
+    // Implement b2ContactListener
     /// Called when two fixtures begin to touch.
     /// Called when two fixtures begin to touch.
     virtual void BeginContact(b2Contact* contact);
     virtual void BeginContact(b2Contact* contact);
     /// Called when two fixtures cease to touch.
     /// Called when two fixtures cease to touch.
     virtual void EndContact(b2Contact* contact);
     virtual void EndContact(b2Contact* contact);
 
 
-    // Implement b2Draw.
+    // Implement b2Draw
     /// Draw a closed polygon provided in CCW order.
     /// Draw a closed polygon provided in CCW order.
     virtual void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
     virtual void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
     /// Draw a solid closed polygon provided in CCW order.
     /// Draw a solid closed polygon provided in CCW order.
@@ -97,6 +110,8 @@ public:
     void Update(float timeStep);
     void Update(float timeStep);
     /// Add debug geometry to the debug renderer.
     /// Add debug geometry to the debug renderer.
     void DrawDebugGeometry();
     void DrawDebugGeometry();
+    /// Enable or disable automatic physics simulation during scene update. Enabled by default.
+    void SetUpdateEnabled(bool enable);
     /// Set draw shape.
     /// Set draw shape.
     void SetDrawShape(bool drawShape);
     void SetDrawShape(bool drawShape);
     /// Set draw joint.
     /// Set draw joint.
@@ -127,6 +142,8 @@ public:
     void AddRigidBody(RigidBody2D* rigidBody);
     void AddRigidBody(RigidBody2D* rigidBody);
     /// Remove rigid body.
     /// Remove rigid body.
     void RemoveRigidBody(RigidBody2D* rigidBody);
     void RemoveRigidBody(RigidBody2D* rigidBody);
+    /// Add a delayed world transform assignment. Called by RigidBody2D.
+    void AddDelayedWorldTransform(const DelayedWorldTransform2D& transform);
 
 
     /// Perform a physics world raycast and return all hits.
     /// Perform a physics world raycast and return all hits.
     void Raycast(PODVector<PhysicsRaycastResult2D>& results, const Vector2& startPoint, const Vector2& endPoint,
     void Raycast(PODVector<PhysicsRaycastResult2D>& results, const Vector2& startPoint, const Vector2& endPoint,
@@ -141,6 +158,9 @@ public:
     /// Return rigid bodies by a box query.
     /// Return rigid bodies by a box query.
     void GetRigidBodies(PODVector<RigidBody2D*>& result, const Rect& aabb, unsigned collisionMask = M_MAX_UNSIGNED);
     void GetRigidBodies(PODVector<RigidBody2D*>& result, const Rect& aabb, unsigned collisionMask = M_MAX_UNSIGNED);
 
 
+    /// Return whether physics world will automatically simulate during scene update.
+    bool IsUpdateEnabled() const { return updateEnabled_; }
+
     /// Return draw shape.
     /// Return draw shape.
     bool GetDrawShape() const { return (m_drawFlags & e_shapeBit) != 0; }
     bool GetDrawShape() const { return (m_drawFlags & e_shapeBit) != 0; }
 
 
@@ -213,12 +233,16 @@ private:
     /// Debug draw depth test mode.
     /// Debug draw depth test mode.
     bool debugDepthTest_;
     bool debugDepthTest_;
 
 
-    /// Physics steping.
-    bool physicsSteping_;
+    /// Automatic simulation update enabled flag.
+    bool updateEnabled_;
+    /// Whether is currently stepping the world. Used internally.
+    bool physicsStepping_;
     /// Applying transforms.
     /// Applying transforms.
     bool applyingTransforms_;
     bool applyingTransforms_;
     /// Rigid bodies.
     /// Rigid bodies.
     Vector<WeakPtr<RigidBody2D> > rigidBodies_;
     Vector<WeakPtr<RigidBody2D> > rigidBodies_;
+    /// Delayed (parented) world transform assignments.
+    HashMap<RigidBody2D*, DelayedWorldTransform2D> delayedWorldTransforms_;
 
 
     /// Contact info.
     /// Contact info.
     struct ContactInfo
     struct ContactInfo
@@ -238,6 +262,8 @@ private:
         SharedPtr<Node> nodeA_;
         SharedPtr<Node> nodeA_;
         /// Node B.
         /// Node B.
         SharedPtr<Node> nodeB_;
         SharedPtr<Node> nodeB_;
+        /// Box2D contact.
+        b2Contact* contact_;
     };
     };
     /// Begin contact infos.
     /// Begin contact infos.
     Vector<ContactInfo> beginContactInfos_;
     Vector<ContactInfo> beginContactInfos_;

+ 68 - 70
Source/Atomic/Atomic2D/Renderer2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -38,12 +38,12 @@
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
-#include "../Atomic2D/Drawable2D.h"
-#include "../Atomic2D/Renderer2D.h"
+#include "../Urho2D/Drawable2D.h"
+#include "../Urho2D/Renderer2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const char* blendModeNames[];
 extern const char* blendModeNames[];
@@ -63,15 +63,14 @@ Renderer2D::Renderer2D(Context* context) :
     Drawable(context, DRAWABLE_GEOMETRY),
     Drawable(context, DRAWABLE_GEOMETRY),
     material_(new Material(context)),
     material_(new Material(context)),
     indexBuffer_(new IndexBuffer(context_)),
     indexBuffer_(new IndexBuffer(context_)),
-    frustum_(0),
-    useTris_(false)
+    viewMask_(DEFAULT_VIEWMASK)
 {
 {
-    material_->SetName("Atomic2D");
+    material_->SetName("Urho2D");
 
 
     Technique* tech = new Technique(context_);
     Technique* tech = new Technique(context_);
     Pass* pass = tech->CreatePass("alpha");
     Pass* pass = tech->CreatePass("alpha");
-    pass->SetVertexShader("Atomic2D");
-    pass->SetPixelShader("Atomic2D");
+    pass->SetVertexShader("Urho2D");
+    pass->SetPixelShader("Urho2D");
     pass->SetDepthWrite(false);
     pass->SetDepthWrite(false);
     cachedTechniques_[BLEND_REPLACE] = tech;
     cachedTechniques_[BLEND_REPLACE] = tech;
 
 
@@ -79,7 +78,7 @@ Renderer2D::Renderer2D(Context* context) :
     material_->SetCullMode(CULL_NONE);
     material_->SetCullMode(CULL_NONE);
 
 
     frame_.frameNumber_ = 0;
     frame_.frameNumber_ = 0;
-    SubscribeToEvent(E_BEGINVIEWUPDATE, HANDLER(Renderer2D, HandleBeginViewUpdate));
+    SubscribeToEvent(E_BEGINVIEWUPDATE, URHO3D_HANDLER(Renderer2D, HandleBeginViewUpdate));
 }
 }
 
 
 Renderer2D::~Renderer2D()
 Renderer2D::~Renderer2D()
@@ -108,7 +107,10 @@ void Renderer2D::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQuery
 {
 {
     unsigned resultSize = results.Size();
     unsigned resultSize = results.Size();
     for (unsigned i = 0; i < drawables_.Size(); ++i)
     for (unsigned i = 0; i < drawables_.Size(); ++i)
-        drawables_[i]->ProcessRayQuery(query, results);
+    {
+        if (drawables_[i]->GetViewMask() & query.viewMask_)
+            drawables_[i]->ProcessRayQuery(query, results);
+    }
 
 
     if (results.Size() != resultSize)
     if (results.Size() != resultSize)
         Sort(results.Begin() + resultSize, results.End(), CompareRayQueryResults);
         Sort(results.Begin() + resultSize, results.End(), CompareRayQueryResults);
@@ -132,7 +134,7 @@ void Renderer2D::UpdateGeometry(const FrameInfo& frame)
     for (HashMap<Camera*, ViewBatchInfo2D>::ConstIterator i = viewBatchInfos_.Begin(); i != viewBatchInfos_.End(); ++i)
     for (HashMap<Camera*, ViewBatchInfo2D>::ConstIterator i = viewBatchInfos_.Begin(); i != viewBatchInfos_.End(); ++i)
     {
     {
         if (i->second_.batchUpdatedFrameNumber_ == frame_.frameNumber_)
         if (i->second_.batchUpdatedFrameNumber_ == frame_.frameNumber_)
-            indexCount = (unsigned)Max((int)indexCount, (int)i->second_.indexCount_);
+            indexCount = Max(indexCount, i->second_.indexCount_);
     }
     }
 
 
     // Fill index buffer
     // Fill index buffer
@@ -144,26 +146,20 @@ void Renderer2D::UpdateGeometry(const FrameInfo& frame)
         void* buffer = indexBuffer_->Lock(0, indexCount, true);
         void* buffer = indexBuffer_->Lock(0, indexCount, true);
         if (buffer)
         if (buffer)
         {
         {
-            unsigned quadCount =  useTris_ ? indexCount/3 : indexCount / 6;
+            unsigned quadCount = indexCount / 6;
             if (largeIndices)
             if (largeIndices)
             {
             {
                 unsigned* dest = reinterpret_cast<unsigned*>(buffer);
                 unsigned* dest = reinterpret_cast<unsigned*>(buffer);
-
                 for (unsigned i = 0; i < quadCount; ++i)
                 for (unsigned i = 0; i < quadCount; ++i)
                 {
                 {
-                    unsigned base = i * (useTris_ ? 3 : 4);
+                    unsigned base = i * 4;
                     dest[0] = base;
                     dest[0] = base;
                     dest[1] = base + 1;
                     dest[1] = base + 1;
                     dest[2] = base + 2;
                     dest[2] = base + 2;
-                    if (!useTris_)
-                    {
-                        dest[3] = base;
-                        dest[4] = base + 2;
-                        dest[5] = base + 3;
-                        dest += 6;
-                    }
-                    else
-                        dest += 3;
+                    dest[3] = base;
+                    dest[4] = base + 2;
+                    dest[5] = base + 3;
+                    dest += 6;
                 }
                 }
             }
             }
             else
             else
@@ -171,19 +167,14 @@ void Renderer2D::UpdateGeometry(const FrameInfo& frame)
                 unsigned short* dest = reinterpret_cast<unsigned short*>(buffer);
                 unsigned short* dest = reinterpret_cast<unsigned short*>(buffer);
                 for (unsigned i = 0; i < quadCount; ++i)
                 for (unsigned i = 0; i < quadCount; ++i)
                 {
                 {
-                    unsigned base = i * (useTris_ ? 3 : 4);
+                    unsigned base = i * 4;
                     dest[0] = (unsigned short)(base);
                     dest[0] = (unsigned short)(base);
                     dest[1] = (unsigned short)(base + 1);
                     dest[1] = (unsigned short)(base + 1);
                     dest[2] = (unsigned short)(base + 2);
                     dest[2] = (unsigned short)(base + 2);
-                    if (!useTris_)
-                    {
-                        dest[3] = (unsigned short)(base);
-                        dest[4] = (unsigned short)(base + 2);
-                        dest[5] = (unsigned short)(base + 3);
-                        dest += 6;
-                    }
-                    else
-                        dest += 3;
+                    dest[3] = (unsigned short)(base);
+                    dest[4] = (unsigned short)(base + 2);
+                    dest[5] = (unsigned short)(base + 3);
+                    dest += 6;
                 }
                 }
             }
             }
 
 
@@ -191,7 +182,7 @@ void Renderer2D::UpdateGeometry(const FrameInfo& frame)
         }
         }
         else
         else
         {
         {
-            LOGERROR("Failed to lock index buffer");
+            URHO3D_LOGERROR("Failed to lock index buffer");
             return;
             return;
         }
         }
     }
     }
@@ -223,7 +214,7 @@ void Renderer2D::UpdateGeometry(const FrameInfo& frame)
                 vertexBuffer->Unlock();
                 vertexBuffer->Unlock();
             }
             }
             else
             else
-                LOGERROR("Failed to lock vertex buffer");
+                URHO3D_LOGERROR("Failed to lock vertex buffer");
         }
         }
 
 
         viewBatchInfo.vertexBufferUpdateFrameNumber_ = frame_.frameNumber_;
         viewBatchInfo.vertexBufferUpdateFrameNumber_ = frame_.frameNumber_;
@@ -277,11 +268,11 @@ Material* Renderer2D::GetMaterial(Texture2D* texture, BlendMode blendMode)
 
 
 bool Renderer2D::CheckVisibility(Drawable2D* drawable) const
 bool Renderer2D::CheckVisibility(Drawable2D* drawable) const
 {
 {
-    const BoundingBox& box = drawable->GetWorldBoundingBox();
-    if (frustum_)
-        return frustum_->IsInsideFast(box) != OUTSIDE;
+    if ((viewMask_ & drawable->GetViewMask()) == 0)
+        return false;
 
 
-    return frustumBoundingBox_.IsInsideFast(box) != OUTSIDE;
+    const BoundingBox& box = drawable->GetWorldBoundingBox();
+    return frustum_.IsInsideFast(box) != OUTSIDE;
 }
 }
 
 
 void Renderer2D::OnWorldBoundingBoxUpdate()
 void Renderer2D::OnWorldBoundingBoxUpdate()
@@ -300,8 +291,8 @@ SharedPtr<Material> Renderer2D::CreateMaterial(Texture2D* texture, BlendMode ble
     {
     {
         SharedPtr<Technique> tech(new Technique(context_));
         SharedPtr<Technique> tech(new Technique(context_));
         Pass* pass = tech->CreatePass("alpha");
         Pass* pass = tech->CreatePass("alpha");
-        pass->SetVertexShader("Atomic2D");
-        pass->SetPixelShader("Atomic2D");
+        pass->SetVertexShader("Urho2D");
+        pass->SetPixelShader("Urho2D");
         pass->SetDepthWrite(false);
         pass->SetDepthWrite(false);
         pass->SetBlendMode(blendMode);
         pass->SetBlendMode(blendMode);
         techIt = cachedTechniques_.Insert(MakePair((int)blendMode, tech));
         techIt = cachedTechniques_.Insert(MakePair((int)blendMode, tech));
@@ -338,20 +329,15 @@ void Renderer2D::HandleBeginViewUpdate(StringHash eventType, VariantMap& eventDa
 
 
     frame_ = static_cast<View*>(eventData[P_VIEW].GetPtr())->GetFrameInfo();
     frame_ = static_cast<View*>(eventData[P_VIEW].GetPtr())->GetFrameInfo();
 
 
-    PROFILE(UpdateRenderer2D);
+    URHO3D_PROFILE(UpdateRenderer2D);
 
 
     Camera* camera = static_cast<Camera*>(eventData[P_CAMERA].GetPtr());
     Camera* camera = static_cast<Camera*>(eventData[P_CAMERA].GetPtr());
-    frustum_ = &camera->GetFrustum();
-    if (camera->IsOrthographic() && camera->GetNode()->GetWorldDirection() == Vector3::FORWARD)
-    {
-        // Define bounding box with min and max points
-        frustumBoundingBox_.Define(frustum_->vertices_[2], frustum_->vertices_[4]);
-        frustum_ = 0;
-    }
+    frustum_ = camera->GetFrustum();
+    viewMask_ = camera->GetViewMask();
 
 
     // Check visibility
     // Check visibility
     {
     {
-        PROFILE(CheckDrawableVisibility);
+        URHO3D_PROFILE(CheckDrawableVisibility);
 
 
         WorkQueue* queue = GetSubsystem<WorkQueue>();
         WorkQueue* queue = GetSubsystem<WorkQueue>();
         int numWorkItems = queue->GetNumThreads() + 1; // Worker threads + main thread
         int numWorkItems = queue->GetNumThreads() + 1; // Worker threads + main thread
@@ -394,6 +380,7 @@ void Renderer2D::HandleBeginViewUpdate(StringHash eventType, VariantMap& eventDa
     batches_.Resize(viewBatchInfo.batchCount_);
     batches_.Resize(viewBatchInfo.batchCount_);
     for (unsigned i = 0; i < viewBatchInfo.batchCount_; ++i)
     for (unsigned i = 0; i < viewBatchInfo.batchCount_; ++i)
     {
     {
+        batches_[i].distance_ = viewBatchInfo.distances_[i];
         batches_[i].material_ = viewBatchInfo.materials_[i];
         batches_[i].material_ = viewBatchInfo.materials_[i];
         batches_[i].geometry_ = viewBatchInfo.geometries_[i];
         batches_[i].geometry_ = viewBatchInfo.geometries_[i];
     }
     }
@@ -419,6 +406,9 @@ void Renderer2D::GetDrawables(PODVector<Drawable2D*>& dest, Node* node)
 
 
 static inline bool CompareSourceBatch2Ds(const SourceBatch2D* lhs, const SourceBatch2D* rhs)
 static inline bool CompareSourceBatch2Ds(const SourceBatch2D* lhs, const SourceBatch2D* rhs)
 {
 {
+    if (lhs->distance_ != rhs->distance_)
+        return lhs->distance_ > rhs->distance_;
+
     if (lhs->drawOrder_ != rhs->drawOrder_)
     if (lhs->drawOrder_ != rhs->drawOrder_)
         return lhs->drawOrder_ < rhs->drawOrder_;
         return lhs->drawOrder_ < rhs->drawOrder_;
 
 
@@ -434,8 +424,8 @@ void Renderer2D::UpdateViewBatchInfo(ViewBatchInfo2D& viewBatchInfo, Camera* cam
     if (viewBatchInfo.batchUpdatedFrameNumber_ == frame_.frameNumber_)
     if (viewBatchInfo.batchUpdatedFrameNumber_ == frame_.frameNumber_)
         return;
         return;
 
 
-    PODVector<const SourceBatch2D*>& soruceBatches = viewBatchInfo.sourceBatches_;
-    soruceBatches.Clear();
+    PODVector<const SourceBatch2D*>& sourceBatches = viewBatchInfo.sourceBatches_;
+    sourceBatches.Clear();
     for (unsigned d = 0; d < drawables_.Size(); ++d)
     for (unsigned d = 0; d < drawables_.Size(); ++d)
     {
     {
         if (!drawables_[d]->IsInView(camera))
         if (!drawables_[d]->IsInView(camera))
@@ -445,11 +435,18 @@ void Renderer2D::UpdateViewBatchInfo(ViewBatchInfo2D& viewBatchInfo, Camera* cam
         for (unsigned b = 0; b < batches.Size(); ++b)
         for (unsigned b = 0; b < batches.Size(); ++b)
         {
         {
             if (batches[b].material_ && !batches[b].vertices_.Empty())
             if (batches[b].material_ && !batches[b].vertices_.Empty())
-                soruceBatches.Push(&batches[b]);
+                sourceBatches.Push(&batches[b]);
         }
         }
     }
     }
 
 
-    Sort(soruceBatches.Begin(), soruceBatches.End(), CompareSourceBatch2Ds);
+    for (unsigned i = 0; i < sourceBatches.Size(); ++i)
+    {
+        const SourceBatch2D* sourceBatch = sourceBatches[i];
+        Vector3 worldPos = sourceBatch->owner_->GetNode()->GetWorldPosition();
+        sourceBatch->distance_ = camera->GetDistance(worldPos);
+    }
+    
+    Sort(sourceBatches.Begin(), sourceBatches.End(), CompareSourceBatch2Ds);
 
 
     viewBatchInfo.batchCount_ = 0;
     viewBatchInfo.batchCount_ = 0;
     Material* currMaterial = 0;
     Material* currMaterial = 0;
@@ -457,52 +454,53 @@ void Renderer2D::UpdateViewBatchInfo(ViewBatchInfo2D& viewBatchInfo, Camera* cam
     unsigned iCount = 0;
     unsigned iCount = 0;
     unsigned vStart = 0;
     unsigned vStart = 0;
     unsigned vCount = 0;
     unsigned vCount = 0;
+    float distance = M_INFINITY;
 
 
-    for (unsigned b = 0; b < soruceBatches.Size(); ++b)
+    for (unsigned b = 0; b < sourceBatches.Size(); ++b)
     {
     {
-        Material* material = soruceBatches[b]->material_;
-        const Vector<Vertex2D>& vertices = soruceBatches[b]->vertices_;
+        distance = Min(distance, sourceBatches[b]->distance_);
+        Material* material = sourceBatches[b]->material_;
+        const Vector<Vertex2D>& vertices = sourceBatches[b]->vertices_;
 
 
         // When new material encountered, finish the current batch and start new
         // When new material encountered, finish the current batch and start new
         if (currMaterial != material)
         if (currMaterial != material)
         {
         {
             if (currMaterial)
             if (currMaterial)
             {
             {
-                AddViewBatch(viewBatchInfo, currMaterial, iStart, iCount, vStart, vCount);
+                AddViewBatch(viewBatchInfo, currMaterial, iStart, iCount, vStart, vCount, distance);
                 iStart += iCount;
                 iStart += iCount;
                 iCount = 0;
                 iCount = 0;
                 vStart += vCount;
                 vStart += vCount;
                 vCount = 0;
                 vCount = 0;
+                distance = M_INFINITY;
             }
             }
 
 
             currMaterial = material;
             currMaterial = material;
         }
         }
 
 
-        unsigned indices;
-        if (useTris_)
-            indices = vertices.Size();
-        else
-            indices = vertices.Size() * 6 / 4;
-
-        iCount += indices;
+        iCount += vertices.Size() * 6 / 4;
         vCount += vertices.Size();
         vCount += vertices.Size();
     }
     }
 
 
     // Add the final batch if necessary
     // Add the final batch if necessary
     if (currMaterial && vCount)
     if (currMaterial && vCount)
-        AddViewBatch(viewBatchInfo, currMaterial, iStart, iCount, vStart, vCount);
+        AddViewBatch(viewBatchInfo, currMaterial, iStart, iCount, vStart, vCount,distance);
 
 
     viewBatchInfo.indexCount_ = iStart + iCount;
     viewBatchInfo.indexCount_ = iStart + iCount;
     viewBatchInfo.vertexCount_ = vStart + vCount;
     viewBatchInfo.vertexCount_ = vStart + vCount;
     viewBatchInfo.batchUpdatedFrameNumber_ = frame_.frameNumber_;
     viewBatchInfo.batchUpdatedFrameNumber_ = frame_.frameNumber_;
 }
 }
 
 
-void Renderer2D::AddViewBatch(ViewBatchInfo2D& viewBatchInfo, Material* material, unsigned indexStart, unsigned indexCount,
-    unsigned vertexStart, unsigned vertexCount)
+void Renderer2D::AddViewBatch(ViewBatchInfo2D& viewBatchInfo, Material* material, 
+    unsigned indexStart, unsigned indexCount, unsigned vertexStart, unsigned vertexCount, float distance)
 {
 {
     if (!material || indexCount == 0 || vertexCount == 0)
     if (!material || indexCount == 0 || vertexCount == 0)
         return;
         return;
 
 
+    if (viewBatchInfo.distances_.Size() <= viewBatchInfo.batchCount_)
+        viewBatchInfo.distances_.Resize(viewBatchInfo.batchCount_ + 1);
+    viewBatchInfo.distances_[viewBatchInfo.batchCount_] = distance;
+
     if (viewBatchInfo.materials_.Size() <= viewBatchInfo.batchCount_)
     if (viewBatchInfo.materials_.Size() <= viewBatchInfo.batchCount_)
         viewBatchInfo.materials_.Resize(viewBatchInfo.batchCount_ + 1);
         viewBatchInfo.materials_.Resize(viewBatchInfo.batchCount_ + 1);
     viewBatchInfo.materials_[viewBatchInfo.batchCount_] = material;
     viewBatchInfo.materials_[viewBatchInfo.batchCount_] = material;
@@ -512,7 +510,7 @@ void Renderer2D::AddViewBatch(ViewBatchInfo2D& viewBatchInfo, Material* material
     {
     {
         SharedPtr<Geometry> geometry(new Geometry(context_));
         SharedPtr<Geometry> geometry(new Geometry(context_));
         geometry->SetIndexBuffer(indexBuffer_);
         geometry->SetIndexBuffer(indexBuffer_);
-        geometry->SetVertexBuffer(0, viewBatchInfo.vertexBuffer_, MASK_VERTEX2D);
+        geometry->SetVertexBuffer(0, viewBatchInfo.vertexBuffer_);
 
 
         viewBatchInfo.geometries_.Push(geometry);
         viewBatchInfo.geometries_.Push(geometry);
     }
     }

+ 12 - 16
Source/Atomic/Atomic2D/Renderer2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,8 +23,9 @@
 #pragma once
 #pragma once
 
 
 #include "../Graphics/Drawable.h"
 #include "../Graphics/Drawable.h"
+#include "../Math/Frustum.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class Drawable2D;
 class Drawable2D;
@@ -55,6 +56,8 @@ struct ViewBatchInfo2D
     PODVector<const SourceBatch2D*> sourceBatches_;
     PODVector<const SourceBatch2D*> sourceBatches_;
     /// Batch count;
     /// Batch count;
     unsigned batchCount_;
     unsigned batchCount_;
+    /// Distances.
+    PODVector<float> distances_;
     /// Materials.
     /// Materials.
     Vector<SharedPtr<Material> > materials_;
     Vector<SharedPtr<Material> > materials_;
     /// Geometries.
     /// Geometries.
@@ -62,9 +65,9 @@ struct ViewBatchInfo2D
 };
 };
 
 
 /// 2D renderer component.
 /// 2D renderer component.
-class ATOMIC_API Renderer2D : public Drawable
+class URHO3D_API Renderer2D : public Drawable
 {
 {
-    OBJECT(Renderer2D);
+    URHO3D_OBJECT(Renderer2D, Drawable);
 
 
     friend void CheckDrawableVisibility(const WorkItem* item, unsigned threadIndex);
     friend void CheckDrawableVisibility(const WorkItem* item, unsigned threadIndex);
 
 
@@ -95,10 +98,6 @@ public:
     /// Check visibility.
     /// Check visibility.
     bool CheckVisibility(Drawable2D* drawable) const;
     bool CheckVisibility(Drawable2D* drawable) const;
 
 
-    /// Whether this renderer uses triangles (instead of quads)
-    void SetUseTris(bool useTris) { useTris_ = useTris; }
-    bool GetUseTris() const { return useTris_; }
-
 private:
 private:
     /// Recalculate the world-space bounding box.
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
     virtual void OnWorldBoundingBoxUpdate();
@@ -111,9 +110,8 @@ private:
     /// Update view batch info.
     /// Update view batch info.
     void UpdateViewBatchInfo(ViewBatchInfo2D& viewBatchInfo, Camera* camera);
     void UpdateViewBatchInfo(ViewBatchInfo2D& viewBatchInfo, Camera* camera);
     /// Add view batch.
     /// Add view batch.
-    void AddViewBatch
-        (ViewBatchInfo2D& viewBatchInfo, Material* material, unsigned indexStart, unsigned indexCount, unsigned vertexStart,
-            unsigned vertexCount);
+    void AddViewBatch(ViewBatchInfo2D& viewBatchInfo, Material* material, 
+        unsigned indexStart, unsigned indexCount, unsigned vertexStart, unsigned vertexCount, float distance);
 
 
     /// Index buffer.
     /// Index buffer.
     SharedPtr<IndexBuffer> indexBuffer_;
     SharedPtr<IndexBuffer> indexBuffer_;
@@ -126,15 +124,13 @@ private:
     /// View batch info.
     /// View batch info.
     HashMap<Camera*, ViewBatchInfo2D> viewBatchInfos_;
     HashMap<Camera*, ViewBatchInfo2D> viewBatchInfos_;
     /// Frustum for current frame.
     /// Frustum for current frame.
-    const Frustum* frustum_;
-    /// Frustum bounding box for current frame.
-    BoundingBox frustumBoundingBox_;
+    Frustum frustum_;
+    /// View mask of current camera for visibility checking.
+    unsigned viewMask_;
     /// Cached materials.
     /// Cached materials.
     HashMap<Texture2D*, HashMap<int, SharedPtr<Material> > > cachedMaterials_;
     HashMap<Texture2D*, HashMap<int, SharedPtr<Material> > > cachedMaterials_;
     /// Cached techniques per blend mode.
     /// Cached techniques per blend mode.
     HashMap<int, SharedPtr<Technique> > cachedTechniques_;
     HashMap<int, SharedPtr<Technique> > cachedTechniques_;
-    /// Whether or not the renderer containts tris (default is quads)
-    bool useTris_;
 };
 };
 
 
 }
 }

+ 176 - 99
Source/Atomic/Atomic2D/RigidBody2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,21 +25,20 @@
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
-#include "../Atomic2D/CollisionShape2D.h"
-#include "../Atomic2D/Constraint2D.h"
-#include "../Atomic2D/PhysicsUtils2D.h"
-#include "../Atomic2D/PhysicsWorld2D.h"
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/CollisionShape2D.h"
+#include "../Urho2D/Constraint2D.h"
+#include "../Urho2D/PhysicsUtils2D.h"
+#include "../Urho2D/PhysicsWorld2D.h"
+#include "../Urho2D/RigidBody2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 static const BodyType2D DEFAULT_BODYTYPE = BT_STATIC;
 static const BodyType2D DEFAULT_BODYTYPE = BT_STATIC;
 
 
-// ATOMIC: Make sure these match box2d order
 static const char* bodyTypeNames[] =
 static const char* bodyTypeNames[] =
 {
 {
     "Static",
     "Static",
@@ -50,12 +49,12 @@ static const char* bodyTypeNames[] =
 
 
 RigidBody2D::RigidBody2D(Context* context) :
 RigidBody2D::RigidBody2D(Context* context) :
     Component(context),
     Component(context),
-    massData_(),    // b2MassData structure does not have a constructor so need to zero-initialize all its members
     useFixtureMass_(true),
     useFixtureMass_(true),
-    body_(0),
-    castShadows_(true)
+    body_(0)
 {
 {
-    // Make sure the massData's center is zero-initialized as well
+    // Make sure the massData members are zero-initialized.
+    massData_.mass = 0.0f;
+    massData_.I = 0.0f;
     massData_.center.SetZero();
     massData_.center.SetZero();
 }
 }
 
 
@@ -71,24 +70,23 @@ RigidBody2D::~RigidBody2D()
 
 
 void RigidBody2D::RegisterObject(Context* context)
 void RigidBody2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<RigidBody2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<RigidBody2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ENUM_ACCESSOR_ATTRIBUTE("Body Type", GetBodyType, SetBodyType, BodyType2D, bodyTypeNames, DEFAULT_BODYTYPE, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Mass", GetMass, SetMass, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Inertia", GetInertia, SetInertia, float, 0.0f, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Mass Center", GetMassCenter, SetMassCenter, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Use Fixture Mass", GetUseFixtureMass, SetUseFixtureMass, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Linear Damping", GetLinearDamping, SetLinearDamping, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Angular Damping", GetAngularDamping, SetAngularDamping, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Allow Sleep", IsAllowSleep, SetAllowSleep, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Fixed Rotation", IsFixedRotation, SetFixedRotation, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Bullet", IsBullet, SetBullet, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Gravity Scale", GetGravityScale, SetGravityScale, float, 1.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Awake", IsAwake, SetAwake, bool, true, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Linear Velocity", GetLinearVelocity, SetLinearVelocity, Vector2, Vector2::ZERO, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Angular Velocity", GetAngularVelocity, SetAngularVelocity, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("CastShadows", GetCastShadows, SetCastShadows, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Body Type", GetBodyType, SetBodyType, BodyType2D, bodyTypeNames, DEFAULT_BODYTYPE, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Mass", GetMass, SetMass, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Inertia", GetInertia, SetInertia, float, 0.0f, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Mass Center", GetMassCenter, SetMassCenter, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Use Fixture Mass", GetUseFixtureMass, SetUseFixtureMass, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Linear Damping", GetLinearDamping, SetLinearDamping, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Angular Damping", GetAngularDamping, SetAngularDamping, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Allow Sleep", IsAllowSleep, SetAllowSleep, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Fixed Rotation", IsFixedRotation, SetFixedRotation, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Bullet", IsBullet, SetBullet, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Gravity Scale", GetGravityScale, SetGravityScale, float, 1.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Awake", IsAwake, SetAwake, bool, true, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Linear Velocity", GetLinearVelocity, SetLinearVelocity, Vector2, Vector2::ZERO, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Angular Velocity", GetAngularVelocity, SetAngularVelocity, float, 0.0f, AM_DEFAULT);
 }
 }
 
 
 
 
@@ -106,14 +104,22 @@ void RigidBody2D::OnSetEnabled()
 
 
 void RigidBody2D::SetBodyType(BodyType2D type)
 void RigidBody2D::SetBodyType(BodyType2D type)
 {
 {
-    b2BodyType bodyType = (b2BodyType)type;
-    if (bodyDef_.type == bodyType)
-        return;
-
-    bodyDef_.type = bodyType;
-
+    b2BodyType bodyType = (b2BodyType)type;    
     if (body_)
     if (body_)
+    {
         body_->SetType(bodyType);
         body_->SetType(bodyType);
+        // Mass data was reset to keep it legal (e.g. static body should have mass 0.)
+        // If not using fixture mass, reassign our mass data now
+        if (!useFixtureMass_)
+            body_->SetMassData(&massData_);
+    }
+    else
+    {
+        if (bodyDef_.type == bodyType)
+            return;
+
+        bodyDef_.type = bodyType;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
@@ -126,7 +132,7 @@ void RigidBody2D::SetMass(float mass)
 
 
     massData_.mass = mass;
     massData_.mass = mass;
 
 
-    if (useFixtureMass_ && body_)
+    if (!useFixtureMass_ && body_)
         body_->SetMassData(&massData_);
         body_->SetMassData(&massData_);
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
@@ -140,7 +146,7 @@ void RigidBody2D::SetInertia(float inertia)
 
 
     massData_.I = inertia;
     massData_.I = inertia;
 
 
-    if (useFixtureMass_ && body_)
+    if (!useFixtureMass_ && body_)
         body_->SetMassData(&massData_);
         body_->SetMassData(&massData_);
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
@@ -154,7 +160,7 @@ void RigidBody2D::SetMassCenter(const Vector2& center)
 
 
     massData_.center = b2Center;
     massData_.center = b2Center;
 
 
-    if (useFixtureMass_ && body_)
+    if (!useFixtureMass_ && body_)
         body_->SetMassData(&massData_);
         body_->SetMassData(&massData_);
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
@@ -169,6 +175,7 @@ void RigidBody2D::SetUseFixtureMass(bool useFixtureMass)
 
 
     if (body_)
     if (body_)
     {
     {
+        body_->m_useFixtureMass = useFixtureMass;
         if (useFixtureMass_)
         if (useFixtureMass_)
             body_->ResetMassData();
             body_->ResetMassData();
         else
         else
@@ -180,91 +187,111 @@ void RigidBody2D::SetUseFixtureMass(bool useFixtureMass)
 
 
 void RigidBody2D::SetLinearDamping(float linearDamping)
 void RigidBody2D::SetLinearDamping(float linearDamping)
 {
 {
-    if (bodyDef_.linearDamping == linearDamping)
-        return;
-
-    bodyDef_.linearDamping = linearDamping;
-
     if (body_)
     if (body_)
         body_->SetLinearDamping(linearDamping);
         body_->SetLinearDamping(linearDamping);
+    else
+    {
+        if (bodyDef_.linearDamping == linearDamping)
+            return;
+
+        bodyDef_.linearDamping = linearDamping;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
 void RigidBody2D::SetAngularDamping(float angularDamping)
 void RigidBody2D::SetAngularDamping(float angularDamping)
 {
 {
-    if (bodyDef_.angularDamping == angularDamping)
-        return;
-
-    bodyDef_.angularDamping = angularDamping;
-
     if (body_)
     if (body_)
         body_->SetAngularDamping(angularDamping);
         body_->SetAngularDamping(angularDamping);
+    else
+    {
+        if (bodyDef_.angularDamping == angularDamping)
+            return;
+
+        bodyDef_.angularDamping = angularDamping;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
 void RigidBody2D::SetAllowSleep(bool allowSleep)
 void RigidBody2D::SetAllowSleep(bool allowSleep)
 {
 {
-    if (bodyDef_.allowSleep == allowSleep)
-        return;
-
-    bodyDef_.allowSleep = allowSleep;
-
     if (body_)
     if (body_)
         body_->SetSleepingAllowed(allowSleep);
         body_->SetSleepingAllowed(allowSleep);
+    else
+    {
+        if (bodyDef_.allowSleep == allowSleep)
+            return;
+
+        bodyDef_.allowSleep = allowSleep;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
 void RigidBody2D::SetFixedRotation(bool fixedRotation)
 void RigidBody2D::SetFixedRotation(bool fixedRotation)
 {
 {
-    if (bodyDef_.fixedRotation == fixedRotation)
-        return;
-
-    bodyDef_.fixedRotation = fixedRotation;
-
     if (body_)
     if (body_)
+    {
         body_->SetFixedRotation(fixedRotation);
         body_->SetFixedRotation(fixedRotation);
+        // Mass data was reset to keep it legal (e.g. non-rotating body should have inertia 0.)
+        // If not using fixture mass, reassign our mass data now
+        if (!useFixtureMass_)
+            body_->SetMassData(&massData_);
+    }
+    else
+    {
+        if (bodyDef_.fixedRotation == fixedRotation)
+            return;
+
+        bodyDef_.fixedRotation = fixedRotation;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
 void RigidBody2D::SetBullet(bool bullet)
 void RigidBody2D::SetBullet(bool bullet)
 {
 {
-    if (bodyDef_.bullet == bullet)
-        return;
-
-    bodyDef_.bullet = bullet;
-
     if (body_)
     if (body_)
         body_->SetBullet(bullet);
         body_->SetBullet(bullet);
+    else
+    {
+        if (bodyDef_.bullet == bullet)
+            return;
+
+        bodyDef_.bullet = bullet;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
 void RigidBody2D::SetGravityScale(float gravityScale)
 void RigidBody2D::SetGravityScale(float gravityScale)
 {
 {
-    if (bodyDef_.gravityScale == gravityScale)
-        return;
-
-    bodyDef_.gravityScale = gravityScale;
-
     if (body_)
     if (body_)
         body_->SetGravityScale(gravityScale);
         body_->SetGravityScale(gravityScale);
+    else
+    {
+        if (bodyDef_.gravityScale == gravityScale)
+            return;
+
+        bodyDef_.gravityScale = gravityScale;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
 void RigidBody2D::SetAwake(bool awake)
 void RigidBody2D::SetAwake(bool awake)
 {
 {
-    if (bodyDef_.awake == awake)
-        return;
-
-    bodyDef_.awake = awake;
-
     if (body_)
     if (body_)
         body_->SetAwake(awake);
         body_->SetAwake(awake);
+    else
+    {
+        if (bodyDef_.awake == awake)
+            return;
+
+        bodyDef_.awake = awake;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
@@ -272,26 +299,30 @@ void RigidBody2D::SetAwake(bool awake)
 void RigidBody2D::SetLinearVelocity(const Vector2& linearVelocity)
 void RigidBody2D::SetLinearVelocity(const Vector2& linearVelocity)
 {
 {
     b2Vec2 b2linearVelocity = ToB2Vec2(linearVelocity);
     b2Vec2 b2linearVelocity = ToB2Vec2(linearVelocity);
-    if (bodyDef_.linearVelocity == b2linearVelocity)
-        return;
-
-    bodyDef_.linearVelocity = b2linearVelocity;
-
     if (body_)
     if (body_)
         body_->SetLinearVelocity(b2linearVelocity);
         body_->SetLinearVelocity(b2linearVelocity);
+    else
+    {
+        if (bodyDef_.linearVelocity == b2linearVelocity)
+            return;
+
+        bodyDef_.linearVelocity = b2linearVelocity;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
 void RigidBody2D::SetAngularVelocity(float angularVelocity)
 void RigidBody2D::SetAngularVelocity(float angularVelocity)
 {
 {
-    if (bodyDef_.angularVelocity == angularVelocity)
-        return;
-
-    bodyDef_.angularVelocity = angularVelocity;
-
     if (body_)
     if (body_)
-        body_->SetAngularVelocity(angularVelocity);
+        body_->SetAngularVelocity(angularVelocity); 
+    else
+    {
+        if (bodyDef_.angularVelocity == angularVelocity)
+            return;
+
+        bodyDef_.angularVelocity = angularVelocity;
+    }
 
 
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
@@ -382,17 +413,49 @@ void RigidBody2D::ReleaseBody()
 
 
 void RigidBody2D::ApplyWorldTransform()
 void RigidBody2D::ApplyWorldTransform()
 {
 {
-    if (!body_)
+    if (!body_ || !node_)
         return;
         return;
 
 
-    physicsWorld_->SetApplyingTransforms(true);
+    // If the rigid body is parented to another rigid body, can not set the transform immediately.
+    // In that case store it to PhysicsWorld2D for delayed assignment
+    RigidBody2D* parentRigidBody = 0;
+    Node* parent = node_->GetParent();
+    if (parent != GetScene() && parent)
+        parentRigidBody = parent->GetComponent<RigidBody2D>();
+
+    // If body is not parented and is static or sleeping, no need to update
+    if (!parentRigidBody && (!body_->IsActive() || body_->GetType() == b2_staticBody || !body_->IsAwake()))
+        return;
 
 
-    Node* node = GetNode();
     const b2Transform& transform = body_->GetTransform();
     const b2Transform& transform = body_->GetTransform();
-    node->SetWorldPosition(ToVector3(transform.p));
-    node->SetWorldRotation(Quaternion(transform.q.GetAngle() * M_RADTODEG, Vector3::FORWARD));
+    Vector3 newWorldPosition = node_->GetWorldPosition();
+    newWorldPosition.x_ = transform.p.x;
+    newWorldPosition.y_ = transform.p.y;
+    Quaternion newWorldRotation(transform.q.GetAngle() * M_RADTODEG, Vector3::FORWARD);
 
 
-    physicsWorld_->SetApplyingTransforms(false);
+    if (parentRigidBody)
+    {
+        DelayedWorldTransform2D delayed;
+        delayed.rigidBody_ = this;
+        delayed.parentRigidBody_ = parentRigidBody;
+        delayed.worldPosition_ = newWorldPosition;
+        delayed.worldRotation_ = newWorldRotation;
+        physicsWorld_->AddDelayedWorldTransform(delayed);
+    }
+    else
+        ApplyWorldTransform(newWorldPosition, newWorldRotation);
+}
+
+void RigidBody2D::ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation)
+{
+    if (newWorldPosition != node_->GetWorldPosition() || newWorldRotation != node_->GetWorldRotation())
+    {
+        // Do not feed changed position back to simulation now
+        physicsWorld_->SetApplyingTransforms(true);
+        node_->SetWorldPosition(newWorldPosition);
+        node_->SetWorldRotation(newWorldRotation);
+        physicsWorld_->SetApplyingTransforms(false);
+    }
 }
 }
 
 
 void RigidBody2D::AddCollisionShape2D(CollisionShape2D* collisionShape)
 void RigidBody2D::AddCollisionShape2D(CollisionShape2D* collisionShape)
@@ -440,24 +503,24 @@ float RigidBody2D::GetMass() const
 {
 {
     if (!useFixtureMass_)
     if (!useFixtureMass_)
         return massData_.mass;
         return massData_.mass;
-
-    return body_ ? body_->GetMass() : 0.0f;
+    else
+        return body_ ? body_->GetMass() : 0.0f;
 }
 }
 
 
 float RigidBody2D::GetInertia() const
 float RigidBody2D::GetInertia() const
 {
 {
     if (!useFixtureMass_)
     if (!useFixtureMass_)
         return massData_.I;
         return massData_.I;
-
-    return body_ ? body_->GetInertia() : 0.0f;
+    else
+        return body_ ? body_->GetInertia() : 0.0f;
 }
 }
 
 
 Vector2 RigidBody2D::GetMassCenter() const
 Vector2 RigidBody2D::GetMassCenter() const
 {
 {
     if (!useFixtureMass_)
     if (!useFixtureMass_)
         return ToVector2(massData_.center);
         return ToVector2(massData_.center);
-
-    return body_ ? ToVector2(body_->GetLocalCenter()) : Vector2::ZERO;
+    else
+        return body_ ? ToVector2(body_->GetLocalCenter()) : Vector2::ZERO;
 }
 }
 
 
 bool RigidBody2D::IsAwake() const
 bool RigidBody2D::IsAwake() const
@@ -478,7 +541,18 @@ float RigidBody2D::GetAngularVelocity() const
 void RigidBody2D::OnNodeSet(Node* node)
 void RigidBody2D::OnNodeSet(Node* node)
 {
 {
     if (node)
     if (node)
+    {
         node->AddListener(this);
         node->AddListener(this);
+
+        PODVector<CollisionShape2D*> shapes;
+        node_->GetDerivedComponents<CollisionShape2D>(shapes);
+
+        for (PODVector<CollisionShape2D*>::Iterator i = shapes.Begin(); i != shapes.End(); ++i)
+        {
+            (*i)->CreateFixture();
+            AddCollisionShape2D(*i);
+        }
+    }
 }
 }
 
 
 void RigidBody2D::OnSceneSet(Scene* scene)
 void RigidBody2D::OnSceneSet(Scene* scene)
@@ -519,10 +593,13 @@ void RigidBody2D::OnMarkedDirty(Node* node)
     float newAngle = node_->GetWorldRotation().RollAngle() * M_DEGTORAD;
     float newAngle = node_->GetWorldRotation().RollAngle() * M_DEGTORAD;
     if (newPosition != bodyDef_.position || newAngle != bodyDef_.angle)
     if (newPosition != bodyDef_.position || newAngle != bodyDef_.angle)
     {
     {
-        bodyDef_.position = newPosition;
-        bodyDef_.angle = newAngle;
         if (body_)
         if (body_)
             body_->SetTransform(newPosition, newAngle);
             body_->SetTransform(newPosition, newAngle);
+        else
+        {
+            bodyDef_.position = newPosition;
+            bodyDef_.angle = newAngle;
+        }
     }
     }
 }
 }
 
 

+ 22 - 27
Source/Atomic/Atomic2D/RigidBody2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,7 +26,7 @@
 
 
 #include <Box2D/Box2D.h>
 #include <Box2D/Box2D.h>
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class CollisionShape2D;
 class CollisionShape2D;
@@ -37,14 +37,14 @@ class PhysicsWorld2D;
 enum BodyType2D
 enum BodyType2D
 {
 {
     BT_STATIC = b2_staticBody,
     BT_STATIC = b2_staticBody,
-    BT_DYNAMIC = b2_dynamicBody,
     BT_KINEMATIC = b2_kinematicBody,
     BT_KINEMATIC = b2_kinematicBody,
+    BT_DYNAMIC = b2_dynamicBody
 };
 };
 
 
 /// 2D rigid body component.
 /// 2D rigid body component.
-class ATOMIC_API RigidBody2D : public Component
+class URHO3D_API RigidBody2D : public Component
 {
 {
-    OBJECT(RigidBody2D);
+    URHO3D_OBJECT(RigidBody2D, Component);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -59,13 +59,13 @@ public:
 
 
     /// Set body type.
     /// Set body type.
     void SetBodyType(BodyType2D bodyType);
     void SetBodyType(BodyType2D bodyType);
-    /// Set Mass.
+    /// Set mass.
     void SetMass(float mass);
     void SetMass(float mass);
     /// Set inertia.
     /// Set inertia.
     void SetInertia(float inertia);
     void SetInertia(float inertia);
     /// Set mass center.
     /// Set mass center.
     void SetMassCenter(const Vector2& center);
     void SetMassCenter(const Vector2& center);
-    /// Use fixture mass (default is true).
+    /// Set whether to automatically calculate mass and inertia from collision shapes. Default true.
     void SetUseFixtureMass(bool useFixtureMass);
     void SetUseFixtureMass(bool useFixtureMass);
     /// Set linear damping.
     /// Set linear damping.
     void SetLinearDamping(float linearDamping);
     void SetLinearDamping(float linearDamping);
@@ -75,7 +75,7 @@ public:
     void SetAllowSleep(bool allowSleep);
     void SetAllowSleep(bool allowSleep);
     /// Set fixed rotation.
     /// Set fixed rotation.
     void SetFixedRotation(bool fixedRotation);
     void SetFixedRotation(bool fixedRotation);
-    /// Set bullet.
+    /// Set bullet mode.
     void SetBullet(bool bullet);
     void SetBullet(bool bullet);
     /// Set gravity scale.
     /// Set gravity scale.
     void SetGravityScale(float gravityScale);
     void SetGravityScale(float gravityScale);
@@ -101,8 +101,10 @@ public:
     /// Release body.
     /// Release body.
     void ReleaseBody();
     void ReleaseBody();
 
 
-    /// Apply world transform.
+    /// Apply world transform from the Box2D body. Called by PhysicsWorld2D.
     void ApplyWorldTransform();
     void ApplyWorldTransform();
+    /// Apply specified world position & rotation. Called by PhysicsWorld2D.
+    void ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation);
     /// Add collision shape.
     /// Add collision shape.
     void AddCollisionShape2D(CollisionShape2D* collisionShape);
     void AddCollisionShape2D(CollisionShape2D* collisionShape);
     /// Remove collision shape.
     /// Remove collision shape.
@@ -113,35 +115,35 @@ public:
     void RemoveConstraint2D(Constraint2D* constraint);
     void RemoveConstraint2D(Constraint2D* constraint);
 
 
     /// Return body type.
     /// Return body type.
-    BodyType2D GetBodyType() const { return (BodyType2D)bodyDef_.type; }
+    BodyType2D GetBodyType() const { return body_ ? (BodyType2D)body_->GetType() : (BodyType2D)bodyDef_.type; }
 
 
-    /// Return Mass.
+    /// Return mass.
     float GetMass() const;
     float GetMass() const;
     /// Return inertia.
     /// Return inertia.
     float GetInertia() const;
     float GetInertia() const;
     /// Return mass center.
     /// Return mass center.
     Vector2 GetMassCenter() const;
     Vector2 GetMassCenter() const;
 
 
-    /// Return use fixture mass.
+    /// Return whether to calculate mass and inertia from collision shapes automatically.
     bool GetUseFixtureMass() const { return useFixtureMass_; }
     bool GetUseFixtureMass() const { return useFixtureMass_; }
 
 
     /// Return linear damping.
     /// Return linear damping.
-    float GetLinearDamping() const { return bodyDef_.linearDamping; }
+    float GetLinearDamping() const { return body_ ? body_->GetLinearDamping() : bodyDef_.linearDamping; }
 
 
     /// Return angular damping.
     /// Return angular damping.
-    float GetAngularDamping() const { return bodyDef_.angularDamping; }
+    float GetAngularDamping() const { return body_ ? body_->GetAngularDamping() : bodyDef_.angularDamping; }
 
 
     /// Return allow sleep.
     /// Return allow sleep.
-    bool IsAllowSleep() const { return bodyDef_.allowSleep; }
+    bool IsAllowSleep() const { return body_ ? body_->IsSleepingAllowed() : bodyDef_.allowSleep; }
 
 
     /// Return fixed rotation.
     /// Return fixed rotation.
-    bool IsFixedRotation() const { return bodyDef_.fixedRotation; }
+    bool IsFixedRotation() const { return body_ ? body_->IsFixedRotation() : bodyDef_.fixedRotation; }
 
 
-    /// Return bullet.
-    bool IsBullet() const { return bodyDef_.bullet; }
+    /// Return bullet mode.
+    bool IsBullet() const { return body_ ? body_->IsBullet() : bodyDef_.bullet; }
 
 
     /// Return gravity scale.
     /// Return gravity scale.
-    float GetGravityScale() const { return bodyDef_.gravityScale; }
+    float GetGravityScale() const { return body_ ? body_->GetGravityScale() : bodyDef_.gravityScale; }
 
 
     /// Return awake.
     /// Return awake.
     bool IsAwake() const;
     bool IsAwake() const;
@@ -153,10 +155,6 @@ public:
     /// Return Box2D body.
     /// Return Box2D body.
     b2Body* GetBody() const { return body_; }
     b2Body* GetBody() const { return body_; }
 
 
-    bool GetCastShadows() const { return castShadows_; }
-    void SetCastShadows(bool castShadows) { castShadows_ = castShadows; }
-
-
 private:
 private:
     /// Handle node being assigned.
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
     virtual void OnNodeSet(Node* node);
@@ -171,7 +169,7 @@ private:
     b2BodyDef bodyDef_;
     b2BodyDef bodyDef_;
     /// Box2D mass data.
     /// Box2D mass data.
     b2MassData massData_;
     b2MassData massData_;
-    /// Use fixture mass.
+    /// Use fixture mass (calculate mass & inertia from collision shapes automatically.)
     bool useFixtureMass_;
     bool useFixtureMass_;
     /// Box2D body.
     /// Box2D body.
     b2Body* body_;
     b2Body* body_;
@@ -179,9 +177,6 @@ private:
     Vector<WeakPtr<CollisionShape2D> > collisionShapes_;
     Vector<WeakPtr<CollisionShape2D> > collisionShapes_;
     /// Constraints.
     /// Constraints.
     Vector<WeakPtr<Constraint2D> > constraints_;
     Vector<WeakPtr<Constraint2D> > constraints_;
-
-    bool castShadows_;
-
 };
 };
 
 
 }
 }

+ 26 - 18
Source/Atomic/Atomic2D/Sprite2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,19 +26,20 @@
 #include "../Graphics/Texture2D.h"
 #include "../Graphics/Texture2D.h"
 #include "../IO/Deserializer.h"
 #include "../IO/Deserializer.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
-#include "../Atomic2D/Drawable2D.h"
-#include "../Atomic2D/Sprite2D.h"
-#include "../Atomic2D/SpriteSheet2D.h"
+#include "../Urho2D/Drawable2D.h"
+#include "../Urho2D/Sprite2D.h"
+#include "../Urho2D/SpriteSheet2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 Sprite2D::Sprite2D(Context* context) :
 Sprite2D::Sprite2D(Context* context) :
     Resource(context),
     Resource(context),
     hotSpot_(0.5f, 0.5f),
     hotSpot_(0.5f, 0.5f),
-    offset_(0, 0)
+    offset_(0, 0),
+    edgeOffset_(0.0f)
 {
 {
 
 
 }
 }
@@ -106,6 +107,15 @@ bool Sprite2D::EndLoad()
 void Sprite2D::SetTexture(Texture2D* texture)
 void Sprite2D::SetTexture(Texture2D* texture)
 {
 {
     texture_ = texture;
     texture_ = texture;
+    // Ensure the texture doesn't have wrap addressing as that will cause bleeding bugs on the edges.
+    // Could also choose border mode, but in that case a universally good border color (without alpha bugs)
+    // would be hard to choose. Ideal is for the user to configure the texture parameters in its parameter
+    // XML file.
+    if (texture_->GetAddressMode(COORD_U) == ADDRESS_WRAP)
+    {
+        texture_->SetAddressMode(COORD_U, ADDRESS_CLAMP);
+        texture_->SetAddressMode(COORD_V, ADDRESS_CLAMP);
+    }
 }
 }
 
 
 void Sprite2D::SetRectangle(const IntRect& rectangle)
 void Sprite2D::SetRectangle(const IntRect& rectangle)
@@ -123,6 +133,11 @@ void Sprite2D::SetOffset(const IntVector2& offset)
     offset_ = offset;
     offset_ = offset;
 }
 }
 
 
+void Sprite2D::SetTextureEdgeOffset(float offset)
+{
+    edgeOffset_ = offset;
+}
+
 void Sprite2D::SetSpriteSheet(SpriteSheet2D* spriteSheet)
 void Sprite2D::SetSpriteSheet(SpriteSheet2D* spriteSheet)
 {
 {
     spriteSheet_ = spriteSheet;
     spriteSheet_ = spriteSheet;
@@ -144,18 +159,11 @@ bool Sprite2D::GetDrawRectangle(Rect& rect, const Vector2& hotSpot, bool flipX,
     float hotSpotX = flipX ? (1.0f - hotSpot.x_) : hotSpot.x_;
     float hotSpotX = flipX ? (1.0f - hotSpot.x_) : hotSpot.x_;
     float hotSpotY = flipY ? (1.0f - hotSpot.y_) : hotSpot.y_;
     float hotSpotY = flipY ? (1.0f - hotSpot.y_) : hotSpot.y_;
 
 
-#ifdef ATOMIC_OPENGL
     rect.min_.x_ = -width * hotSpotX;
     rect.min_.x_ = -width * hotSpotX;
     rect.max_.x_ = width * (1.0f - hotSpotX);
     rect.max_.x_ = width * (1.0f - hotSpotX);
     rect.min_.y_ = -height * hotSpotY;
     rect.min_.y_ = -height * hotSpotY;
     rect.max_.y_ = height * (1.0f - hotSpotY);
     rect.max_.y_ = height * (1.0f - hotSpotY);
-#else
-    const float halfPixelOffset = 0.5f * PIXEL_SIZE;
-    rect.min_.x_ = -width * hotSpotX + halfPixelOffset;
-    rect.max_.x_ = width * (1.0f - hotSpotX) + halfPixelOffset;
-    rect.min_.y_ = -height * hotSpotY + halfPixelOffset;
-    rect.max_.y_ = height * (1.0f - hotSpotY) + halfPixelOffset;
-#endif
+
     return true;
     return true;
 }
 }
 
 
@@ -167,11 +175,11 @@ bool Sprite2D::GetTextureRectangle(Rect& rect, bool flipX, bool flipY) const
     float invWidth = 1.0f / (float)texture_->GetWidth();
     float invWidth = 1.0f / (float)texture_->GetWidth();
     float invHeight = 1.0f / (float)texture_->GetHeight();
     float invHeight = 1.0f / (float)texture_->GetHeight();
 
 
-    rect.min_.x_ = rectangle_.left_ * invWidth;
-    rect.max_.x_ = rectangle_.right_ * invWidth;
+    rect.min_.x_ = ((float)rectangle_.left_ + edgeOffset_) * invWidth;
+    rect.max_.x_ = ((float)rectangle_.right_ - edgeOffset_) * invWidth;
 
 
-    rect.min_.y_ = rectangle_.bottom_ * invHeight;
-    rect.max_.y_ = rectangle_.top_ * invHeight;
+    rect.min_.y_ = ((float)rectangle_.bottom_ - edgeOffset_) * invHeight;
+    rect.max_.y_ = ((float)rectangle_.top_ + edgeOffset_) * invHeight;
 
 
     if (flipX)
     if (flipX)
         Swap(rect.min_.x_, rect.max_.x_);
         Swap(rect.min_.x_, rect.max_.x_);

+ 12 - 4
Source/Atomic/Atomic2D/Sprite2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,16 +24,16 @@
 
 
 #include "../Resource/Resource.h"
 #include "../Resource/Resource.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class SpriteSheet2D;
 class SpriteSheet2D;
 class Texture2D;
 class Texture2D;
 
 
 /// Sprite.
 /// Sprite.
-class ATOMIC_API Sprite2D : public Resource
+class URHO3D_API Sprite2D : public Resource
 {
 {
-    OBJECT(Sprite2D);
+    URHO3D_OBJECT(Sprite2D, Resource);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -56,6 +56,8 @@ public:
     void SetHotSpot(const Vector2& hotSpot);
     void SetHotSpot(const Vector2& hotSpot);
     /// Set offset.
     /// Set offset.
     void SetOffset(const IntVector2& offset);
     void SetOffset(const IntVector2& offset);
+    /// Set texture edge offset in pixels. This affects the left/right and top/bottom edges equally to prevent edge sampling artifacts. Default 0.
+    void SetTextureEdgeOffset(float offset);
     /// Set sprite sheet.
     /// Set sprite sheet.
     void SetSpriteSheet(SpriteSheet2D* spriteSheet);
     void SetSpriteSheet(SpriteSheet2D* spriteSheet);
 
 
@@ -71,9 +73,13 @@ public:
     /// Return offset.
     /// Return offset.
     const IntVector2& GetOffset() const { return offset_; }
     const IntVector2& GetOffset() const { return offset_; }
 
 
+    /// Return texture edge offset.
+    float GetTextureEdgeOffset() const { return edgeOffset_; }
+
     /// Return sprite sheet.
     /// Return sprite sheet.
     SpriteSheet2D* GetSpriteSheet() const { return spriteSheet_; }
     SpriteSheet2D* GetSpriteSheet() const { return spriteSheet_; }
 
 
+
     /// Return draw rectangle.
     /// Return draw rectangle.
     bool GetDrawRectangle(Rect& rect, bool flipX = false, bool flipY = false) const;
     bool GetDrawRectangle(Rect& rect, bool flipX = false, bool flipY = false) const;
     /// Return draw rectangle with custom hot spot.
     /// Return draw rectangle with custom hot spot.
@@ -99,6 +105,8 @@ private:
     WeakPtr<SpriteSheet2D> spriteSheet_;
     WeakPtr<SpriteSheet2D> spriteSheet_;
     /// Texture used while loading.
     /// Texture used while loading.
     SharedPtr<Texture2D> loadTexture_;
     SharedPtr<Texture2D> loadTexture_;
+    /// Offset to fix texture edge bleeding.
+    float edgeOffset_;
 };
 };
 
 
 }
 }

+ 109 - 17
Source/Atomic/Atomic2D/SpriteSheet2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -30,12 +30,13 @@
 #include "../Resource/PListFile.h"
 #include "../Resource/PListFile.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/XMLFile.h"
 #include "../Resource/XMLFile.h"
-#include "../Atomic2D/Sprite2D.h"
-#include "../Atomic2D/SpriteSheet2D.h"
+#include "../Resource/JSONFile.h"
+#include "../Urho2D/Sprite2D.h"
+#include "../Urho2D/SpriteSheet2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 SpriteSheet2D::SpriteSheet2D(Context* context) :
 SpriteSheet2D::SpriteSheet2D(Context* context) :
@@ -67,7 +68,11 @@ bool SpriteSheet2D::BeginLoad(Deserializer& source)
     if (extension == ".xml")
     if (extension == ".xml")
         return BeginLoadFromXMLFile(source);
         return BeginLoadFromXMLFile(source);
 
 
-    LOGERROR("Unsupported file type");
+    if (extension == ".json")
+        return BeginLoadFromJSONFile(source);
+
+
+    URHO3D_LOGERROR("Unsupported file type");
     return false;
     return false;
 }
 }
 
 
@@ -79,16 +84,16 @@ bool SpriteSheet2D::EndLoad()
     if (loadXMLFile_)
     if (loadXMLFile_)
         return EndLoadFromXMLFile();
         return EndLoadFromXMLFile();
 
 
+    if (loadJSONFile_)
+        return EndLoadFromJSONFile();
+
     return false;
     return false;
 }
 }
 
 
-Sprite2D* SpriteSheet2D::GetSprite(const String& name) const
+void SpriteSheet2D::SetTexture(Texture2D* texture)
 {
 {
-    HashMap<String, SharedPtr<Sprite2D> >::ConstIterator i = spriteMapping_.Find(name);
-    if (i == spriteMapping_.End())
-        return 0;
-
-    return i->second_;
+    loadTextureName_.Clear();
+    texture_ = texture;
 }
 }
 
 
 void SpriteSheet2D::DefineSprite(const String& name, const IntRect& rectangle, const Vector2& hotSpot, const IntVector2& offset)
 void SpriteSheet2D::DefineSprite(const String& name, const IntRect& rectangle, const Vector2& hotSpot, const IntVector2& offset)
@@ -110,12 +115,21 @@ void SpriteSheet2D::DefineSprite(const String& name, const IntRect& rectangle, c
     spriteMapping_[name] = sprite;
     spriteMapping_[name] = sprite;
 }
 }
 
 
+Sprite2D* SpriteSheet2D::GetSprite(const String& name) const
+{
+    HashMap<String, SharedPtr<Sprite2D> >::ConstIterator i = spriteMapping_.Find(name);
+    if (i == spriteMapping_.End())
+        return 0;
+
+    return i->second_;
+}
+
 bool SpriteSheet2D::BeginLoadFromPListFile(Deserializer& source)
 bool SpriteSheet2D::BeginLoadFromPListFile(Deserializer& source)
 {
 {
     loadPListFile_ = new PListFile(context_);
     loadPListFile_ = new PListFile(context_);
     if (!loadPListFile_->Load(source))
     if (!loadPListFile_->Load(source))
     {
     {
-        LOGERROR("Could not load sprite sheet");
+        URHO3D_LOGERROR("Could not load sprite sheet");
         loadPListFile_.Reset();
         loadPListFile_.Reset();
         return false;
         return false;
     }
     }
@@ -140,7 +154,7 @@ bool SpriteSheet2D::EndLoadFromPListFile()
     texture_ = cache->GetResource<Texture2D>(loadTextureName_);
     texture_ = cache->GetResource<Texture2D>(loadTextureName_);
     if (!texture_)
     if (!texture_)
     {
     {
-        LOGERROR("Could not load texture " + loadTextureName_);
+        URHO3D_LOGERROR("Could not load texture " + loadTextureName_);
         loadXMLFile_.Reset();
         loadXMLFile_.Reset();
         loadTextureName_.Clear();
         loadTextureName_.Clear();
         return false;
         return false;
@@ -155,7 +169,7 @@ bool SpriteSheet2D::EndLoadFromPListFile()
         const PListValueMap& frameInfo = i->second_.GetValueMap();
         const PListValueMap& frameInfo = i->second_.GetValueMap();
         if (frameInfo["rotated"]->GetBool())
         if (frameInfo["rotated"]->GetBool())
         {
         {
-            LOGWARNING("Rotated sprite is not support now");
+            URHO3D_LOGWARNING("Rotated sprite is not support now");
             continue;
             continue;
         }
         }
 
 
@@ -187,7 +201,7 @@ bool SpriteSheet2D::BeginLoadFromXMLFile(Deserializer& source)
     loadXMLFile_ = new XMLFile(context_);
     loadXMLFile_ = new XMLFile(context_);
     if (!loadXMLFile_->Load(source))
     if (!loadXMLFile_->Load(source))
     {
     {
-        LOGERROR("Could not load sprite sheet");
+        URHO3D_LOGERROR("Could not load sprite sheet");
         loadXMLFile_.Reset();
         loadXMLFile_.Reset();
         return false;
         return false;
     }
     }
@@ -197,7 +211,7 @@ bool SpriteSheet2D::BeginLoadFromXMLFile(Deserializer& source)
     XMLElement rootElem = loadXMLFile_->GetRoot("TextureAtlas");
     XMLElement rootElem = loadXMLFile_->GetRoot("TextureAtlas");
     if (!rootElem)
     if (!rootElem)
     {
     {
-        LOGERROR("Invalid sprite sheet");
+        URHO3D_LOGERROR("Invalid sprite sheet");
         loadXMLFile_.Reset();
         loadXMLFile_.Reset();
         return false;
         return false;
     }
     }
@@ -216,7 +230,7 @@ bool SpriteSheet2D::EndLoadFromXMLFile()
     texture_ = cache->GetResource<Texture2D>(loadTextureName_);
     texture_ = cache->GetResource<Texture2D>(loadTextureName_);
     if (!texture_)
     if (!texture_)
     {
     {
-        LOGERROR("Could not load texture " + loadTextureName_);
+        URHO3D_LOGERROR("Could not load texture " + loadTextureName_);
         loadXMLFile_.Reset();
         loadXMLFile_.Reset();
         loadTextureName_.Clear();
         loadTextureName_.Clear();
         return false;
         return false;
@@ -256,4 +270,82 @@ bool SpriteSheet2D::EndLoadFromXMLFile()
     return true;
     return true;
 }
 }
 
 
+bool SpriteSheet2D::BeginLoadFromJSONFile(Deserializer& source)
+{
+    loadJSONFile_ = new JSONFile(context_);
+    if (!loadJSONFile_->Load(source))
+    {
+        URHO3D_LOGERROR("Could not load sprite sheet");
+        loadJSONFile_.Reset();
+        return false;
+    }
+
+    SetMemoryUse(source.GetSize());
+
+    JSONValue rootElem = loadJSONFile_->GetRoot();
+    if (rootElem.IsNull())
+    {
+        URHO3D_LOGERROR("Invalid sprite sheet");
+        loadJSONFile_.Reset();
+        return false;
+    }
+
+    // If we're async loading, request the texture now. Finish during EndLoad().
+    loadTextureName_ = GetParentPath(GetName()) + rootElem.Get("imagePath").GetString();
+    if (GetAsyncLoadState() == ASYNC_LOADING)
+        GetSubsystem<ResourceCache>()->BackgroundLoadResource<Texture2D>(loadTextureName_, true, this);
+
+    return true;
+}
+
+bool SpriteSheet2D::EndLoadFromJSONFile()
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    texture_ = cache->GetResource<Texture2D>(loadTextureName_);
+    if (!texture_)
+    {
+        URHO3D_LOGERROR("Could not load texture " + loadTextureName_);
+        loadJSONFile_.Reset();
+        loadTextureName_.Clear();
+        return false;
+    }
+
+    JSONValue rootVal = loadJSONFile_->GetRoot();
+    JSONArray subTextureArray = rootVal.Get("subtextures").GetArray();
+
+    for (unsigned i = 0; i < subTextureArray.Size(); i++)
+    {
+        const JSONValue& subTextureVal = subTextureArray.At(i);
+        String name = subTextureVal.Get("name").GetString();
+
+        int x = subTextureVal.Get("x").GetInt();
+        int y = subTextureVal.Get("y").GetInt();
+        int width = subTextureVal.Get("width").GetInt();
+        int height = subTextureVal.Get("height").GetInt();
+        IntRect rectangle(x, y, x + width, y + height);
+
+        Vector2 hotSpot(0.5f, 0.5f);
+        IntVector2 offset(0, 0);
+        JSONValue frameWidthVal = subTextureVal.Get("frameWidth");
+        JSONValue frameHeightVal = subTextureVal.Get("frameHeight");
+
+        if (!frameHeightVal.IsNull() && !frameHeightVal.IsNull())
+        {
+            offset.x_ = subTextureVal.Get("frameX").GetInt();
+            offset.y_ = subTextureVal.Get("frameY").GetInt();
+            int frameWidth = frameWidthVal.GetInt();
+            int frameHeight = frameHeightVal.GetInt();
+            hotSpot.x_ = ((float)offset.x_ + frameWidth / 2) / width;
+            hotSpot.y_ = 1.0f - ((float)offset.y_ + frameHeight / 2) / height;
+        }
+
+        DefineSprite(name, rectangle, hotSpot, offset);
+
+    }
+
+    loadJSONFile_.Reset();
+    loadTextureName_.Clear();
+    return true;
+}
+
 }
 }

+ 18 - 8
Source/Atomic/Atomic2D/SpriteSheet2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,18 +24,19 @@
 
 
 #include "../Resource/Resource.h"
 #include "../Resource/Resource.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class PListFile;
 class PListFile;
 class Sprite2D;
 class Sprite2D;
 class Texture2D;
 class Texture2D;
 class XMLFile;
 class XMLFile;
+class JSONFile;
 
 
 /// Sprite sheet.
 /// Sprite sheet.
-class ATOMIC_API SpriteSheet2D : public Resource
+class URHO3D_API SpriteSheet2D : public Resource
 {
 {
-    OBJECT(SpriteSheet2D);
+    URHO3D_OBJECT(SpriteSheet2D, Resource);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -50,14 +51,16 @@ public:
     /// Finish resource loading. Always called from the main thread. Return true if successful.
     /// Finish resource loading. Always called from the main thread. Return true if successful.
     virtual bool EndLoad();
     virtual bool EndLoad();
 
 
+    /// Set texture.
+    void SetTexture(Texture2D* texture);
+    /// Define sprite.
+    void DefineSprite(const String& name, const IntRect& rectangle, const Vector2& hotSpot = Vector2(0.5f, 0.5f),
+        const IntVector2& offset = IntVector2::ZERO);
+
     /// Return texture.
     /// Return texture.
     Texture2D* GetTexture() const { return texture_; }
     Texture2D* GetTexture() const { return texture_; }
-
     /// Return sprite.
     /// Return sprite.
     Sprite2D* GetSprite(const String& name) const;
     Sprite2D* GetSprite(const String& name) const;
-    /// Define sprite.
-    void DefineSprite(const String& name, const IntRect& rectangle, const Vector2& hotSpot = Vector2(0.5f, 0.5f),
-        const IntVector2& offset = IntVector2::ZERO);
 
 
     /// Return sprite mapping.
     /// Return sprite mapping.
     const HashMap<String, SharedPtr<Sprite2D> >& GetSpriteMapping() const { return spriteMapping_; }
     const HashMap<String, SharedPtr<Sprite2D> >& GetSpriteMapping() const { return spriteMapping_; }
@@ -67,10 +70,15 @@ private:
     bool BeginLoadFromPListFile(Deserializer& source);
     bool BeginLoadFromPListFile(Deserializer& source);
     /// End load from PList file.
     /// End load from PList file.
     bool EndLoadFromPListFile();
     bool EndLoadFromPListFile();
+
     /// Begin load from XML file.
     /// Begin load from XML file.
     bool BeginLoadFromXMLFile(Deserializer& source);
     bool BeginLoadFromXMLFile(Deserializer& source);
     /// End load from XML file.
     /// End load from XML file.
     bool EndLoadFromXMLFile();
     bool EndLoadFromXMLFile();
+    /// Begin load from JSON file.
+    bool BeginLoadFromJSONFile(Deserializer& source);
+    /// End load from JSON file.
+    bool EndLoadFromJSONFile();
 
 
     /// Texture.
     /// Texture.
     SharedPtr<Texture2D> texture_;
     SharedPtr<Texture2D> texture_;
@@ -80,6 +88,8 @@ private:
     SharedPtr<PListFile> loadPListFile_;
     SharedPtr<PListFile> loadPListFile_;
     /// XML file used while loading.
     /// XML file used while loading.
     SharedPtr<XMLFile> loadXMLFile_;
     SharedPtr<XMLFile> loadXMLFile_;
+    /// JSON file used while loading.
+    SharedPtr<JSONFile> loadJSONFile_;
     /// Texture name used while loading.
     /// Texture name used while loading.
     String loadTextureName_;
     String loadTextureName_;
 };
 };

+ 759 - 0
Source/Atomic/Atomic2D/SpriterData2D.cpp

@@ -0,0 +1,759 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// 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 "../Precompiled.h"
+
+#include "../Math/MathDefs.h"
+#include "../Urho2D/SpriterData2D.h"
+
+#include <PugiXml/pugixml.hpp>
+
+#include <cstring>
+
+using namespace pugi;
+
+namespace Urho3D
+{
+
+namespace Spriter
+{
+
+SpriterData::SpriterData()
+{
+}
+
+SpriterData::~SpriterData()
+{
+    Reset();
+}
+
+void SpriterData::Reset()
+{
+    if (!folders_.Empty())
+    {
+        for (size_t i = 0; i < folders_.Size(); ++i)
+            delete folders_[i];
+        folders_.Clear();
+    }
+
+    if (!entities_.Empty())
+    {
+        for (size_t i = 0; i < entities_.Size(); ++i)
+            delete entities_[i];
+        entities_.Clear();
+    }
+}
+
+bool SpriterData::Load(const pugi::xml_node& node)
+{
+    Reset();
+
+    if (strcmp(node.name(), "spriter_data"))
+        return false;
+
+    scmlVersion_ = node.attribute("scml_version").as_int();
+    generator_ = node.attribute("generator").as_string();
+    generatorVersion_ = node.attribute("scml_version").as_string();
+
+    for (xml_node folderNode = node.child("folder"); !folderNode.empty(); folderNode = folderNode.next_sibling("folder"))
+    {
+        folders_.Push(new  Folder());
+        if (!folders_.Back()->Load(folderNode))
+            return false;
+    }
+
+    for (xml_node entityNode = node.child("entity"); !entityNode.empty(); entityNode = entityNode.next_sibling("entity"))
+    {
+        entities_.Push(new  Entity());
+        if (!entities_.Back()->Load(entityNode))
+            return false;
+    }
+
+    return true;
+}
+
+bool SpriterData::Load(const void* data, size_t size)
+{
+    xml_document document;
+    if (!document.load_buffer(data, size))
+        return false;
+
+    return Load(document.child("spriter_data"));
+}
+
+Folder::Folder()
+{
+
+}
+
+Folder::~Folder()
+{
+    Reset();
+}
+
+void Folder::Reset()
+{
+    for (size_t i = 0; i < files_.Size(); ++i)
+        delete files_[i];
+    files_.Clear();
+}
+
+bool Folder::Load(const pugi::xml_node& node)
+{
+    Reset();
+
+    if (strcmp(node.name(), "folder"))
+        return false;
+
+    id_ = node.attribute("id").as_int();
+    name_ = node.attribute("name").as_string();
+
+    for (xml_node fileNode = node.child("file"); !fileNode.empty(); fileNode = fileNode.next_sibling("file"))
+    {
+        files_.Push(new  File(this));
+        if (!files_.Back()->Load(fileNode))
+            return false;
+    }
+
+    return true;
+}
+
+File::File(Folder* folder) : 
+    folder_(folder)
+{
+}
+
+File::~File()
+{
+}
+
+bool File::Load(const pugi::xml_node& node)
+{
+    if (strcmp(node.name(), "file"))
+        return false;
+
+    id_ = node.attribute("id").as_int();
+    name_ = node.attribute("name").as_string();
+    width_ = node.attribute("width").as_float();
+    height_ = node.attribute("height").as_float();
+    pivotX_ = node.attribute("pivot_x").as_float(0.0f);
+    pivotY_ = node.attribute("pivot_y").as_float(1.0f);
+
+    return true;
+}
+
+Entity::Entity()
+{
+
+}
+
+Entity::~Entity()
+{
+    Reset();
+}
+
+void Entity::Reset()
+{
+    for (size_t i = 0; i < characterMaps_.Size(); ++i)
+        delete characterMaps_[i];
+    characterMaps_.Clear();
+
+    for (size_t i = 0; i < animations_.Size(); ++i)
+        delete animations_[i];
+    animations_.Clear();
+}
+
+bool Entity::Load(const pugi::xml_node& node)
+{
+    Reset();
+
+    if (strcmp(node.name(), "entity"))
+        return false;
+
+    id_ = node.attribute("id").as_int();
+    name_ = node.attribute("name").as_string();
+
+    for (xml_node characterMapNode = node.child("character_map"); !characterMapNode.empty(); characterMapNode = characterMapNode.next_sibling("character_map"))
+    {
+        characterMaps_.Push(new CharacterMap());
+        if (!characterMaps_.Back()->Load(characterMapNode))
+            return false;
+    }
+
+    for (xml_node animationNode = node.child("animation"); !animationNode.empty(); animationNode = animationNode.next_sibling("animation"))
+    {
+        animations_.Push(new  Animation());
+        if (!animations_.Back()->Load(animationNode))
+            return false;
+    }
+
+    return true;
+}
+
+CharacterMap::CharacterMap()
+{
+
+}
+
+CharacterMap::~CharacterMap()
+{
+
+}
+
+void CharacterMap::Reset()
+{
+    for (size_t i = 0; i < maps_.Size(); ++i)
+        delete maps_[i];
+    maps_.Clear();
+}
+
+bool CharacterMap::Load(const pugi::xml_node& node)
+{
+    Reset();
+
+    if (strcmp(node.name(), "character_map"))
+        return false;
+
+    id_ = node.attribute("id").as_int();
+    name_ = node.attribute("name").as_string();
+
+    for (xml_node mapNode = node.child("map"); !mapNode.empty(); mapNode = mapNode.next_sibling("map"))
+    {
+        maps_.Push(new MapInstruction());
+        if (!maps_.Back()->Load(mapNode))
+            return false;
+    }
+
+    return false;
+}
+
+MapInstruction::MapInstruction()
+{
+
+}
+
+MapInstruction::~MapInstruction()
+{
+
+}
+
+bool MapInstruction::Load(const pugi::xml_node& node)
+{
+    if (strcmp(node.name(), "map"))
+        return false;
+
+    folder_ = node.attribute("folder").as_int();
+    file_ = node.attribute("file").as_int();
+    targetFolder_ = node.attribute("target_folder").as_int(-1);
+    targetFile_ = node.attribute("target_file").as_int(-1);
+
+    return true;
+}
+
+Animation::Animation()
+{
+
+}
+
+Animation::~Animation()
+{
+    Reset();
+}
+
+void Animation::Reset()
+{
+    if (!mainlineKeys_.Empty())
+    {
+        for (size_t i = 0; i < mainlineKeys_.Size(); ++i)
+            delete mainlineKeys_[i];
+        mainlineKeys_.Clear();
+    }
+
+    for (size_t i = 0; i < timelines_.Size(); ++i)
+        delete timelines_[i];
+    timelines_.Clear();
+}
+
+bool Animation::Load(const pugi::xml_node& node)
+{
+    Reset();
+
+    if (strcmp(node.name(), "animation"))
+        return false;
+
+    id_ = node.attribute("id").as_int();
+    name_ = node.attribute("name").as_string();
+    length_ = node.attribute("length").as_float() * 0.001f;
+    looping_ = node.attribute("looping").as_bool(true);
+
+    xml_node mainlineNode = node.child("mainline");
+    for (xml_node keyNode = mainlineNode.child("key"); !keyNode.empty(); keyNode = keyNode.next_sibling("key"))
+    {
+        mainlineKeys_.Push(new MainlineKey());
+        if (!mainlineKeys_.Back()->Load(keyNode))
+            return false;
+    }
+
+    for (xml_node timelineNode = node.child("timeline"); !timelineNode.empty(); timelineNode = timelineNode.next_sibling("timeline"))
+    {
+        timelines_.Push(new Timeline());
+        if (!timelines_.Back()->Load(timelineNode))
+            return false;
+    }
+
+    return true;
+}
+
+MainlineKey::MainlineKey()
+{
+
+}
+
+MainlineKey::~MainlineKey()
+{
+    Reset();
+}
+
+void MainlineKey::Reset()
+{
+    for (size_t i = 0; i < boneRefs_.Size(); ++i)
+        delete boneRefs_[i];
+    boneRefs_.Clear();
+
+    for (size_t i = 0; i < objectRefs_.Size(); ++i)
+        delete objectRefs_[i];
+    objectRefs_.Clear();
+}
+
+bool MainlineKey::Load(const pugi::xml_node& node)
+{
+    id_ = node.attribute("id").as_int();
+    time_ = node.attribute("time").as_float() * 0.001f;
+
+    for (xml_node boneRefNode = node.child("bone_ref"); !boneRefNode.empty(); boneRefNode = boneRefNode.next_sibling("bone_ref"))
+    {
+        boneRefs_.Push(new Ref());
+        if (!boneRefs_.Back()->Load(boneRefNode))
+            return false;
+    }
+
+    for (xml_node objectRefNode = node.child("object_ref"); !objectRefNode.empty(); objectRefNode = objectRefNode.next_sibling("object_ref"))
+    {
+        objectRefs_.Push(new Ref());
+        if (!objectRefs_.Back()->Load(objectRefNode))
+            return false;
+    }
+
+    return true;
+}
+
+Ref::Ref()
+{
+
+}
+
+Ref::~Ref()
+{
+}
+
+bool Ref::Load(const pugi::xml_node& node)
+{
+    if (strcmp(node.name(), "bone_ref") && strcmp(node.name(), "object_ref"))
+        return false;
+
+    id_ = node.attribute("id").as_int();
+    parent_ = node.attribute("parent").as_int(-1);
+    timeline_ = node.attribute("timeline").as_int();
+    key_ = node.attribute("key").as_int();
+    zIndex_ = node.attribute("z_index").as_int();
+
+    return true;
+}
+
+Timeline::Timeline()
+{
+
+}
+
+Timeline::~Timeline()
+{
+    Reset();
+}
+
+void Timeline::Reset()
+{
+    for (size_t i = 0; i < keys_.Size(); ++i)
+        delete keys_[i];
+    keys_.Clear();
+}
+
+bool Timeline::Load(const pugi::xml_node& node)
+{
+    Reset();
+
+    if (strcmp(node.name(), "timeline"))
+        return false;
+
+    id_ = node.attribute("id").as_int();
+    name_ = node.attribute("name").as_string();
+
+    String typeString;
+    xml_attribute typeAttr = node.attribute("type");
+    if (typeAttr.empty())
+        typeString = node.attribute("object_type").as_string("sprite");
+    else
+        typeString = typeAttr.as_string("sprite");
+    
+    if (typeString == "bone")
+    {
+        objectType_ = BONE;
+        for (xml_node keyNode = node.child("key"); !keyNode.empty(); keyNode = keyNode.next_sibling("key"))
+        {
+            keys_.Push(new BoneTimelineKey(this));
+            if (!keys_.Back()->Load(keyNode))
+                return false;
+        }
+    }
+    else if (typeString == "sprite")
+    {
+        objectType_ = SPRITE;
+        for (xml_node keyNode = node.child("key"); !keyNode.empty(); keyNode = keyNode.next_sibling("key"))
+        {
+            keys_.Push(new SpriteTimelineKey(this));
+            if (!keys_.Back()->Load(keyNode))
+                return false;
+        }
+    }
+    else
+    {
+        // Unsupported object type now.
+        return false;
+    }
+
+    return true;
+}
+
+TimelineKey::TimelineKey(Timeline* timeline)
+{
+    this->timeline_ = timeline;
+}
+
+TimelineKey::~TimelineKey()
+{
+}
+
+bool TimelineKey::Load(const pugi::xml_node& node)
+{
+    if (strcmp(node.name(), "key"))
+        return false;
+
+    id_ = node.attribute("id").as_int();
+    time_ = node.attribute("time").as_float() * 0.001f;
+
+    String curveType = node.attribute("curve_type").as_string("linear");
+    if (curveType == "instant")
+        curveType_ = INSTANT;
+    else if (curveType == "linear")
+        curveType_ = LINEAR;
+    else if (curveType == "quadratic")
+        curveType_ = QUADRATIC;
+    else if (curveType == "cubic")
+        curveType_ = CUBIC;
+    else
+        curveType_ = LINEAR;
+
+    c1_ = node.attribute("c1").as_float();
+    c2_ = node.attribute("c2").as_float();
+
+    return true;
+}
+
+TimelineKey& TimelineKey::operator=(const TimelineKey& rhs)
+{
+    id_ = rhs.id_;
+    time_ = rhs.time_;
+    curveType_ = rhs.curveType_;
+    c1_ = rhs.c1_;
+    c2_ = rhs.c2_;
+    return *this;
+}
+
+// From http://www.brashmonkey.com/ScmlDocs/ScmlReference.html
+
+inline float Linear(float a, float b, float t)
+{
+    return a + (b - a) * t;
+}
+
+inline float Quadratic(float a, float b, float c, float t)
+{
+    return Linear(Linear(a, b, t), Linear(b, c, t), t);
+}
+
+inline float Cubic(float a, float b, float c, float d, float t)
+{
+    return Linear(Quadratic(a, b, c, t), Quadratic(b, c, d, t), t);
+}
+
+float TimelineKey::GetTByCurveType(float currentTime, float nextTimelineTime) const
+{
+    if (curveType_ == INSTANT)
+        return 0.0f;
+
+    float t = (currentTime - time_) / (nextTimelineTime - time_);
+    switch (curveType_)
+    {
+    case LINEAR:
+        return t;
+
+    case QUADRATIC:
+        return Quadratic(0.0f, c1_, 1.0f, t);
+
+    case CUBIC:
+        return Cubic(0.0f, c1_, c2_, 1.0f, t);
+
+    default:
+        return 0.0f;
+    }
+}
+
+SpatialInfo::SpatialInfo(float x, float y, float angle, float scale_x, float scale_y, float a, int spin)
+{
+    this->x_ = x; 
+    this->y_ = y; 
+    this->angle_ = angle;
+    this->scaleX_ = scale_x; 
+    this->scaleY_ = scale_y; 
+    this->alpha_ = a;
+    this->spin = spin;
+}
+
+SpatialInfo SpatialInfo::UnmapFromParent(const SpatialInfo& parentInfo) const
+{
+    float unmappedX;
+    float unmappedY;
+    float unmappedAngle = angle_ + parentInfo.angle_;
+    float unmappedScaleX = scaleX_ * parentInfo.scaleX_;
+    float unmappedScaleY = scaleY_ * parentInfo.scaleY_;
+    float unmappedAlpha = alpha_ * parentInfo.alpha_;
+
+    if (x_ != 0.0f || y_ != 0.0f)
+    {
+        float preMultX = x_ * parentInfo.scaleX_;
+        float preMultY = y_ * parentInfo.scaleY_;
+
+        float s = Sin(parentInfo.angle_);
+        float c = Cos(parentInfo.angle_);
+
+        unmappedX = (preMultX * c) - (preMultY * s) + parentInfo.x_;
+        unmappedY = (preMultX * s) + (preMultY * c) + parentInfo.y_;
+    }
+    else
+    {
+        unmappedX = parentInfo.x_;
+        unmappedY = parentInfo.y_;
+    }
+
+    return SpatialInfo(unmappedX, unmappedY, unmappedAngle, unmappedScaleX, unmappedScaleY, unmappedAlpha, spin);
+}
+
+void SpatialInfo::Interpolate(const SpatialInfo& other, float t)
+{
+    x_ = Linear(x_, other.x_, t);
+    y_ = Linear(y_, other.y_, t);
+
+    if (spin > 0.0f && (other.angle_ - angle_ < 0.0f))
+    {
+        angle_ = Linear(angle_, other.angle_ + 360.0f, t);
+    }
+    else if (spin < 0.0f && (other.angle_ - angle_ > 0.0f))
+    {
+        angle_ = Linear(angle_, other.angle_ - 360.0f, t);
+    }
+    else
+    {
+        angle_ = Linear(angle_, other.angle_, t);
+    }
+
+    scaleX_ = Linear(scaleX_, other.scaleX_, t);
+    scaleY_ = Linear(scaleY_, other.scaleY_, t);
+    alpha_ = Linear(alpha_, other.alpha_, t);
+}
+
+SpatialTimelineKey::SpatialTimelineKey(Timeline* timeline) : 
+    TimelineKey(timeline)
+{
+
+}
+
+SpatialTimelineKey::~SpatialTimelineKey()
+{
+
+}
+
+bool SpatialTimelineKey::Load(const xml_node& node)
+{
+    if (!TimelineKey::Load(node))
+        return false;
+
+    xml_node childNode = node.child("bone");
+    if (childNode.empty())
+        childNode = node.child("object");
+
+    info_.x_ = childNode.attribute("x").as_float();
+    info_.y_ = childNode.attribute("y").as_float();
+    info_.angle_ = childNode.attribute("angle").as_float();
+    info_.scaleX_ = childNode.attribute("scale_x").as_float(1.0f);
+    info_.scaleY_ = childNode.attribute("scale_y").as_float(1.0f);
+    info_.alpha_ = childNode.attribute("a").as_float(1.0f);
+
+    info_.spin = node.attribute("spin").as_int(1);
+
+    return true;
+}
+
+SpatialTimelineKey& SpatialTimelineKey::operator=(const SpatialTimelineKey& rhs)
+{
+    TimelineKey::operator=(rhs);
+    info_ = rhs.info_;
+    return *this;
+}   
+
+void SpatialTimelineKey::Interpolate(const TimelineKey& other, float t)
+{
+    const SpatialTimelineKey& o = (const SpatialTimelineKey&)other;
+    info_.Interpolate(o.info_, t);
+}
+
+BoneTimelineKey::BoneTimelineKey(Timeline* timeline) : 
+    SpatialTimelineKey(timeline)
+{
+
+}
+
+BoneTimelineKey::~BoneTimelineKey()
+{
+
+}
+
+TimelineKey* BoneTimelineKey::Clone() const
+{
+    BoneTimelineKey* result = new BoneTimelineKey(timeline_);
+    *result = *this;
+    return result;
+}
+
+bool BoneTimelineKey::Load(const xml_node& node)
+{
+    if (!SpatialTimelineKey::Load(node))
+        return false;
+
+    xml_node boneNode = node.child("bone");
+    length_ = boneNode.attribute("length").as_float(200.0f);
+    width_ = boneNode.attribute("width").as_float(10.0f);
+
+    return true;
+}
+
+BoneTimelineKey& BoneTimelineKey::operator=(const BoneTimelineKey& rhs)
+{
+    SpatialTimelineKey::operator=(rhs);
+    length_ = rhs.length_;
+    width_ = rhs.width_;
+
+    return *this;
+}
+
+void BoneTimelineKey::Interpolate(const TimelineKey& other, float t)
+{
+    SpatialTimelineKey::Interpolate(other, t);
+
+    const BoneTimelineKey& o = (const BoneTimelineKey&)other;
+    length_ = Linear(length_, o.length_, t);
+    width_ = Linear(width_, o.width_, t);
+}
+
+TimelineKey* SpriteTimelineKey::Clone() const
+{
+    SpriteTimelineKey* result = new SpriteTimelineKey(timeline_);
+    *result = *this;
+    return result;
+}
+
+SpriteTimelineKey::SpriteTimelineKey(Timeline* timeline) : 
+    SpatialTimelineKey(timeline)
+{
+}
+
+SpriteTimelineKey::~SpriteTimelineKey()
+{
+
+}
+
+bool SpriteTimelineKey::Load(const pugi::xml_node& node)
+{
+    if (!SpatialTimelineKey::Load(node))
+        return false;
+
+    xml_node objectNode = node.child("object");
+    folderId_ = objectNode.attribute("folder").as_int(-1);
+    fileId_ = objectNode.attribute("file").as_int(-1);
+
+    xml_attribute pivotXAttr = objectNode.attribute("pivot_x");
+    xml_attribute pivotYAttr = objectNode.attribute("pivot_y");
+    if (pivotXAttr.empty() && pivotYAttr.empty())
+        useDefaultPivot_ = true;
+    else
+    {
+        useDefaultPivot_ = false;
+        pivotX_ = pivotXAttr.as_float(0.0f);
+        pivotY_ = pivotYAttr.as_float(1.0f);
+    }
+
+    return true;
+}
+
+void SpriteTimelineKey::Interpolate(const TimelineKey& other, float t)
+{
+    SpatialTimelineKey::Interpolate(other, t);
+        
+    const SpriteTimelineKey& o = (const SpriteTimelineKey&)other;
+    pivotX_ = Linear(pivotX_, o.pivotX_, t);
+    pivotY_ = Linear(pivotY_, o.pivotY_, t);
+}
+
+SpriteTimelineKey& SpriteTimelineKey::operator=(const SpriteTimelineKey& rhs)
+{
+    SpatialTimelineKey::operator=(rhs);
+        
+    folderId_ = rhs.folderId_;
+    fileId_ = rhs.fileId_;
+    useDefaultPivot_ = rhs.useDefaultPivot_;
+    pivotX_ = rhs.pivotX_;
+    pivotY_ = rhs.pivotY_;
+
+    return *this;
+}
+
+}
+
+}

+ 311 - 0
Source/Atomic/Atomic2D/SpriterData2D.h

@@ -0,0 +1,311 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// 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
+
+namespace pugi
+{
+class xml_node;
+}
+
+namespace Urho3D
+{
+
+namespace Spriter
+{
+
+struct Animation;
+struct BoneTimelineKey;
+struct CharacterMap;
+struct Entity;
+struct File;
+struct Folder;
+struct MainlineKey;
+struct MapInstruction;
+struct Ref;
+struct SpatialInfo;
+struct SpatialTimelineKey;
+struct SpriterData;
+struct SpriteTimelineKey;
+struct Timeline;
+struct TimelineKey;
+
+/// Spriter data.
+struct SpriterData
+{
+    SpriterData();
+    ~SpriterData();
+
+    void Reset();
+    bool Load(const pugi::xml_node& node);
+    bool Load(const void* data, size_t size);
+
+    int scmlVersion_;
+    String generator_;
+    String generatorVersion_;
+    PODVector<Folder*> folders_;
+    PODVector<Entity*> entities_;
+};
+
+/// Folder.
+struct Folder
+{
+    Folder();
+    ~Folder();
+
+    void Reset();
+    bool Load(const pugi::xml_node& node);
+
+    int id_;
+    String name_;
+    PODVector<File*> files_;
+};
+
+/// File.
+struct File
+{
+    File(Folder* folder);
+    ~File();
+
+    bool Load(const pugi::xml_node& node);
+
+    Folder* folder_;
+    int id_;
+    String name_;
+    float width_;
+    float height_;
+    float pivotX_;
+    float pivotY_;
+};
+
+/// Entity.
+struct Entity
+{
+    Entity();
+    ~Entity();
+
+    void Reset();
+    bool Load(const pugi::xml_node& node);
+
+    int id_;
+    String name_;
+    PODVector<CharacterMap*> characterMaps_;
+    PODVector<Animation*> animations_;
+};
+
+/// Character map.
+struct CharacterMap
+{
+    CharacterMap();
+    ~CharacterMap();
+
+    void Reset();
+    bool Load(const pugi::xml_node& node);
+
+    int id_;
+    String name_;
+    PODVector<MapInstruction*> maps_;
+};
+
+/// Map instruction.
+struct MapInstruction
+{
+    MapInstruction();
+    ~MapInstruction();
+
+    bool Load(const pugi::xml_node& node);
+
+    int folder_;
+    int file_;
+    int targetFolder_;
+    int targetFile_;
+};
+
+/// Animation.
+struct Animation
+{
+    Animation();
+    ~Animation();
+
+    void Reset();
+    bool Load(const pugi::xml_node& node);
+
+    int id_;
+    String name_;
+    float length_;
+    bool looping_;
+    PODVector<MainlineKey*> mainlineKeys_;
+    PODVector<Timeline*> timelines_;
+};
+
+/// Mainline key.
+struct MainlineKey
+{
+    MainlineKey();
+    ~MainlineKey();
+
+    void Reset();
+    bool Load(const pugi::xml_node& node);
+
+    int id_;
+    float time_;
+    PODVector<Ref*> boneRefs_;
+    PODVector<Ref*> objectRefs_;
+};
+
+/// Ref.
+struct Ref
+{
+    Ref();
+    ~Ref();
+
+    bool Load(const pugi::xml_node& node);
+
+    int id_;
+    int parent_;
+    int timeline_;
+    int key_;
+    int zIndex_;
+};
+
+/// Object type.
+enum ObjectType
+{
+    BONE = 0,
+    SPRITE
+};
+
+/// Timeline.
+struct Timeline
+{
+    Timeline();
+    ~Timeline();
+
+    void Reset();
+    bool Load(const pugi::xml_node& node);
+
+    int id_;
+    String name_;
+    ObjectType objectType_;
+    PODVector<SpatialTimelineKey*> keys_;
+};
+
+/// Curve type.
+enum CurveType 
+{
+    INSTANT = 0,
+    LINEAR,
+    QUADRATIC,
+    CUBIC
+};
+
+/// Timeline key.
+struct TimelineKey
+{
+    TimelineKey(Timeline* timeline);
+    virtual ~TimelineKey();
+
+    virtual ObjectType GetObjectType() const = 0;
+    virtual TimelineKey* Clone() const = 0;
+    virtual bool Load(const pugi::xml_node& node);
+    virtual void Interpolate(const TimelineKey& other, float t) = 0;
+    TimelineKey& operator=(const TimelineKey& rhs);
+    float GetTByCurveType(float currentTime, float nextTimelineTime) const;
+
+    Timeline* timeline_;
+    int id_;
+    float time_;
+    CurveType curveType_;
+    float c1_;
+    float c2_;
+};
+
+/// Spatial info.
+struct SpatialInfo
+{
+    float x_;
+    float y_;
+    float angle_;
+    float scaleX_;
+    float scaleY_;
+    float alpha_;
+    int spin;
+
+    SpatialInfo(float x = 0.0f, float y = 0.0f, float angle = 0.0f, float scale_x = 1, float scale_y = 1, float a = 1, int spin = 1);
+    SpatialInfo UnmapFromParent(const SpatialInfo& parentInfo) const;
+    void Interpolate(const SpatialInfo& other, float t);
+};
+
+/// Spatial timeline key.
+struct SpatialTimelineKey : TimelineKey
+{
+    SpatialInfo info_;
+
+    SpatialTimelineKey(Timeline* timeline);
+    virtual ~SpatialTimelineKey();
+
+    virtual bool Load(const pugi::xml_node& node);
+    virtual void Interpolate(const TimelineKey& other, float t);
+    SpatialTimelineKey& operator=(const SpatialTimelineKey& rhs);
+};
+
+/// Bone timeline key.
+struct BoneTimelineKey : SpatialTimelineKey
+{
+    float length_;
+    float width_;
+
+    BoneTimelineKey(Timeline* timeline);
+    virtual ~BoneTimelineKey();
+
+    virtual ObjectType GetObjectType() const { return BONE; }
+    virtual TimelineKey* Clone() const;
+    virtual bool Load(const pugi::xml_node& node);
+    virtual void Interpolate(const TimelineKey& other, float t);
+    BoneTimelineKey& operator=(const BoneTimelineKey& rhs);
+};
+
+/// Sprite timeline key.
+struct SpriteTimelineKey : SpatialTimelineKey
+{
+    int folderId_;
+    int fileId_;
+    bool useDefaultPivot_;
+    float pivotX_;
+    float pivotY_;
+
+    /// Run time data.
+    int zIndex_;
+
+    SpriteTimelineKey(Timeline* timeline);
+    virtual ~SpriteTimelineKey();
+
+    virtual ObjectType GetObjectType() const { return SPRITE; }
+    virtual TimelineKey* Clone() const;
+    virtual bool Load(const pugi::xml_node& node);
+    virtual void Interpolate(const TimelineKey& other, float t);
+    SpriteTimelineKey& operator=(const SpriteTimelineKey& rhs);
+};
+
+}
+
+}

+ 313 - 0
Source/Atomic/Atomic2D/SpriterInstance2D.cpp

@@ -0,0 +1,313 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// 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 "../Precompiled.h"
+
+#include "../Graphics/DrawableEvents.h"
+#include "../Scene/Component.h"
+#include "../Scene/Node.h"
+#include "../Urho2D/SpriterInstance2D.h"
+
+#include <cmath>
+
+namespace Urho3D
+{
+
+namespace Spriter
+{
+
+SpriterInstance::SpriterInstance(Component* owner, SpriterData* spriteData) : 
+    owner_(owner),
+    spriterData_(spriteData),
+    entity_(0),
+    animation_(0)
+{
+}
+
+SpriterInstance::~SpriterInstance()
+{
+    Clear();
+
+    OnSetAnimation(0);
+    OnSetEntity(0);
+}
+
+bool SpriterInstance::SetEntity(int index)
+{
+    if (!spriterData_)
+        return false;
+
+    if (index < (int)spriterData_->entities_.Size())
+    {
+        OnSetEntity(spriterData_->entities_[index]);
+        return true;
+    }
+
+    return false;
+}
+
+bool SpriterInstance::SetEntity(const String& entityName)
+{
+    if (!spriterData_)
+        return false;
+
+    for (size_t i = 0; i < spriterData_->entities_.Size(); ++i)
+    {
+        if (spriterData_->entities_[i]->name_ == entityName)
+        {
+            OnSetEntity(spriterData_->entities_[i]);
+            return true;
+        }
+    }
+
+    return false;
+}   
+
+bool SpriterInstance::SetAnimation(int index, LoopMode loopMode)
+{
+    if (!entity_)
+        return false;
+
+    if (index < (int)entity_->animations_.Size())
+    {
+        OnSetAnimation(entity_->animations_[index], loopMode);
+        return true;
+    }
+
+    return false;
+}
+
+bool SpriterInstance::SetAnimation(const String& animationName, LoopMode loopMode)
+{
+    if (!entity_)
+        return false;
+
+    for (size_t i = 0; i < entity_->animations_.Size(); ++i)
+    {
+        if (entity_->animations_[i]->name_ == animationName)
+        {
+            OnSetAnimation(entity_->animations_[i], loopMode);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void SpriterInstance::setSpatialInfo(const SpatialInfo& spatialInfo)
+{
+    this->spatialInfo_ = spatialInfo;
+}
+
+void SpriterInstance::setSpatialInfo(float x, float y, float angle, float scaleX, float scaleY)
+{
+    spatialInfo_ = SpatialInfo(x, y, angle, scaleX, scaleY);
+}
+
+void SpriterInstance::Update(float deltaTime)
+{
+    if (!animation_)
+        return;
+
+    Clear();
+
+    float lastTime = currentTime_;
+    currentTime_ += deltaTime;
+    if (currentTime_ > animation_->length_)
+    {
+        bool sendFinishEvent = false;
+
+        if (looping_)
+        {
+            currentTime_ = fmod(currentTime_, animation_->length_);
+            sendFinishEvent = true;
+        }
+        else
+        {
+            currentTime_ = animation_->length_;
+            sendFinishEvent = lastTime != currentTime_;
+        }
+
+        if (sendFinishEvent && owner_)
+        {
+            Node* senderNode = owner_->GetNode();
+            if (senderNode)
+            {
+                using namespace AnimationFinished;
+
+                VariantMap& eventData = senderNode->GetEventDataMap();
+                eventData[P_NODE] = senderNode;
+                eventData[P_ANIMATION] = animation_;
+                eventData[P_NAME] = animation_->name_;
+                eventData[P_LOOPED] = looping_;
+    
+                senderNode->SendEvent(E_ANIMATIONFINISHED, eventData);
+            }
+        }
+    }
+
+    UpdateMainlineKey();
+    UpdateTimelineKeys();
+}
+
+void SpriterInstance::OnSetEntity(Entity* entity)
+{
+    if (entity == this->entity_)
+        return;
+
+    OnSetAnimation(0);
+
+    this->entity_ = entity;
+}
+
+void SpriterInstance::OnSetAnimation(Animation* animation, LoopMode loopMode)
+{
+    if (animation == this->animation_)
+        return;
+
+    animation_ = animation;
+    if (animation_)
+    {
+        if (loopMode == Default)
+            looping_ = animation_->looping_;
+        else if (loopMode == ForceLooped)
+            looping_ = true;
+        else
+            looping_ = false;
+    }
+        
+    currentTime_ = 0.0f;
+    Clear();        
+}
+
+void SpriterInstance::UpdateTimelineKeys()
+{
+    for (size_t i = 0; i < mainlineKey_->boneRefs_.Size(); ++i)
+    {
+        Ref* ref = mainlineKey_->boneRefs_[i];
+        BoneTimelineKey* timelineKey = (BoneTimelineKey*)GetTimelineKey(ref);
+        if (ref->parent_ >= 0)
+        {
+            timelineKey->info_ = timelineKey->info_.UnmapFromParent(timelineKeys_[ref->parent_]->info_);
+        }
+        else
+        {
+            timelineKey->info_ = timelineKey->info_.UnmapFromParent(spatialInfo_);
+        }
+        timelineKeys_.Push(timelineKey);
+    }
+
+    for (size_t i = 0; i < mainlineKey_->objectRefs_.Size(); ++i)
+    {
+        Ref* ref = mainlineKey_->objectRefs_[i];
+        SpriteTimelineKey* timelineKey = (SpriteTimelineKey*)GetTimelineKey(ref);
+            
+        if (ref->parent_ >= 0)
+        {
+            timelineKey->info_ = timelineKey->info_.UnmapFromParent(timelineKeys_[ref->parent_]->info_);
+        }
+        else
+        {
+            timelineKey->info_ = timelineKey->info_.UnmapFromParent(spatialInfo_);
+        }
+        
+        timelineKey->zIndex_ = ref->zIndex_;
+
+        timelineKeys_.Push(timelineKey);
+    }
+}
+
+void SpriterInstance::UpdateMainlineKey()
+{
+    const PODVector<MainlineKey*>& mainlineKeys = animation_->mainlineKeys_;
+    for (size_t i = 0; i < mainlineKeys.Size(); ++i)
+    {
+        if (mainlineKeys[i]->time_ <= currentTime_)
+        {
+            mainlineKey_ = mainlineKeys[i];
+        }
+
+        if (mainlineKeys[i]->time_ >= currentTime_)
+        {
+            break;
+        }
+    }
+
+    if (!mainlineKey_)
+    {
+        mainlineKey_ = mainlineKeys[0];
+    }
+}
+
+TimelineKey* SpriterInstance::GetTimelineKey(Ref* ref) const
+{
+    Timeline* timeline = animation_->timelines_[ref->timeline_];
+    TimelineKey* timelineKey = timeline->keys_[ref->key_]->Clone();
+    if (timeline->keys_.Size() == 1 || timelineKey->curveType_ == INSTANT)
+    {
+        return timelineKey;
+    }
+
+    size_t nextTimelineKeyIndex = ref->key_ + 1;
+    if (nextTimelineKeyIndex >= timeline->keys_.Size())
+    {
+        if (animation_->looping_)
+        {
+            nextTimelineKeyIndex = 0;
+        }
+        else
+        {
+            return timelineKey;
+        }
+    }
+
+    TimelineKey* nextTimelineKey = timeline->keys_[nextTimelineKeyIndex];
+        
+    float nextTimelineKeyTime = nextTimelineKey->time_;
+    if (nextTimelineKey->time_ < timelineKey->time_)
+    {
+        nextTimelineKeyTime += animation_->length_;
+    }
+
+    float t = timelineKey->GetTByCurveType(currentTime_, nextTimelineKeyTime);
+    timelineKey->Interpolate(*nextTimelineKey, t);
+
+    return timelineKey;
+}
+
+void SpriterInstance::Clear()
+{
+    mainlineKey_ = 0;
+
+    if (!timelineKeys_.Empty())
+    {
+        for (size_t i = 0; i < timelineKeys_.Size(); ++i)
+        {
+            delete timelineKeys_[i];
+        }
+        timelineKeys_.Clear();
+    }
+}
+
+}
+
+}

+ 112 - 0
Source/Atomic/Atomic2D/SpriterInstance2D.h

@@ -0,0 +1,112 @@
+//
+// Copyright (c) 2008-2016 the Urho3D project.
+//
+// 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 "../Urho2D/SpriterData2D.h"
+
+namespace Urho3D
+{
+
+class Component;
+
+namespace Spriter
+{
+
+/// Loop Mode.
+enum LoopMode
+{
+    Default = 0,
+    ForceLooped,
+    ForceClamped,
+};
+
+/// Spriter instance.
+class SpriterInstance
+{
+public:
+    /// Constructor with spriter data.
+    SpriterInstance(Component* owner, SpriterData* spriteData);
+    /// Destructor.
+    ~SpriterInstance();
+
+    /// Set current entity.
+    bool SetEntity(int index);
+    /// Set current entity.
+    bool SetEntity(const String& entityName);
+    /// Set current animation.
+    bool SetAnimation(int index, LoopMode loopMode = Default);
+    /// Set current animation.
+    bool SetAnimation(const String& animationName, LoopMode loopMode = Default);
+    /// Set root spatial info.
+    void setSpatialInfo(const SpatialInfo& spatialInfo);
+    /// Set root spatial info.
+    void setSpatialInfo(float x, float y, float angle, float scaleX, float scaleY);
+    /// Update animation.
+    void Update(float delta_time);
+
+    /// Return current entity.
+    Entity* GetEntity() const { return entity_; }
+    /// Return current animation.
+    Animation* GetAnimation() const { return animation_; }
+    /// Return root spatial info.
+    const SpatialInfo& GetSpatialInfo() const { return spatialInfo_; }
+    /// Return animation result timeline keys.
+    const PODVector<SpatialTimelineKey*>& GetTimelineKeys() const { return timelineKeys_; }
+
+private:
+    /// Handle set entity.
+    void OnSetEntity(Entity* entity);
+    /// Handle set animation.
+    void OnSetAnimation(Animation* animation, LoopMode loopMode = Default);
+    /// Update mainline key.
+    void UpdateMainlineKey();
+    /// Update timeline keys.
+    void UpdateTimelineKeys();
+    /// Get timeline key by ref.
+    TimelineKey* GetTimelineKey(Ref* ref) const;
+    /// Clear mainline key and timeline keys.
+    void Clear();
+
+    /// Parent component.
+    Component* owner_;
+    /// Spriter data.
+    SpriterData* spriterData_;
+    /// Current entity.
+    Entity* entity_;
+    /// Current animation.
+    Animation* animation_;
+    /// Looping.
+    bool looping_;
+    /// Root spatial info.
+    SpatialInfo spatialInfo_;
+    /// Current time.
+    float currentTime_;
+    /// Current mainline key.
+    MainlineKey* mainlineKey_;
+    /// Current timeline keys.
+    PODVector<SpatialTimelineKey*> timelineKeys_;
+};
+
+}
+
+}

+ 21 - 41
Source/Atomic/Atomic2D/StaticSprite2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -27,16 +27,16 @@
 #include "../Graphics/Texture2D.h"
 #include "../Graphics/Texture2D.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
-#include "../Atomic2D/Renderer2D.h"
-#include "../Atomic2D/Sprite2D.h"
-#include "../Atomic2D/StaticSprite2D.h"
+#include "../Urho2D/Renderer2D.h"
+#include "../Urho2D/Sprite2D.h"
+#include "../Urho2D/StaticSprite2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 extern const char* blendModeNames[];
 extern const char* blendModeNames[];
 
 
 StaticSprite2D::StaticSprite2D(Context* context) :
 StaticSprite2D::StaticSprite2D(Context* context) :
@@ -49,6 +49,7 @@ StaticSprite2D::StaticSprite2D(Context* context) :
     hotSpot_(0.5f, 0.5f)
     hotSpot_(0.5f, 0.5f)
 {
 {
     sourceBatches_.Resize(1);
     sourceBatches_.Resize(1);
+    sourceBatches_[0].owner_ = this;
 }
 }
 
 
 StaticSprite2D::~StaticSprite2D()
 StaticSprite2D::~StaticSprite2D()
@@ -57,17 +58,17 @@ StaticSprite2D::~StaticSprite2D()
 
 
 void StaticSprite2D::RegisterObject(Context* context)
 void StaticSprite2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<StaticSprite2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<StaticSprite2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Drawable2D);
-    MIXED_ACCESSOR_ATTRIBUTE("Sprite", GetSpriteAttr, SetSpriteAttr, ResourceRef, ResourceRef(Sprite2D::GetTypeStatic()),
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Drawable2D);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Sprite", GetSpriteAttr, SetSpriteAttr, ResourceRef, ResourceRef(Sprite2D::GetTypeStatic()),
         AM_DEFAULT);
         AM_DEFAULT);
-    ENUM_ACCESSOR_ATTRIBUTE("Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModeNames, BLEND_ALPHA, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Flip X", GetFlipX, SetFlipX, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Flip Y", GetFlipY, SetFlipY, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Color", GetColor, SetColor, Color, Color::WHITE, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Custom material", GetCustomMaterialAttr, SetCustomMaterialAttr, ResourceRef,
+    URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModeNames, BLEND_ALPHA, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Flip X", GetFlipX, SetFlipX, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Flip Y", GetFlipY, SetFlipY, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Color", GetColor, SetColor, Color, Color::WHITE, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Custom material", GetCustomMaterialAttr, SetCustomMaterialAttr, ResourceRef,
         ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
         ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
 }
 }
 
 
@@ -103,8 +104,6 @@ void StaticSprite2D::SetFlip(bool flipX, bool flipY)
     flipY_ = flipY;
     flipY_ = flipY;
     sourceBatchesDirty_ = true;
     sourceBatchesDirty_ = true;
 
 
-    OnFlipChanged();
-
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
@@ -211,27 +210,13 @@ ResourceRef StaticSprite2D::GetCustomMaterialAttr() const
 void StaticSprite2D::OnWorldBoundingBoxUpdate()
 void StaticSprite2D::OnWorldBoundingBoxUpdate()
 {
 {
     boundingBox_.Clear();
     boundingBox_.Clear();
+    worldBoundingBox_.Clear();
 
 
-    if (sprite_)
-    {
-        const IntRect& rectangle_ = sprite_->GetRectangle();
-        float width = (float)rectangle_.Width() * PIXEL_SIZE;     // Compute width and height in pixels
-        float height = (float)rectangle_.Height() * PIXEL_SIZE;
-
-        const Vector2& hotSpot = sprite_->GetHotSpot();
-        float hotSpotX = flipX_ ? (1.0f - hotSpot.x_) : hotSpot.x_;
-        float hotSpotY = flipY_ ? (1.0f - hotSpot.y_) : hotSpot.y_;
-
-        float leftX = -width * hotSpotX;
-        float rightX = width * (1.0f - hotSpotX);
-        float bottomY = -height * hotSpotY;
-        float topY = height * (1.0f - hotSpotY);
-
-        boundingBox_.Merge(Vector3(leftX, bottomY, 0.0f));
-        boundingBox_.Merge(Vector3(rightX, topY, 0.0f));
-    }
+    const Vector<SourceBatch2D>& sourceBatches = GetSourceBatches();
+    for (unsigned i = 0; i < sourceBatches[0].vertices_.Size(); ++i)
+        worldBoundingBox_.Merge(sourceBatches[0].vertices_[i].position_);
 
 
-    worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
+    boundingBox_ = worldBoundingBox_.Transformed(node_->GetWorldTransform().Inverse());
 }
 }
 
 
 void StaticSprite2D::OnDrawOrderChanged()
 void StaticSprite2D::OnDrawOrderChanged()
@@ -302,11 +287,6 @@ void StaticSprite2D::UpdateSourceBatches()
     sourceBatchesDirty_ = false;
     sourceBatchesDirty_ = false;
 }
 }
 
 
-void StaticSprite2D::OnFlipChanged()
-{
-
-}
-
 void StaticSprite2D::UpdateMaterial()
 void StaticSprite2D::UpdateMaterial()
 {
 {
     if (customMaterial_)
     if (customMaterial_)

+ 5 - 7
Source/Atomic/Atomic2D/StaticSprite2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,17 +22,17 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic2D/Drawable2D.h"
+#include "../Urho2D/Drawable2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class Sprite2D;
 class Sprite2D;
 
 
 /// Static sprite component.
 /// Static sprite component.
-class ATOMIC_API StaticSprite2D : public Drawable2D
+class URHO3D_API StaticSprite2D : public Drawable2D
 {
 {
-    OBJECT(StaticSprite2D);
+    URHO3D_OBJECT(StaticSprite2D, Drawable2D);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -106,8 +106,6 @@ protected:
     virtual void OnDrawOrderChanged();
     virtual void OnDrawOrderChanged();
     /// Update source batches.
     /// Update source batches.
     virtual void UpdateSourceBatches();
     virtual void UpdateSourceBatches();
-    /// Handle flip changed.
-    virtual void OnFlipChanged();
     /// Update material.
     /// Update material.
     void UpdateMaterial();
     void UpdateMaterial();
 
 

+ 16 - 20
Source/Atomic/Atomic2D/TileMap2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -27,17 +27,17 @@
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
-#include "../Atomic2D/TileMap2D.h"
-#include "../Atomic2D/TileMapLayer2D.h"
-#include "../Atomic2D/TmxFile2D.h"
+#include "../Urho2D/TileMap2D.h"
+#include "../Urho2D/TileMapLayer2D.h"
+#include "../Urho2D/TmxFile2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const float PIXEL_SIZE;
 extern const float PIXEL_SIZE;
-extern const char* ATOMIC2D_CATEGORY;
+extern const char* URHO2D_CATEGORY;
 
 
 TileMap2D::TileMap2D(Context* context) :
 TileMap2D::TileMap2D(Context* context) :
     Component(context)
     Component(context)
@@ -50,10 +50,10 @@ TileMap2D::~TileMap2D()
 
 
 void TileMap2D::RegisterObject(Context* context)
 void TileMap2D::RegisterObject(Context* context)
 {
 {
-    context->RegisterFactory<TileMap2D>(ATOMIC2D_CATEGORY);
+    context->RegisterFactory<TileMap2D>(URHO2D_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Tmx File", GetTmxFileAttr, SetTmxFileAttr, ResourceRef, ResourceRef(TmxFile2D::GetTypeStatic()),
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Tmx File", GetTmxFileAttr, SetTmxFileAttr, ResourceRef, ResourceRef(TmxFile2D::GetTypeStatic()),
         AM_DEFAULT);
         AM_DEFAULT);
 }
 }
 
 
@@ -85,6 +85,13 @@ void TileMap2D::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
         debug->AddLine(Vector2(mapW, mapH), Vector2(0.0f, mapH), color);
         debug->AddLine(Vector2(mapW, mapH), Vector2(0.0f, mapH), color);
         debug->AddLine(Vector2(0.0f, mapH), Vector2(0.0f, 0.0f), color);
         debug->AddLine(Vector2(0.0f, mapH), Vector2(0.0f, 0.0f), color);
         break;
         break;
+
+    case O_HEXAGONAL:
+        debug->AddLine(Vector2(0.0f, 0.0f), Vector2(mapW, 0.0f), color);
+        debug->AddLine(Vector2(mapW, 0.0f), Vector2(mapW, mapH), color);
+        debug->AddLine(Vector2(mapW, mapH), Vector2(0.0f, mapH), color);
+        debug->AddLine(Vector2(0.0f, mapH), Vector2(0.0f, 0.0f), color);
+        break;
     }
     }
 
 
     for (unsigned i = 0; i < layers_.Size(); ++i)
     for (unsigned i = 0; i < layers_.Size(); ++i)
@@ -157,17 +164,6 @@ TileMapLayer2D* TileMap2D::GetLayer(unsigned index) const
     return layers_[index];
     return layers_[index];
 }
 }
 
 
-TileMapLayer2D* TileMap2D::GetLayerByName(const String& name) const
-{
-    for (unsigned i = 0; i < layers_.Size(); i++)
-    {
-        if (layers_[i]->GetName() == name)
-            return layers_[i];
-    }
-
-    return 0;
-}
-
 Vector2 TileMap2D::TileIndexToPosition(int x, int y) const
 Vector2 TileMap2D::TileIndexToPosition(int x, int y) const
 {
 {
     return info_.TileIndexToPosition(x, y);
     return info_.TileIndexToPosition(x, y);

+ 5 - 8
Source/Atomic/Atomic2D/TileMap2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,18 +23,18 @@
 #pragma once
 #pragma once
 
 
 #include "../Scene/Component.h"
 #include "../Scene/Component.h"
-#include "../Atomic2D/TileMapDefs2D.h"
+#include "../Urho2D/TileMapDefs2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class TileMapLayer2D;
 class TileMapLayer2D;
 class TmxFile2D;
 class TmxFile2D;
 
 
 /// Tile map component.
 /// Tile map component.
-class ATOMIC_API TileMap2D : public Component
+class URHO3D_API TileMap2D : public Component
 {
 {
-    OBJECT(TileMap2D);
+    URHO3D_OBJECT(TileMap2D, Component);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -63,9 +63,6 @@ public:
 
 
     /// Return tile map layer at index.
     /// Return tile map layer at index.
     TileMapLayer2D* GetLayer(unsigned index) const;
     TileMapLayer2D* GetLayer(unsigned index) const;
-
-    TileMapLayer2D* GetLayerByName(const String& name) const;
-
     /// Convert tile index to position.
     /// Convert tile index to position.
     Vector2 TileIndexToPosition(int x, int y) const;
     Vector2 TileIndexToPosition(int x, int y) const;
     /// Convert position to tile index, if out of map return false.
     /// Convert position to tile index, if out of map return false.

+ 27 - 39
Source/Atomic/Atomic2D/TileMapDefs2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,17 +23,14 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Resource/XMLElement.h"
 #include "../Resource/XMLElement.h"
-#include "../Atomic2D/TileMapDefs2D.h"
-#include "../Atomic2D/CollisionBox2D.h"
+#include "../Resource/JSONFile.h"
+#include "../Urho2D/TileMapDefs2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-#include "../Scene/Node.h"
-#include "../Atomic2D/TmxFile2D.h"
-
-namespace Atomic
+namespace Urho3D
 {
 {
-extern ATOMIC_API const float PIXEL_SIZE;
+extern URHO3D_API const float PIXEL_SIZE;
 
 
 float TileMapInfo2D::GetMapWidth() const
 float TileMapInfo2D::GetMapWidth() const
 {
 {
@@ -44,6 +41,8 @@ float TileMapInfo2D::GetMapHeight() const
 {
 {
     if (orientation_ == O_STAGGERED)
     if (orientation_ == O_STAGGERED)
         return (height_ + 1) * 0.5f * tileHeight_;
         return (height_ + 1) * 0.5f * tileHeight_;
+    else if (orientation_ == O_HEXAGONAL)
+        return (height_) * 0.5f * (tileHeight_ + tileHeight_ * 0.5f);
     else
     else
         return height_ * tileHeight_;
         return height_ * tileHeight_;
 }
 }
@@ -62,6 +61,7 @@ Vector2 TileMapInfo2D::ConvertPosition(const Vector2& position) const
     case O_STAGGERED:
     case O_STAGGERED:
         return Vector2(position.x_ * PIXEL_SIZE, GetMapHeight() - position.y_ * PIXEL_SIZE);
         return Vector2(position.x_ * PIXEL_SIZE, GetMapHeight() - position.y_ * PIXEL_SIZE);
 
 
+    case O_HEXAGONAL:
     case O_ORTHOGONAL:
     case O_ORTHOGONAL:
     default:
     default:
         return Vector2(position.x_ * PIXEL_SIZE, GetMapHeight() - position.y_ * PIXEL_SIZE);
         return Vector2(position.x_ * PIXEL_SIZE, GetMapHeight() - position.y_ * PIXEL_SIZE);
@@ -83,6 +83,12 @@ Vector2 TileMapInfo2D::TileIndexToPosition(int x, int y) const
         else
         else
             return Vector2((x + 0.5f) * tileWidth_, (height_ - 1 - y) * 0.5f * tileHeight_);
             return Vector2((x + 0.5f) * tileWidth_, (height_ - 1 - y) * 0.5f * tileHeight_);
 
 
+    case O_HEXAGONAL:
+        if (y % 2 == 0)
+            return Vector2(x * tileWidth_, (height_ - 1 - y) * 0.75f * tileHeight_);
+        else
+            return Vector2((x + 0.5f) * tileWidth_, (height_ - 1 - y)  * 0.75f * tileHeight_);
+
     case O_ORTHOGONAL:
     case O_ORTHOGONAL:
     default:
     default:
         return Vector2(x * tileWidth_, (height_ - 1 - y) * tileHeight_);
         return Vector2(x * tileWidth_, (height_ - 1 - y) * tileHeight_);
@@ -97,10 +103,11 @@ bool TileMapInfo2D::PositionToTileIndex(int& x, int& y, const Vector2& position)
     {
     {
     case O_ISOMETRIC:
     case O_ISOMETRIC:
     {
     {
-        int x_sub_y = (int)(position.x_ * 2.0f / tileWidth_ + 1 - width_);
-        int x_add_y = (int)(height_ * 2.0f - position.y_ * 2.0f / tileHeight_ - 2.0f);
-        x = (x_sub_y - x_add_y) / 2;
-        y = (x_sub_y - x_add_y) / 2;
+        float ox = position.x_ / tileWidth_ - height_ * 0.5f;
+        float oy = position.y_ / tileHeight_;
+
+        x = (int)(width_ - oy + ox);
+        y = (int)(height_ - oy - ox);
     }
     }
         break;
         break;
 
 
@@ -113,6 +120,14 @@ bool TileMapInfo2D::PositionToTileIndex(int& x, int& y, const Vector2& position)
 
 
         break;
         break;
 
 
+    case O_HEXAGONAL:
+        y = (int)(height_ - 1 - position.y_ / 0.75f / tileHeight_);
+        if (y % 2 == 0)
+            x = (int)(position.x_ / tileWidth_);
+        else
+            x = (int)(position.x_ / tileWidth_ - 0.75f);
+        break;
+
     case O_ORTHOGONAL:
     case O_ORTHOGONAL:
     default:
     default:
         x = (int)(position.x_ / tileWidth_);
         x = (int)(position.x_ / tileWidth_);
@@ -163,10 +178,6 @@ Sprite2D* Tile2D::GetSprite() const
     return sprite_;
     return sprite_;
 }
 }
 
 
-TmxObjectGroup2D* Tile2D::GetObjectGroup() const
-{
-    return objectGroup_;
-}
 bool Tile2D::HasProperty(const String& name) const
 bool Tile2D::HasProperty(const String& name) const
 {
 {
     if (!propertySet_)
     if (!propertySet_)
@@ -218,27 +229,4 @@ const String& TileMapObject2D::GetProperty(const String& name) const
     return propertySet_->GetProperty(name);
     return propertySet_->GetProperty(name);
 }
 }
 
 
-bool TileMapObject2D::ValidCollisionShape() const
-{
-    if (objectType_ == OT_RECTANGLE)
-        return true;
-
-    return false;
-}
-
-CollisionShape2D* TileMapObject2D::CreateCollisionShape(Node *node) const
-{
-    CollisionShape2D* shape = NULL;
-
-    if (objectType_ == OT_RECTANGLE)
-    {
-        CollisionBox2D* box = node->CreateComponent<CollisionBox2D>();
-        box->SetSize(size_);
-        box->SetCenter(position_);
-        shape = box;
-    }
-
-    return shape;
-}
-
 }
 }

+ 10 - 28
Source/Atomic/Atomic2D/TileMapDefs2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,12 @@
 #pragma once
 #pragma once
 
 
 #include "../Container/RefCounted.h"
 #include "../Container/RefCounted.h"
-#include "../Atomic2D/Sprite2D.h"
+#include "../Urho2D/Sprite2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class XMLElement;
 class XMLElement;
-class TmxObjectGroup2D;
-class CollisionShape2D;
-class Node;
 
 
 /// Orientation.
 /// Orientation.
 enum Orientation2D
 enum Orientation2D
@@ -41,11 +38,13 @@ enum Orientation2D
     /// Isometric.
     /// Isometric.
     O_ISOMETRIC,
     O_ISOMETRIC,
     /// Staggered.
     /// Staggered.
-    O_STAGGERED
+    O_STAGGERED,
+    /// Hexagonal
+    O_HEXAGONAL
 };
 };
 
 
 /// Tile map information.
 /// Tile map information.
-struct ATOMIC_API TileMapInfo2D
+struct URHO3D_API TileMapInfo2D
 {
 {
     /// Orientation.
     /// Orientation.
     Orientation2D orientation_;
     Orientation2D orientation_;
@@ -101,11 +100,8 @@ enum TileMapObjectType2D
 };
 };
 
 
 /// Property set.
 /// Property set.
-class ATOMIC_API PropertySet2D : public RefCounted
+class URHO3D_API PropertySet2D : public RefCounted
 {
 {
-
-    REFCOUNTED(PropertySet2D)
-
 public:
 public:
     PropertySet2D();
     PropertySet2D();
     virtual ~PropertySet2D();
     virtual ~PropertySet2D();
@@ -123,10 +119,8 @@ protected:
 };
 };
 
 
 /// Tile define.
 /// Tile define.
-class ATOMIC_API Tile2D : public RefCounted
+class URHO3D_API Tile2D : public RefCounted
 {
 {
-    REFCOUNTED(Tile2D)
-
 public:
 public:
     /// Construct.
     /// Construct.
     Tile2D();
     Tile2D();
@@ -136,10 +130,6 @@ public:
 
 
     /// Return sprite.
     /// Return sprite.
     Sprite2D* GetSprite() const;
     Sprite2D* GetSprite() const;
-
-    /// Return Object Group.
-    TmxObjectGroup2D* GetObjectGroup() const;
-
     /// Return has property.
     /// Return has property.
     bool HasProperty(const String& name) const;
     bool HasProperty(const String& name) const;
     /// Return property.
     /// Return property.
@@ -154,16 +144,11 @@ private:
     SharedPtr<Sprite2D> sprite_;
     SharedPtr<Sprite2D> sprite_;
     /// Property set.
     /// Property set.
     SharedPtr<PropertySet2D> propertySet_;
     SharedPtr<PropertySet2D> propertySet_;
-    /// Object group (collision)
-    SharedPtr<TmxObjectGroup2D> objectGroup_;
-
 };
 };
 
 
 /// Tile map object.
 /// Tile map object.
-class ATOMIC_API TileMapObject2D : public RefCounted
+class URHO3D_API TileMapObject2D : public RefCounted
 {
 {
-    REFCOUNTED(TileMapObject2D)
-
 public:
 public:
     TileMapObject2D();
     TileMapObject2D();
 
 
@@ -197,9 +182,6 @@ public:
     /// Return property value.
     /// Return property value.
     const String& GetProperty(const String& name) const;
     const String& GetProperty(const String& name) const;
 
 
-    bool ValidCollisionShape() const;
-    CollisionShape2D* CreateCollisionShape(Node* node) const;
-
 private:
 private:
     friend class TmxObjectGroup2D;
     friend class TmxObjectGroup2D;
 
 

+ 7 - 41
Source/Atomic/Atomic2D/TileMapLayer2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,16 +26,14 @@
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
-#include "../Atomic2D/StaticSprite2D.h"
-#include "../Atomic2D/TileMap2D.h"
-#include "../Atomic2D/TileMapLayer2D.h"
-#include "../Atomic2D/TmxFile2D.h"
-
-#include "../Atomic2D/RigidBody2D.h"
+#include "../Urho2D/StaticSprite2D.h"
+#include "../Urho2D/TileMap2D.h"
+#include "../Urho2D/TileMapLayer2D.h"
+#include "../Urho2D/TmxFile2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 TileMapLayer2D::TileMapLayer2D(Context* context) :
 TileMapLayer2D::TileMapLayer2D(Context* context) :
@@ -311,30 +309,6 @@ void TileMapLayer2D::SetTileLayer(const TmxTileLayer2D* tileLayer)
             staticSprite->SetLayer(drawOrder_);
             staticSprite->SetLayer(drawOrder_);
             staticSprite->SetOrderInLayer(y * width + x);
             staticSprite->SetOrderInLayer(y * width + x);
 
 
-            // collision
-            RigidBody2D *body = NULL;
-            TmxObjectGroup2D* group = tile->GetObjectGroup();
-            if (group)
-            {
-                for (unsigned i = 0; i < group->GetNumObjects(); i++)
-                {
-                    TileMapObject2D* o = group->GetObject(i);
-
-                    if (o->ValidCollisionShape())
-                    {
-                        if (!body)
-                        {
-                            body = tileNode->CreateComponent<RigidBody2D>();
-                            body->SetBodyType(BT_STATIC);
-                        }
-
-                        o->CreateCollisionShape(tileNode);
-
-                    }
-                }
-
-            }
-
             nodes_[y * width + x] = tileNode;
             nodes_[y * width + x] = tileNode;
         }
         }
     }
     }
@@ -352,7 +326,7 @@ void TileMapLayer2D::SetObjectGroup(const TmxObjectGroup2D* objectGroup)
         const TileMapObject2D* object = objectGroup->GetObject(i);
         const TileMapObject2D* object = objectGroup->GetObject(i);
 
 
         // Create dummy node for all object
         // Create dummy node for all object
-        SharedPtr<Node> objectNode(GetNode()->CreateChild(object->GetName()));
+        SharedPtr<Node> objectNode(GetNode()->CreateChild("Object"));
         objectNode->SetTemporary(true);
         objectNode->SetTemporary(true);
         objectNode->SetPosition(object->GetPosition());
         objectNode->SetPosition(object->GetPosition());
 
 
@@ -393,12 +367,4 @@ void TileMapLayer2D::SetImageLayer(const TmxImageLayer2D* imageLayer)
     nodes_.Push(imageNode);
     nodes_.Push(imageNode);
 }
 }
 
 
-const String& TileMapLayer2D::GetName() const
-{
-    static String none("");
-    if (tmxLayer_)
-        return tmxLayer_->GetName();
-
-    return none;
-}
 }
 }

+ 5 - 7
Source/Atomic/Atomic2D/TileMapLayer2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,9 +23,9 @@
 #pragma once
 #pragma once
 
 
 #include "../Scene/Component.h"
 #include "../Scene/Component.h"
-#include "../Atomic2D/TileMapDefs2D.h"
+#include "../Urho2D/TileMapDefs2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class DebugRenderer;
 class DebugRenderer;
@@ -37,9 +37,9 @@ class TmxObjectGroup2D;
 class TmxTileLayer2D;
 class TmxTileLayer2D;
 
 
 /// Tile map component.
 /// Tile map component.
-class ATOMIC_API TileMapLayer2D : public Component
+class URHO3D_API TileMapLayer2D : public Component
 {
 {
-    OBJECT(TileMapLayer2D);
+    URHO3D_OBJECT(TileMapLayer2D, Component);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -97,8 +97,6 @@ public:
     /// Return image node (for image layer only).
     /// Return image node (for image layer only).
     Node* GetImageNode() const;
     Node* GetImageNode() const;
 
 
-    const String& GetName() const;
-
 private:
 private:
     /// Set tile layer.
     /// Set tile layer.
     void SetTileLayer(const TmxTileLayer2D* tileLayer);
     void SetTileLayer(const TmxTileLayer2D* tileLayer);

+ 98 - 123
Source/Atomic/Atomic2D/TmxFile2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -28,12 +28,12 @@
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/XMLFile.h"
 #include "../Resource/XMLFile.h"
-#include "../Atomic2D/Sprite2D.h"
-#include "../Atomic2D/TmxFile2D.h"
+#include "../Urho2D/Sprite2D.h"
+#include "../Urho2D/TmxFile2D.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const float PIXEL_SIZE;
 extern const float PIXEL_SIZE;
@@ -97,13 +97,13 @@ bool TmxTileLayer2D::Load(const XMLElement& element, const TileMapInfo2D& info)
     XMLElement dataElem = element.GetChild("data");
     XMLElement dataElem = element.GetChild("data");
     if (!dataElem)
     if (!dataElem)
     {
     {
-        LOGERROR("Could not find data in layer");
+        URHO3D_LOGERROR("Could not find data in layer");
         return false;
         return false;
     }
     }
 
 
     if (dataElem.HasAttribute("encoding") && dataElem.GetAttribute("encoding") != "xml")
     if (dataElem.HasAttribute("encoding") && dataElem.GetAttribute("encoding") != "xml")
     {
     {
-        LOGERROR("Encoding not support now");
+        URHO3D_LOGERROR("Encoding not support now");
         return false;
         return false;
     }
     }
 
 
@@ -124,7 +124,6 @@ bool TmxTileLayer2D::Load(const XMLElement& element, const TileMapInfo2D& info)
                 tile->gid_ = gid;
                 tile->gid_ = gid;
                 tile->sprite_ = tmxFile_->GetTileSprite(gid);
                 tile->sprite_ = tmxFile_->GetTileSprite(gid);
                 tile->propertySet_ = tmxFile_->GetTilePropertySet(gid);
                 tile->propertySet_ = tmxFile_->GetTilePropertySet(gid);
-                tile->objectGroup_ = tmxFile_->GetTileObjectGroup(gid);
                 tiles_[y * width_ + x] = tile;
                 tiles_[y * width_ + x] = tile;
             }
             }
 
 
@@ -151,7 +150,7 @@ TmxObjectGroup2D::TmxObjectGroup2D(TmxFile2D* tmxFile) :
 {
 {
 }
 }
 
 
-bool TmxObjectGroup2D::Load(const XMLElement& element, const TileMapInfo2D& info, bool local)
+bool TmxObjectGroup2D::Load(const XMLElement& element, const TileMapInfo2D& info)
 {
 {
     LoadInfo(element);
     LoadInfo(element);
 
 
@@ -161,82 +160,71 @@ bool TmxObjectGroup2D::Load(const XMLElement& element, const TileMapInfo2D& info
 
 
         if (objectElem.HasAttribute("name"))
         if (objectElem.HasAttribute("name"))
             object->name_ = objectElem.GetAttribute("name");
             object->name_ = objectElem.GetAttribute("name");
-        else
-            object->name_ = "Object";
         if (objectElem.HasAttribute("type"))
         if (objectElem.HasAttribute("type"))
             object->type_ = objectElem.GetAttribute("type");
             object->type_ = objectElem.GetAttribute("type");
 
 
-        Vector2 position(objectElem.GetFloat("x"), objectElem.GetFloat("y"));
+        if (objectElem.HasAttribute("gid"))
+            object->objectType_ = OT_TILE;
+        else if (objectElem.HasChild("polygon"))
+            object->objectType_ = OT_POLYGON;
+        else if (objectElem.HasChild("polyline"))
+            object->objectType_ = OT_POLYLINE;
+        else if (objectElem.HasChild("ellipse"))
+            object->objectType_ = OT_ELLIPSE;
+        else
+            object->objectType_ = OT_RECTANGLE;
 
 
-        if (objectElem.HasAttribute("width") || objectElem.HasAttribute("height"))
+        const Vector2 position(objectElem.GetFloat("x"), objectElem.GetFloat("y"));
+        const Vector2 size(objectElem.GetFloat("width"), objectElem.GetFloat("height"));
+
+        switch (object->objectType_)
         {
         {
-            if (!objectElem.HasChild("ellipse"))
-                object->objectType_ = OT_RECTANGLE;
-            else
-                object->objectType_ = OT_ELLIPSE;
+        case OT_RECTANGLE:
+        case OT_ELLIPSE:
+            object->position_ = info.ConvertPosition(Vector2(position.x_, position.y_ + size.y_));
+            object->size_ = Vector2(size.x_ * PIXEL_SIZE, size.y_ * PIXEL_SIZE);
+            break;
 
 
-            Vector2 size(objectElem.GetFloat("width"), objectElem.GetFloat("height"));
+        case OT_TILE:
+            object->position_ = info.ConvertPosition(position);
+            object->gid_ = objectElem.GetInt("gid");
+            object->sprite_ = tmxFile_->GetTileSprite(object->gid_);
 
 
-            if (!local)
+            if (objectElem.HasAttribute("width") || objectElem.HasAttribute("height"))
             {
             {
-                object->position_ = info.ConvertPosition(Vector2(position.x_, position.y_ + size.y_));
                 object->size_ = Vector2(size.x_ * PIXEL_SIZE, size.y_ * PIXEL_SIZE);
                 object->size_ = Vector2(size.x_ * PIXEL_SIZE, size.y_ * PIXEL_SIZE);
             }
             }
-            else
+            else if (object->sprite_)
             {
             {
-                object->size_ = Vector2(size.x_ * PIXEL_SIZE, size.y_ * PIXEL_SIZE);
-
-                position.x_ *= PIXEL_SIZE;
-                position.y_ *= PIXEL_SIZE;
-
-                position.x_ = position.x_ + object->size_.x_ / 2.0f;
-                position.y_ = position.y_ + object->size_.y_ / 2.0f;
-
-                position.y_ = info.tileHeight_  - position.y_;
-
-                object->position_ = position;
+                IntVector2 spriteSize = object->sprite_->GetRectangle().Size();
+                object->size_ = Vector2(spriteSize.x_, spriteSize.y_);
             }
             }
+            break;
 
 
-        }
-        else if (objectElem.HasAttribute("gid"))
-        {
-            object->objectType_ = OT_TILE;
-            object->position_ = info.ConvertPosition(position);
-            object->gid_ = objectElem.GetInt("gid");
-            object->sprite_ = tmxFile_->GetTileSprite(object->gid_);
-        }
-        else
-        {
-            Vector<String> points;
-
-            if (objectElem.HasChild("polygon"))
+        case OT_POLYGON:
+        case OT_POLYLINE:
             {
             {
-                object->objectType_ = OT_POLYGON;
+                Vector<String> points;
 
 
-                XMLElement polygonElem = objectElem.GetChild("polygon");
+                const char* name = object->objectType_ == OT_POLYGON ? "polygon" : "polyline";
+                XMLElement polygonElem = objectElem.GetChild(name);
                 points = polygonElem.GetAttribute("points").Split(' ');
                 points = polygonElem.GetAttribute("points").Split(' ');
-            }
-            else if (objectElem.HasChild("polyline"))
-            {
-                object->objectType_ = OT_POLYLINE;
 
 
-                XMLElement polylineElem = objectElem.GetChild("polyline");
-                points = polylineElem.GetAttribute("points").Split(' ');
-            }
-            else
-                return false;
-
-            if (points.Size() <= 1)
-                continue;
+                if (points.Size() <= 1)
+                    continue;
 
 
-            object->points_.Resize(points.Size());
+                object->points_.Resize(points.Size());
 
 
-            for (unsigned i = 0; i < points.Size(); ++i)
-            {
-                points[i].Replace(',', ' ');
-                Vector2 point = position + ToVector2(points[i]);
-                object->points_[i] = info.ConvertPosition(point);
+                for (unsigned i = 0; i < points.Size(); ++i)
+                {
+                    points[i].Replace(',', ' ');
+                    Vector2 point = position + ToVector2(points[i]);
+                    object->points_[i] = info.ConvertPosition(point);
+                }
             }
             }
+            break;
+
+        default: break;
         }
         }
 
 
         if (objectElem.HasChild("properties"))
         if (objectElem.HasChild("properties"))
@@ -282,7 +270,7 @@ bool TmxImageLayer2D::Load(const XMLElement& element, const TileMapInfo2D& info)
     SharedPtr<Texture2D> texture(cache->GetResource<Texture2D>(textureFilePath));
     SharedPtr<Texture2D> texture(cache->GetResource<Texture2D>(textureFilePath));
     if (!texture)
     if (!texture)
     {
     {
-        LOGERROR("Could not load texture " + textureFilePath);
+        URHO3D_LOGERROR("Could not load texture " + textureFilePath);
         return false;
         return false;
     }
     }
 
 
@@ -310,6 +298,8 @@ TmxFile2D::TmxFile2D(Context* context) :
 
 
 TmxFile2D::~TmxFile2D()
 TmxFile2D::~TmxFile2D()
 {
 {
+    for (unsigned i = 0; i < layers_.Size(); ++i)
+        delete layers_[i];
 }
 }
 
 
 void TmxFile2D::RegisterObject(Context* context)
 void TmxFile2D::RegisterObject(Context* context)
@@ -325,7 +315,7 @@ bool TmxFile2D::BeginLoad(Deserializer& source)
     loadXMLFile_ = new XMLFile(context_);
     loadXMLFile_ = new XMLFile(context_);
     if (!loadXMLFile_->Load(source))
     if (!loadXMLFile_->Load(source))
     {
     {
-        LOGERROR("Load XML failed " + source.GetName());
+        URHO3D_LOGERROR("Load XML failed " + source.GetName());
         loadXMLFile_.Reset();
         loadXMLFile_.Reset();
         return false;
         return false;
     }
     }
@@ -333,7 +323,7 @@ bool TmxFile2D::BeginLoad(Deserializer& source)
     XMLElement rootElem = loadXMLFile_->GetRoot("map");
     XMLElement rootElem = loadXMLFile_->GetRoot("map");
     if (!rootElem)
     if (!rootElem)
     {
     {
-        LOGERROR("Invalid tmx file " + source.GetName());
+        URHO3D_LOGERROR("Invalid tmx file " + source.GetName());
         loadXMLFile_.Reset();
         loadXMLFile_.Reset();
         return false;
         return false;
     }
     }
@@ -384,7 +374,7 @@ bool TmxFile2D::EndLoad()
     String version = rootElem.GetAttribute("version");
     String version = rootElem.GetAttribute("version");
     if (version != "1.0")
     if (version != "1.0")
     {
     {
-        LOGERROR("Invalid version");
+        URHO3D_LOGERROR("Invalid version");
         return false;
         return false;
     }
     }
 
 
@@ -395,9 +385,11 @@ bool TmxFile2D::EndLoad()
         info_.orientation_ = O_ISOMETRIC;
         info_.orientation_ = O_ISOMETRIC;
     else if (orientation == "staggered")
     else if (orientation == "staggered")
         info_.orientation_ = O_STAGGERED;
         info_.orientation_ = O_STAGGERED;
+    else if (orientation == "hexagonal")
+        info_.orientation_ = O_HEXAGONAL;
     else
     else
     {
     {
-        LOGERROR("Unsupported orientation type " + orientation);
+        URHO3D_LOGERROR("Unsupported orientation type " + orientation);
         return false;
         return false;
     }
     }
 
 
@@ -406,6 +398,8 @@ bool TmxFile2D::EndLoad()
     info_.tileWidth_ = rootElem.GetFloat("tilewidth") * PIXEL_SIZE;
     info_.tileWidth_ = rootElem.GetFloat("tilewidth") * PIXEL_SIZE;
     info_.tileHeight_ = rootElem.GetFloat("tileheight") * PIXEL_SIZE;
     info_.tileHeight_ = rootElem.GetFloat("tileheight") * PIXEL_SIZE;
 
 
+    for (unsigned i = 0; i < layers_.Size(); ++i)
+        delete layers_[i];
     layers_.Clear();
     layers_.Clear();
 
 
     for (XMLElement childElement = rootElem.GetChild(); childElement; childElement = childElement.GetNext())
     for (XMLElement childElement = rootElem.GetChild(); childElement; childElement = childElement.GetNext())
@@ -416,24 +410,25 @@ bool TmxFile2D::EndLoad()
             ret = LoadTileSet(childElement);
             ret = LoadTileSet(childElement);
         else if (name == "layer")
         else if (name == "layer")
         {
         {
-            SharedPtr<TmxTileLayer2D> tileLayer (new TmxTileLayer2D(this));
+            TmxTileLayer2D* tileLayer = new TmxTileLayer2D(this);
             ret = tileLayer->Load(childElement, info_);
             ret = tileLayer->Load(childElement, info_);
 
 
-            layers_.Push((SharedPtr<TmxLayer2D>)(tileLayer));
+            layers_.Push(tileLayer);
         }
         }
         else if (name == "objectgroup")
         else if (name == "objectgroup")
         {
         {
-            SharedPtr<TmxObjectGroup2D> objectGroup (new TmxObjectGroup2D(this));
+            TmxObjectGroup2D* objectGroup = new TmxObjectGroup2D(this);
             ret = objectGroup->Load(childElement, info_);
             ret = objectGroup->Load(childElement, info_);
 
 
-            layers_.Push((SharedPtr<TmxLayer2D>)(objectGroup));
+            layers_.Push(objectGroup);
+
         }
         }
         else if (name == "imagelayer")
         else if (name == "imagelayer")
         {
         {
-            SharedPtr<TmxImageLayer2D> imageLayer (new TmxImageLayer2D(this));
+            TmxImageLayer2D* imageLayer = new TmxImageLayer2D(this);
             ret = imageLayer->Load(childElement, info_);
             ret = imageLayer->Load(childElement, info_);
 
 
-            layers_.Push((SharedPtr<TmxLayer2D>)(imageLayer));
+            layers_.Push(imageLayer);
         }
         }
 
 
         if (!ret)
         if (!ret)
@@ -449,6 +444,31 @@ bool TmxFile2D::EndLoad()
     return true;
     return true;
 }
 }
 
 
+bool TmxFile2D::SetInfo(Orientation2D orientation, int width, int height, float tileWidth, float tileHeight)
+{
+    if (layers_.Size() > 0)
+        return false;
+    info_.orientation_ = orientation;
+    info_.width_ = width;
+    info_.height_ = height;
+    info_.tileWidth_ = tileWidth * PIXEL_SIZE;
+    info_.tileHeight_ = tileHeight * PIXEL_SIZE;
+    return true;
+}
+
+void TmxFile2D::AddLayer(unsigned index, TmxLayer2D *layer)
+{
+    if (index > layers_.Size())
+        layers_.Push(layer);
+    else // index <= layers_.size()
+        layers_.Insert(index, layer);
+}
+
+void TmxFile2D::AddLayer(TmxLayer2D *layer)
+{
+    layers_.Push(layer);
+}
+
 Sprite2D* TmxFile2D::GetTileSprite(int gid) const
 Sprite2D* TmxFile2D::GetTileSprite(int gid) const
 {
 {
     HashMap<int, SharedPtr<Sprite2D> >::ConstIterator i = gidToSpriteMapping_.Find(gid);
     HashMap<int, SharedPtr<Sprite2D> >::ConstIterator i = gidToSpriteMapping_.Find(gid);
@@ -466,14 +486,6 @@ PropertySet2D* TmxFile2D::GetTilePropertySet(int gid) const
     return i->second_;
     return i->second_;
 }
 }
 
 
-TmxObjectGroup2D* TmxFile2D::GetTileObjectGroup(int gid) const
-{
-    HashMap<int, SharedPtr<TmxObjectGroup2D> >::ConstIterator i = gidToObjectGroupMapping_.Find(gid);
-    if (i == gidToObjectGroupMapping_.End())
-        return 0;
-    return i->second_;
-}
-
 const TmxLayer2D* TmxFile2D::GetLayer(unsigned index) const
 const TmxLayer2D* TmxFile2D::GetLayer(unsigned index) const
 {
 {
     if (index >= layers_.Size())
     if (index >= layers_.Size())
@@ -482,7 +494,6 @@ const TmxLayer2D* TmxFile2D::GetLayer(unsigned index) const
     return layers_[index];
     return layers_[index];
 }
 }
 
 
-
 SharedPtr<XMLFile> TmxFile2D::LoadTSXFile(const String& source)
 SharedPtr<XMLFile> TmxFile2D::LoadTSXFile(const String& source)
 {
 {
     String tsxFilePath = GetParentPath(GetName()) + source;
     String tsxFilePath = GetParentPath(GetName()) + source;
@@ -490,7 +501,7 @@ SharedPtr<XMLFile> TmxFile2D::LoadTSXFile(const String& source)
     SharedPtr<XMLFile> tsxXMLFile(new XMLFile(context_));
     SharedPtr<XMLFile> tsxXMLFile(new XMLFile(context_));
     if (!tsxFile || !tsxXMLFile->Load(*tsxFile))
     if (!tsxFile || !tsxXMLFile->Load(*tsxFile))
     {
     {
-        LOGERROR("Load TSX file failed " + tsxFilePath);
+        URHO3D_LOGERROR("Load TSX file failed " + tsxFilePath);
         return SharedPtr<XMLFile>();
         return SharedPtr<XMLFile>();
     }
     }
 
 
@@ -512,7 +523,7 @@ bool TmxFile2D::LoadTileSet(const XMLElement& element)
             if (!tsxXMLFile)
             if (!tsxXMLFile)
                 return false;
                 return false;
 
 
-            // Add to mapping to avoid release
+            // Add to napping to avoid release
             tsxXMLFiles_[source] = tsxXMLFile;
             tsxXMLFiles_[source] = tsxXMLFile;
 
 
             tileSetElem = tsxXMLFile->GetRoot("tileset");
             tileSetElem = tsxXMLFile->GetRoot("tileset");
@@ -529,13 +540,10 @@ bool TmxFile2D::LoadTileSet(const XMLElement& element)
     SharedPtr<Texture2D> texture(cache->GetResource<Texture2D>(textureFilePath));
     SharedPtr<Texture2D> texture(cache->GetResource<Texture2D>(textureFilePath));
     if (!texture)
     if (!texture)
     {
     {
-        LOGERROR("Could not load texture " + textureFilePath);
+        URHO3D_LOGERROR("Could not load texture " + textureFilePath);
         return false;
         return false;
     }
     }
 
 
-    // reduces border tile sample errors
-    texture->SetFilterMode(FILTER_NEAREST);
-
     tileSetTextures_.Push(texture);
     tileSetTextures_.Push(texture);
 
 
     int tileWidth = tileSetElem.GetInt("tilewidth");
     int tileWidth = tileSetElem.GetInt("tilewidth");
@@ -576,39 +584,6 @@ bool TmxFile2D::LoadTileSet(const XMLElement& element)
             propertySet->Load(tileElem.GetChild("properties"));
             propertySet->Load(tileElem.GetChild("properties"));
             gidToPropertySetMapping_[firstgid + tileElem.GetInt("id")] = propertySet;
             gidToPropertySetMapping_[firstgid + tileElem.GetInt("id")] = propertySet;
         }
         }
-
-        // collision information
-        if (tileElem.HasChild("objectgroup"))
-        {
-            // ok, when you use multiple tile sets the "info"
-            // numbers can be different, this is a bit of a hack
-            // if something is wrong, look here... may have to need
-            // to refactor "info" to be per set
-
-            float _tileWidth = info_.tileWidth_;
-            float _tileHeight = info_.tileHeight_;
-
-            info_.tileHeight_ = (float) tileHeight * PIXEL_SIZE;
-            info_.tileWidth_ = (float) tileWidth * PIXEL_SIZE;
-
-            XMLElement groupElem = tileElem.GetChild("objectgroup");
-            TmxObjectGroup2D* objectGroup = new TmxObjectGroup2D(this);
-            if (!objectGroup->Load(groupElem, info_, true))
-            {
-                LOGERROR("Could not load objectgroup");
-                objectGroup->ReleaseRef();
-            }
-            else
-            {
-                gidToObjectGroupMapping_[firstgid + tileElem.GetInt("id")] = objectGroup;
-            }
-
-            info_.tileWidth_ = _tileWidth;
-            info_.tileHeight_ = _tileHeight;
-
-
-        }
-
     }
     }
 
 
     return true;
     return true;

+ 20 - 22
Source/Atomic/Atomic2D/TmxFile2D.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,9 +23,9 @@
 #pragma once
 #pragma once
 
 
 #include "../Resource/Resource.h"
 #include "../Resource/Resource.h"
-#include "../Atomic2D/TileMapDefs2D.h"
+#include "../Urho2D/TileMapDefs2D.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class Sprite2D;
 class Sprite2D;
@@ -37,10 +37,8 @@ class XMLFile;
 /// Tmx layer.
 /// Tmx layer.
 class TmxLayer2D : public RefCounted
 class TmxLayer2D : public RefCounted
 {
 {
-    REFCOUNTED(TmxLayer2D)
-
 public:
 public:
-    TmxLayer2D(TmxFile2D* tmxFile = 0, TileMapLayerType2D type = LT_TILE_LAYER);
+    TmxLayer2D(TmxFile2D* tmxFile, TileMapLayerType2D type);
     virtual ~TmxLayer2D();
     virtual ~TmxLayer2D();
 
 
     /// Return tmx file.
     /// Return tmx file.
@@ -92,10 +90,8 @@ protected:
 /// Tmx tile layer.
 /// Tmx tile layer.
 class TmxTileLayer2D : public TmxLayer2D
 class TmxTileLayer2D : public TmxLayer2D
 {
 {
-    REFCOUNTED(TmxTileLayer2D)
-
 public:
 public:
-    TmxTileLayer2D(TmxFile2D* tmxFile = 0);
+    TmxTileLayer2D(TmxFile2D* tmxFile);
 
 
     /// Load from XML element.
     /// Load from XML element.
     bool Load(const XMLElement& element, const TileMapInfo2D& info);
     bool Load(const XMLElement& element, const TileMapInfo2D& info);
@@ -110,13 +106,11 @@ protected:
 /// Tmx image layer.
 /// Tmx image layer.
 class TmxObjectGroup2D : public TmxLayer2D
 class TmxObjectGroup2D : public TmxLayer2D
 {
 {
-    REFCOUNTED(TmxObjectGroup2D)
-
 public:
 public:
     TmxObjectGroup2D(TmxFile2D* tmxFile);
     TmxObjectGroup2D(TmxFile2D* tmxFile);
 
 
     /// Load from XML element.
     /// Load from XML element.
-    bool Load(const XMLElement& element, const TileMapInfo2D& info, bool local = false);
+    bool Load(const XMLElement& element, const TileMapInfo2D& info);
 
 
     /// Return number of objects.
     /// Return number of objects.
     unsigned GetNumObjects() const { return objects_.Size(); }
     unsigned GetNumObjects() const { return objects_.Size(); }
@@ -132,8 +126,6 @@ private:
 /// Tmx image layer.
 /// Tmx image layer.
 class TmxImageLayer2D : public TmxLayer2D
 class TmxImageLayer2D : public TmxLayer2D
 {
 {
-    REFCOUNTED(TmxImageLayer2D)
-
 public:
 public:
     TmxImageLayer2D(TmxFile2D* tmxFile);
     TmxImageLayer2D(TmxFile2D* tmxFile);
 
 
@@ -159,9 +151,9 @@ private:
 };
 };
 
 
 /// Tile map file.
 /// Tile map file.
-class ATOMIC_API TmxFile2D : public Resource
+class URHO3D_API TmxFile2D : public Resource
 {
 {
-    OBJECT(TmxFile2D);
+    URHO3D_OBJECT(TmxFile2D, Resource);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -176,15 +168,23 @@ public:
     /// Finish resource loading. Always called from the main thread. Return true if successful.
     /// Finish resource loading. Always called from the main thread. Return true if successful.
     virtual bool EndLoad();
     virtual bool EndLoad();
 
 
-    /// Return information.
+    /// Set Tilemap information.
+    bool SetInfo(Orientation2D orientation, int width, int height, float tileWidth, float tileHeight);
+
+    /// Add layer at index, if index > number of layers then append to end.
+    void AddLayer(unsigned index, TmxLayer2D *layer);
+
+    /// Append layer to end.
+    void AddLayer(Urho3D::TmxLayer2D* layer);
+
+    /// Return Tilemap information.
     const TileMapInfo2D& GetInfo() const { return info_; }
     const TileMapInfo2D& GetInfo() const { return info_; }
 
 
     /// Return tile sprite by gid, if not exist return 0.
     /// Return tile sprite by gid, if not exist return 0.
     Sprite2D* GetTileSprite(int gid) const;
     Sprite2D* GetTileSprite(int gid) const;
+
     /// Return tile property set by gid, if not exist return 0.
     /// Return tile property set by gid, if not exist return 0.
     PropertySet2D* GetTilePropertySet(int gid) const;
     PropertySet2D* GetTilePropertySet(int gid) const;
-    /// Return tile object group by gid, if not exist return 0.
-    TmxObjectGroup2D* GetTileObjectGroup(int gid) const;
 
 
     /// Return number of layers.
     /// Return number of layers.
     unsigned GetNumLayers() const { return layers_.Size(); }
     unsigned GetNumLayers() const { return layers_.Size(); }
@@ -210,10 +210,8 @@ private:
     HashMap<int, SharedPtr<Sprite2D> > gidToSpriteMapping_;
     HashMap<int, SharedPtr<Sprite2D> > gidToSpriteMapping_;
     /// Gid to tile property set mapping.
     /// Gid to tile property set mapping.
     HashMap<int, SharedPtr<PropertySet2D> > gidToPropertySetMapping_;
     HashMap<int, SharedPtr<PropertySet2D> > gidToPropertySetMapping_;
-    /// Gid to tile objectgroup  mapping.
-    HashMap<int, SharedPtr<TmxObjectGroup2D> > gidToObjectGroupMapping_;
     /// Layers.
     /// Layers.
-    Vector<SharedPtr<TmxLayer2D>> layers_;
+    Vector<TmxLayer2D*> layers_;
 };
 };
 
 
 }
 }

+ 142 - 133
Source/Atomic/Atomic3D/AnimatedModel.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +24,9 @@
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
-#include "../Atomic3D/AnimatedModel.h"
-#include "../Atomic3D/Animation.h"
-#include "../Atomic3D/AnimationState.h"
+#include "../Graphics/AnimatedModel.h"
+#include "../Graphics/Animation.h"
+#include "../Graphics/AnimationState.h"
 #include "../Graphics/Batch.h"
 #include "../Graphics/Batch.h"
 #include "../Graphics/Camera.h"
 #include "../Graphics/Camera.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/DebugRenderer.h"
@@ -44,7 +44,7 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const char* GEOMETRY_CATEGORY;
 extern const char* GEOMETRY_CATEGORY;
@@ -56,8 +56,6 @@ static bool CompareAnimationOrder(const SharedPtr<AnimationState>& lhs, const Sh
 
 
 static const unsigned MAX_ANIMATION_STATES = 256;
 static const unsigned MAX_ANIMATION_STATES = 256;
 
 
-bool AnimatedModel::boneCreationEnabled_ = true;
-
 AnimatedModel::AnimatedModel(Context* context) :
 AnimatedModel::AnimatedModel(Context* context) :
     StaticModel(context),
     StaticModel(context),
     animationLodFrameNumber_(0),
     animationLodFrameNumber_(0),
@@ -94,33 +92,25 @@ void AnimatedModel::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<AnimatedModel>(GEOMETRY_CATEGORY);
     context->RegisterFactory<AnimatedModel>(GEOMETRY_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Material", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()),
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Material", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()),
         AM_DEFAULT);
         AM_DEFAULT);
-    ATTRIBUTE("Is Occluder", bool, occluder_, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Update When Invisible", GetUpdateInvisible, SetUpdateInvisible, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Drawable);
-    MIXED_ACCESSOR_ATTRIBUTE("Bone Animation Enabled", GetBonesEnabledAttr, SetBonesEnabledAttr, VariantVector,
+    URHO3D_ATTRIBUTE("Is Occluder", bool, occluder_, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Update When Invisible", GetUpdateInvisible, SetUpdateInvisible, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Bone Animation Enabled", GetBonesEnabledAttr, SetBonesEnabledAttr, VariantVector,
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
-    MIXED_ACCESSOR_ATTRIBUTE("Animation States", GetAnimationStatesAttr, SetAnimationStatesAttr, VariantVector,
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Animation States", GetAnimationStatesAttr, SetAnimationStatesAttr, VariantVector,
         Variant::emptyVariantVector, AM_FILE);
         Variant::emptyVariantVector, AM_FILE);
-    ACCESSOR_ATTRIBUTE("Morphs", GetMorphsAttr, SetMorphsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
+    URHO3D_ACCESSOR_ATTRIBUTE("Morphs", GetMorphsAttr, SetMorphsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
         AM_DEFAULT | AM_NOEDIT);
         AM_DEFAULT | AM_NOEDIT);
-
-    // ATOMIC BEGIN
-
-    ACCESSOR_ATTRIBUTE("Geometry Enabled", GetGeometryEnabledAttr, SetGeometryEnabledAttr, VariantVector,
-        Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
-
-    // ATOMIC END
-
 }
 }
 
 
 bool AnimatedModel::Load(Deserializer& source, bool setInstanceDefault)
 bool AnimatedModel::Load(Deserializer& source, bool setInstanceDefault)
@@ -141,6 +131,15 @@ bool AnimatedModel::LoadXML(const XMLElement& source, bool setInstanceDefault)
     return success;
     return success;
 }
 }
 
 
+bool AnimatedModel::LoadJSON(const JSONValue& source, bool setInstanceDefault)
+{
+    loading_ = true;
+    bool success = Component::LoadJSON(source, setInstanceDefault);
+    loading_ = false;
+
+    return success;
+}
+
 void AnimatedModel::ApplyAttributes()
 void AnimatedModel::ApplyAttributes()
 {
 {
     if (assignBonesPending_)
     if (assignBonesPending_)
@@ -151,7 +150,7 @@ void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQu
 {
 {
     // If no bones or no bone-level testing, use the StaticModel test
     // If no bones or no bone-level testing, use the StaticModel test
     RayQueryLevel level = query.level_;
     RayQueryLevel level = query.level_;
-    if (level < RAY_TRIANGLE || !skeleton_.GetNumBones() || !boneCreationEnabled_)
+    if (level < RAY_TRIANGLE || !skeleton_.GetNumBones())
     {
     {
         StaticModel::ProcessRayQuery(query, results);
         StaticModel::ProcessRayQuery(query, results);
         return;
         return;
@@ -281,30 +280,6 @@ void AnimatedModel::UpdateBatches(const FrameInfo& frame)
         lodDistance_ = newLodDistance;
         lodDistance_ = newLodDistance;
         CalculateLodLevels();
         CalculateLodLevels();
     }
     }
-
-    // Handle mesh hiding
-    if (geometryDisabled_)
-    {
-        for (unsigned i = 0; i < batches_.Size(); ++i)
-        {
-            SourceBatch* batch = &batches_[i];
-            StaticModelGeometryData* data = &geometryData_[i];
-
-            if (batch->geometry_)
-                data->batchGeometry_ = batch->geometry_;
-
-            if (data->enabled_ && !batch->geometry_)
-            {
-                batch->geometry_ = data->batchGeometry_;
-            }
-            else if (!data->enabled_ && batch->geometry_)
-            {
-                data->batchGeometry_ = batch->geometry_;
-                batch->geometry_ = 0;
-            }
-        }
-    }
-
 }
 }
 
 
 void AnimatedModel::UpdateGeometry(const FrameInfo& frame)
 void AnimatedModel::UpdateGeometry(const FrameInfo& frame)
@@ -355,7 +330,7 @@ void AnimatedModel::SetModel(Model* model, bool createBones)
 
 
     if (model)
     if (model)
     {
     {
-        SubscribeToEvent(model, E_RELOADFINISHED, HANDLER(AnimatedModel, HandleModelReloadFinished));
+        SubscribeToEvent(model, E_RELOADFINISHED, URHO3D_HANDLER(AnimatedModel, HandleModelReloadFinished));
 
 
         // Copy the subgeometry & LOD level structure
         // Copy the subgeometry & LOD level structure
         SetNumGeometries(model->GetNumGeometries());
         SetNumGeometries(model->GetNumGeometries());
@@ -365,8 +340,6 @@ void AnimatedModel::SetModel(Model* model, bool createBones)
         {
         {
             geometries_[i] = geometries[i];
             geometries_[i] = geometries[i];
             geometryData_[i].center_ = geometryCenters[i];
             geometryData_[i].center_ = geometryCenters[i];
-            geometryData_[i].enabled_ = true;
-            geometryData_[i].batchGeometry_ = 0;
         }
         }
 
 
         // Copy geometry bone mappings
         // Copy geometry bone mappings
@@ -449,7 +422,7 @@ AnimationState* AnimatedModel::AddAnimationState(Animation* animation)
 {
 {
     if (!isMaster_)
     if (!isMaster_)
     {
     {
-        LOGERROR("Can not add animation state to non-master model");
+        URHO3D_LOGERROR("Can not add animation state to non-master model");
         return 0;
         return 0;
     }
     }
 
 
@@ -702,13 +675,10 @@ void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
 {
 {
     if (!node_ && createBones)
     if (!node_ && createBones)
     {
     {
-        LOGERROR("AnimatedModel not attached to a scene node, can not create bone nodes");
+        URHO3D_LOGERROR("AnimatedModel not attached to a scene node, can not create bone nodes");
         return;
         return;
     }
     }
 
 
-    if (createBones && !boneCreationEnabled_)
-        createBones = false;
-
     if (isMaster_)
     if (isMaster_)
     {
     {
         // Check if bone structure has stayed compatible (reloading the model.) In that case retain the old bones and animations
         // Check if bone structure has stayed compatible (reloading the model.) In that case retain the old bones and animations
@@ -748,17 +718,10 @@ void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
 
 
         skeleton_.Define(skeleton);
         skeleton_.Define(skeleton);
 
 
-        // Remove collision information from dummy bones that do not affect skinning, to prevent them from being merged
-        // to the bounding box
-        Vector<Bone>& bones = skeleton_.GetModifiableBones();
-        for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i)
-        {
-            if (i->collisionMask_ & BONECOLLISION_BOX && i->boundingBox_.Size().Length() < M_EPSILON)
-                i->collisionMask_ &= ~BONECOLLISION_BOX;
-            if (i->collisionMask_ & BONECOLLISION_SPHERE && i->radius_ < M_EPSILON)
-                i->collisionMask_ &= ~BONECOLLISION_SPHERE;
-        }
+        // Merge bounding boxes from non-master models
+        FinalizeBoneBoundingBoxes();
 
 
+        Vector<Bone>& bones = skeleton_.GetModifiableBones();
         // Create scene nodes for the bones
         // Create scene nodes for the bones
         if (createBones)
         if (createBones)
         {
         {
@@ -768,6 +731,8 @@ void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
                 Node* boneNode = node_->CreateChild(i->name_, LOCAL);
                 Node* boneNode = node_->CreateChild(i->name_, LOCAL);
                 boneNode->AddListener(this);
                 boneNode->AddListener(this);
                 boneNode->SetTransform(i->initialPosition_, i->initialRotation_, i->initialScale_);
                 boneNode->SetTransform(i->initialPosition_, i->initialRotation_, i->initialScale_);
+                // Copy the model component's temporary status
+                boneNode->SetTemporary(IsTemporary());
                 i->node_ = boneNode;
                 i->node_ = boneNode;
             }
             }
 
 
@@ -790,6 +755,11 @@ void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
         // For non-master models: use the bone nodes of the master model
         // For non-master models: use the bone nodes of the master model
         skeleton_.Define(skeleton);
         skeleton_.Define(skeleton);
 
 
+        // Instruct the master model to refresh (merge) its bone bounding boxes
+        AnimatedModel* master = node_->GetComponent<AnimatedModel>();
+        if (master && master != this)
+            master->FinalizeBoneBoundingBoxes();
+
         if (createBones)
         if (createBones)
         {
         {
             Vector<Bone>& bones = skeleton_.GetModifiableBones();
             Vector<Bone>& bones = skeleton_.GetModifiableBones();
@@ -917,6 +887,34 @@ const PODVector<unsigned char>& AnimatedModel::GetMorphsAttr() const
     return attrBuffer_.GetBuffer();
     return attrBuffer_.GetBuffer();
 }
 }
 
 
+void AnimatedModel::UpdateBoneBoundingBox()
+{
+    if (skeleton_.GetNumBones())
+    {
+        // The bone bounding box is in local space, so need the node's inverse transform
+        boneBoundingBox_.Clear();
+        Matrix3x4 inverseNodeTransform = node_->GetWorldTransform().Inverse();
+
+        const Vector<Bone>& bones = skeleton_.GetBones();
+        for (Vector<Bone>::ConstIterator i = bones.Begin(); i != bones.End(); ++i)
+        {
+            Node* boneNode = i->node_;
+            if (!boneNode)
+                continue;
+
+            // Use hitbox if available. If not, use only half of the sphere radius
+            /// \todo The sphere radius should be multiplied with bone scale
+            if (i->collisionMask_ & BONECOLLISION_BOX)
+                boneBoundingBox_.Merge(i->boundingBox_.Transformed(inverseNodeTransform * boneNode->GetWorldTransform()));
+            else if (i->collisionMask_ & BONECOLLISION_SPHERE)
+                boneBoundingBox_.Merge(Sphere(inverseNodeTransform * boneNode->GetWorldPosition(), i->radius_ * 0.5f));
+        }
+    }
+
+    boneBoundingBoxDirty_ = false;
+    worldBoundingBoxDirty_ = true;
+}
+
 void AnimatedModel::OnNodeSet(Node* node)
 void AnimatedModel::OnNodeSet(Node* node)
 {
 {
     Drawable::OnNodeSet(node);
     Drawable::OnNodeSet(node);
@@ -932,11 +930,13 @@ void AnimatedModel::OnMarkedDirty(Node* node)
 {
 {
     Drawable::OnMarkedDirty(node);
     Drawable::OnMarkedDirty(node);
 
 
-    // If the scene node or any of the bone nodes move, mark skinning and the bone bounding box dirty
+    // If the scene node or any of the bone nodes move, mark skinning dirty
     if (skeleton_.GetNumBones())
     if (skeleton_.GetNumBones())
     {
     {
         skinningDirty_ = true;
         skinningDirty_ = true;
-        boneBoundingBoxDirty_ = true;
+        // Bone bounding box doesn't need to be marked dirty when only the base scene node moves
+        if (node != node_)
+            boneBoundingBoxDirty_ = true;
     }
     }
 }
 }
 
 
@@ -964,7 +964,7 @@ void AnimatedModel::AssignBoneNodes()
 {
 {
     assignBonesPending_ = false;
     assignBonesPending_ = false;
 
 
-    if (!node_ || !boneCreationEnabled_)
+    if (!node_)
         return;
         return;
 
 
     // Find the bone nodes from the node hierarchy and add listeners
     // Find the bone nodes from the node hierarchy and add listeners
@@ -984,34 +984,75 @@ void AnimatedModel::AssignBoneNodes()
     // If no bones found, this may be a prefab where the bone information was left out.
     // If no bones found, this may be a prefab where the bone information was left out.
     // In that case reassign the skeleton now if possible
     // In that case reassign the skeleton now if possible
     if (!boneFound && model_)
     if (!boneFound && model_)
+        SetSkeleton(model_->GetSkeleton(), true);
+
+    // Re-assign the same start bone to animations to get the proper bone node this time
+    for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
     {
     {
-        // ATOMIC: instead of calling SetSkeleton which does significant initialization
-        // create the bone nodes here (which also avoids a bone init issue)
+        AnimationState* state = *i;
+        state->SetStartBone(state->GetStartBone());
+    }
+}
 
 
-        // SetSkeleton(model_->GetSkeleton(), true);
+void AnimatedModel::FinalizeBoneBoundingBoxes()
+{
+    Vector<Bone>& bones = skeleton_.GetModifiableBones();
+    PODVector<AnimatedModel*> models;
+    GetComponents<AnimatedModel>(models);
 
 
-        for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i)
+    if (models.Size() > 1)
+    {
+        // Reset first to the model resource's original bone bounding information if available (should be)
+        if (model_)
         {
         {
-            // Create bones as local, as they are never to be directly synchronized over the network
-            Node* boneNode = node_->CreateChild(i->name_, LOCAL);
-            boneNode->AddListener(this);
-            boneNode->SetTransform(i->initialPosition_, i->initialRotation_, i->initialScale_);
-            i->node_ = boneNode;
+            const Vector<Bone>& modelBones = model_->GetSkeleton().GetBones();
+            for (unsigned i = 0; i < bones.Size() && i < modelBones.Size(); ++i)
+            {
+                bones[i].collisionMask_ = modelBones[i].collisionMask_;
+                bones[i].radius_ = modelBones[i].radius_;
+                bones[i].boundingBox_ = modelBones[i].boundingBox_;
+            }
         }
         }
 
 
-        for (unsigned i = 0; i < bones.Size(); ++i)
+        // Get matching bones from all non-master models and merge their bone bounding information
+        // to prevent culling errors (master model may not have geometry in all bones, or the bounds are smaller)
+        for (PODVector<AnimatedModel*>::Iterator i = models.Begin(); i != models.End(); ++i)
         {
         {
-            unsigned parentIndex = bones[i].parentIndex_;
-            if (parentIndex != i && parentIndex < bones.Size())
-                bones[parentIndex].node_->AddChild(bones[i].node_);
+            if ((*i) == this)
+                continue;
+
+            Skeleton& otherSkeleton = (*i)->GetSkeleton();
+            for (Vector<Bone>::Iterator j = bones.Begin(); j != bones.End(); ++j)
+            {
+                Bone* otherBone = otherSkeleton.GetBone(j->nameHash_);
+                if (otherBone)
+                {
+                    if (otherBone->collisionMask_ & BONECOLLISION_SPHERE)
+                    {
+                        j->collisionMask_ |= BONECOLLISION_SPHERE;
+                        j->radius_ = Max(j->radius_, otherBone->radius_);
+                    }
+                    if (otherBone->collisionMask_ & BONECOLLISION_BOX)
+                    {
+                        j->collisionMask_ |= BONECOLLISION_BOX;
+                        if (j->boundingBox_.Defined())
+                            j->boundingBox_.Merge(otherBone->boundingBox_);
+                        else
+                            j->boundingBox_.Define(otherBone->boundingBox_);
+                    }
+                }
+            }
         }
         }
     }
     }
 
 
-    // Re-assign the same start bone to animations to get the proper bone node this time
-    for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
+    // Remove collision information from dummy bones that do not affect skinning, to prevent them from being merged
+    // to the bounding box and making it artificially large
+    for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i)
     {
     {
-        AnimationState* state = *i;
-        state->SetStartBone(state->GetStartBone());
+        if (i->collisionMask_ & BONECOLLISION_BOX && i->boundingBox_.Size().Length() < M_EPSILON)
+            i->collisionMask_ &= ~BONECOLLISION_BOX;
+        if (i->collisionMask_ & BONECOLLISION_SPHERE && i->radius_ < M_EPSILON)
+            i->collisionMask_ &= ~BONECOLLISION_SPHERE;
     }
     }
 }
 }
 
 
@@ -1096,16 +1137,16 @@ void AnimatedModel::CloneGeometries()
             for (unsigned k = 0; k < originalBuffers.Size(); ++k)
             for (unsigned k = 0; k < originalBuffers.Size(); ++k)
             {
             {
                 VertexBuffer* originalBuffer = originalBuffers[k];
                 VertexBuffer* originalBuffer = originalBuffers[k];
-                unsigned originalMask = original->GetVertexElementMask(k);
 
 
                 if (clonedVertexBuffers.Contains(originalBuffer))
                 if (clonedVertexBuffers.Contains(originalBuffer))
                 {
                 {
                     VertexBuffer* clonedBuffer = clonedVertexBuffers[originalBuffer];
                     VertexBuffer* clonedBuffer = clonedVertexBuffers[originalBuffer];
-                    clone->SetVertexBuffer(l++, originalBuffer, originalMask & ~clonedBuffer->GetElementMask());
-                    clone->SetVertexBuffer(l++, clonedBuffer, originalMask & clonedBuffer->GetElementMask());
+                    clone->SetVertexBuffer(l++, originalBuffer);
+                    // Specify the morph buffer at a greater index to override the model's original positions/normals/tangents
+                    clone->SetVertexBuffer(l++, clonedBuffer);
                 }
                 }
                 else
                 else
-                    clone->SetVertexBuffer(l++, originalBuffer, originalMask);
+                    clone->SetVertexBuffer(l++, originalBuffer);
             }
             }
 
 
             clone->SetIndexBuffer(original->GetIndexBuffer());
             clone->SetIndexBuffer(original->GetIndexBuffer());
@@ -1125,8 +1166,8 @@ void AnimatedModel::CopyMorphVertices(void* destVertexData, void* srcVertexData,
     VertexBuffer* srcBuffer)
     VertexBuffer* srcBuffer)
 {
 {
     unsigned mask = destBuffer->GetElementMask() & srcBuffer->GetElementMask();
     unsigned mask = destBuffer->GetElementMask() & srcBuffer->GetElementMask();
-    unsigned normalOffset = srcBuffer->GetElementOffset(ELEMENT_NORMAL);
-    unsigned tangentOffset = srcBuffer->GetElementOffset(ELEMENT_TANGENT);
+    unsigned normalOffset = srcBuffer->GetElementOffset(SEM_NORMAL);
+    unsigned tangentOffset = srcBuffer->GetElementOffset(SEM_TANGENT);
     unsigned vertexSize = srcBuffer->GetVertexSize();
     unsigned vertexSize = srcBuffer->GetVertexSize();
     float* dest = (float*)destVertexData;
     float* dest = (float*)destVertexData;
     unsigned char* src = (unsigned char*)srcVertexData;
     unsigned char* src = (unsigned char*)srcVertexData;
@@ -1238,34 +1279,6 @@ void AnimatedModel::UpdateAnimation(const FrameInfo& frame)
     animationDirty_ = false;
     animationDirty_ = false;
 }
 }
 
 
-void AnimatedModel::UpdateBoneBoundingBox()
-{
-    if (skeleton_.GetNumBones())
-    {
-        // The bone bounding box is in local space, so need the node's inverse transform
-        boneBoundingBox_.defined_ = false;
-        Matrix3x4 inverseNodeTransform = node_->GetWorldTransform().Inverse();
-
-        const Vector<Bone>& bones = skeleton_.GetBones();
-        for (Vector<Bone>::ConstIterator i = bones.Begin(); i != bones.End(); ++i)
-        {
-            Node* boneNode = i->node_;
-            if (!boneNode)
-                continue;
-
-            // Use hitbox if available. If not, use only half of the sphere radius
-            /// \todo The sphere radius should be multiplied with bone scale
-            if (i->collisionMask_ & BONECOLLISION_BOX)
-                boneBoundingBox_.Merge(i->boundingBox_.Transformed(inverseNodeTransform * boneNode->GetWorldTransform()));
-            else if (i->collisionMask_ & BONECOLLISION_SPHERE)
-                boneBoundingBox_.Merge(Sphere(inverseNodeTransform * boneNode->GetWorldPosition(), i->radius_ * 0.5f));
-        }
-    }
-
-    boneBoundingBoxDirty_ = false;
-    worldBoundingBoxDirty_ = true;
-}
-
 void AnimatedModel::UpdateSkinning()
 void AnimatedModel::UpdateSkinning()
 {
 {
     // Note: the model's world transform will be baked in the skin matrices
     // Note: the model's world transform will be baked in the skin matrices
@@ -1354,8 +1367,8 @@ void AnimatedModel::ApplyMorph(VertexBuffer* buffer, void* destVertexData, unsig
 {
 {
     unsigned elementMask = morph.elementMask_ & buffer->GetElementMask();
     unsigned elementMask = morph.elementMask_ & buffer->GetElementMask();
     unsigned vertexCount = morph.vertexCount_;
     unsigned vertexCount = morph.vertexCount_;
-    unsigned normalOffset = buffer->GetElementOffset(ELEMENT_NORMAL);
-    unsigned tangentOffset = buffer->GetElementOffset(ELEMENT_TANGENT);
+    unsigned normalOffset = buffer->GetElementOffset(SEM_NORMAL);
+    unsigned tangentOffset = buffer->GetElementOffset(SEM_TANGENT);
     unsigned vertexSize = buffer->GetVertexSize();
     unsigned vertexSize = buffer->GetVertexSize();
 
 
     unsigned char* srcData = morph.morphData_;
     unsigned char* srcData = morph.morphData_;
@@ -1403,8 +1416,4 @@ void AnimatedModel::HandleModelReloadFinished(StringHash eventType, VariantMap&
     SetModel(currentModel);
     SetModel(currentModel);
 }
 }
 
 
-void AnimatedModel::SetBoneCreationEnabled(bool enabled)
-{
-    boneCreationEnabled_ = enabled;
-}
 }
 }

+ 13 - 13
Source/Atomic/Atomic3D/AnimatedModel.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,20 +22,20 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic3D/Model.h"
-#include "../Atomic3D/Skeleton.h"
-#include "../Atomic3D/StaticModel.h"
+#include "../Graphics/Model.h"
+#include "../Graphics/Skeleton.h"
+#include "../Graphics/StaticModel.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class Animation;
 class Animation;
 class AnimationState;
 class AnimationState;
 
 
 /// Animated model component.
 /// Animated model component.
-class ATOMIC_API AnimatedModel : public StaticModel
+class URHO3D_API AnimatedModel : public StaticModel
 {
 {
-    OBJECT(AnimatedModel);
+    URHO3D_OBJECT(AnimatedModel, StaticModel);
 
 
     friend class AnimationState;
     friend class AnimationState;
 
 
@@ -51,6 +51,8 @@ public:
     virtual bool Load(Deserializer& source, bool setInstanceDefault = false);
     virtual bool Load(Deserializer& source, bool setInstanceDefault = false);
     /// Load from XML data. Return true if successful.
     /// Load from XML data. Return true if successful.
     virtual bool LoadXML(const XMLElement& source, bool setInstanceDefault = false);
     virtual bool LoadXML(const XMLElement& source, bool setInstanceDefault = false);
+    /// Load from JSON data. Return true if successful.
+    virtual bool LoadJSON(const JSONValue& source, bool setInstanceDefault = false);
     /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
     /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
     virtual void ApplyAttributes();
     virtual void ApplyAttributes();
     /// Process octree raycast. May be called from a worker thread.
     /// Process octree raycast. May be called from a worker thread.
@@ -161,8 +163,8 @@ public:
     /// Return per-geometry skin matrices. If empty, uses global skinning
     /// Return per-geometry skin matrices. If empty, uses global skinning
     const Vector<PODVector<Matrix3x4> >& GetGeometrySkinMatrices() const { return geometrySkinMatrices_; }
     const Vector<PODVector<Matrix3x4> >& GetGeometrySkinMatrices() const { return geometrySkinMatrices_; }
 
 
-    /// Globally enable/disable bone creation, useful for when in the editor
-    static void SetBoneCreationEnabled(bool enabled);
+    /// Recalculate the bone bounding box. Normally called internally, but can also be manually called if up-to-date information before rendering is necessary.
+    void UpdateBoneBoundingBox();
 
 
 protected:
 protected:
     /// Handle node being assigned.
     /// Handle node being assigned.
@@ -175,6 +177,8 @@ protected:
 private:
 private:
     /// Assign skeleton and animation bone node references as a postprocess. Called by ApplyAttributes.
     /// Assign skeleton and animation bone node references as a postprocess. Called by ApplyAttributes.
     void AssignBoneNodes();
     void AssignBoneNodes();
+    /// Finalize master model bone bounding boxes by merging from matching non-master bones.. Performed whenever any of the AnimatedModels in the same node changes its model.
+    void FinalizeBoneBoundingBoxes();
     /// Remove (old) skeleton root bone.
     /// Remove (old) skeleton root bone.
     void RemoveRootBone();
     void RemoveRootBone();
     /// Mark animation and skinning to require an update.
     /// Mark animation and skinning to require an update.
@@ -193,8 +197,6 @@ private:
     void CopyMorphVertices(void* dest, void* src, unsigned vertexCount, VertexBuffer* clone, VertexBuffer* original);
     void CopyMorphVertices(void* dest, void* src, unsigned vertexCount, VertexBuffer* clone, VertexBuffer* original);
     /// Recalculate animations. Called from Update().
     /// Recalculate animations. Called from Update().
     void UpdateAnimation(const FrameInfo& frame);
     void UpdateAnimation(const FrameInfo& frame);
-    /// Recalculate the bone bounding box.
-    void UpdateBoneBoundingBox();
     /// Recalculate skinning.
     /// Recalculate skinning.
     void UpdateSkinning();
     void UpdateSkinning();
     /// Reapply all vertex morphs.
     /// Reapply all vertex morphs.
@@ -253,8 +255,6 @@ private:
     bool loading_;
     bool loading_;
     /// Bone nodes assignment pending flag.
     /// Bone nodes assignment pending flag.
     bool assignBonesPending_;
     bool assignBonesPending_;
-    /// Whether bone creation is enabled, globally
-    static bool boneCreationEnabled_;
     /// Force animation update after becoming visible flag.
     /// Force animation update after becoming visible flag.
     bool forceAnimationUpdate_;
     bool forceAnimationUpdate_;
 };
 };

+ 157 - 36
Source/Atomic/Atomic3D/Animation.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,19 +22,21 @@
 
 
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
+#include "../Container/Sort.h"
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
-#include "../Atomic3D/Animation.h"
+#include "../Graphics/Animation.h"
 #include "../IO/Deserializer.h"
 #include "../IO/Deserializer.h"
 #include "../IO/FileSystem.h"
 #include "../IO/FileSystem.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../IO/Serializer.h"
 #include "../IO/Serializer.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/XMLFile.h"
 #include "../Resource/XMLFile.h"
+#include "../Resource/JSONFile.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 inline bool CompareTriggers(AnimationTriggerPoint& lhs, AnimationTriggerPoint& rhs)
 inline bool CompareTriggers(AnimationTriggerPoint& lhs, AnimationTriggerPoint& rhs)
@@ -42,6 +44,51 @@ inline bool CompareTriggers(AnimationTriggerPoint& lhs, AnimationTriggerPoint& r
     return lhs.time_ < rhs.time_;
     return lhs.time_ < rhs.time_;
 }
 }
 
 
+inline bool CompareKeyFrames(AnimationKeyFrame& lhs, AnimationKeyFrame& rhs)
+{
+    return lhs.time_ < rhs.time_;
+}
+
+void AnimationTrack::SetKeyFrame(unsigned index, const AnimationKeyFrame& keyFrame)
+{
+    if (index < keyFrames_.Size())
+    {
+        keyFrames_[index] = keyFrame;
+        Urho3D::Sort(keyFrames_.Begin(), keyFrames_.End(), CompareKeyFrames);
+    }
+    else if (index == keyFrames_.Size())
+        AddKeyFrame(keyFrame);
+}
+
+void AnimationTrack::AddKeyFrame(const AnimationKeyFrame& keyFrame)
+{
+    bool needSort = keyFrames_.Size() ? keyFrames_.Back().time_ > keyFrame.time_ : false;
+    keyFrames_.Push(keyFrame);
+    if (needSort)
+        Urho3D::Sort(keyFrames_.Begin(), keyFrames_.End(), CompareKeyFrames);
+}
+
+void AnimationTrack::InsertKeyFrame(unsigned index, const AnimationKeyFrame& keyFrame)
+{
+    keyFrames_.Insert(index, keyFrame);
+    Urho3D::Sort(keyFrames_.Begin(), keyFrames_.End(), CompareKeyFrames);
+}
+
+void AnimationTrack::RemoveKeyFrame(unsigned index)
+{
+    keyFrames_.Erase(index);
+}
+
+void AnimationTrack::RemoveAllKeyFrames()
+{
+    keyFrames_.Clear();
+}
+
+AnimationKeyFrame* AnimationTrack::GetKeyFrame(unsigned index)
+{
+    return index < keyFrames_.Size() ? &keyFrames_[index] : (AnimationKeyFrame*)0;
+}
+
 void AnimationTrack::GetKeyFrameIndex(float time, unsigned& index) const
 void AnimationTrack::GetKeyFrameIndex(float time, unsigned& index) const
 {
 {
     if (time < 0.0f)
     if (time < 0.0f)
@@ -81,7 +128,7 @@ bool Animation::BeginLoad(Deserializer& source)
     // Check ID
     // Check ID
     if (source.ReadFileID() != "UANI")
     if (source.ReadFileID() != "UANI")
     {
     {
-        LOGERROR(source.GetName() + " is not a valid animation file");
+        URHO3D_LOGERROR(source.GetName() + " is not a valid animation file");
         return false;
         return false;
     }
     }
 
 
@@ -92,31 +139,28 @@ bool Animation::BeginLoad(Deserializer& source)
     tracks_.Clear();
     tracks_.Clear();
 
 
     unsigned tracks = source.ReadUInt();
     unsigned tracks = source.ReadUInt();
-    tracks_.Resize(tracks);
     memoryUse += tracks * sizeof(AnimationTrack);
     memoryUse += tracks * sizeof(AnimationTrack);
 
 
     // Read tracks
     // Read tracks
     for (unsigned i = 0; i < tracks; ++i)
     for (unsigned i = 0; i < tracks; ++i)
     {
     {
-        AnimationTrack& newTrack = tracks_[i];
-        newTrack.name_ = source.ReadString();
-        newTrack.nameHash_ = newTrack.name_;
-        newTrack.channelMask_ = source.ReadUByte();
+        AnimationTrack* newTrack = CreateTrack(source.ReadString());
+        newTrack->channelMask_ = source.ReadUByte();
 
 
         unsigned keyFrames = source.ReadUInt();
         unsigned keyFrames = source.ReadUInt();
-        newTrack.keyFrames_.Resize(keyFrames);
+        newTrack->keyFrames_.Resize(keyFrames);
         memoryUse += keyFrames * sizeof(AnimationKeyFrame);
         memoryUse += keyFrames * sizeof(AnimationKeyFrame);
 
 
         // Read keyframes of the track
         // Read keyframes of the track
         for (unsigned j = 0; j < keyFrames; ++j)
         for (unsigned j = 0; j < keyFrames; ++j)
         {
         {
-            AnimationKeyFrame& newKeyFrame = newTrack.keyFrames_[j];
+            AnimationKeyFrame& newKeyFrame = newTrack->keyFrames_[j];
             newKeyFrame.time_ = source.ReadFloat();
             newKeyFrame.time_ = source.ReadFloat();
-            if (newTrack.channelMask_ & CHANNEL_POSITION)
+            if (newTrack->channelMask_ & CHANNEL_POSITION)
                 newKeyFrame.position_ = source.ReadVector3();
                 newKeyFrame.position_ = source.ReadVector3();
-            if (newTrack.channelMask_ & CHANNEL_ROTATION)
+            if (newTrack->channelMask_ & CHANNEL_ROTATION)
                 newKeyFrame.rotation_ = source.ReadQuaternion();
                 newKeyFrame.rotation_ = source.ReadQuaternion();
-            if (newTrack.channelMask_ & CHANNEL_SCALE)
+            if (newTrack->channelMask_ & CHANNEL_SCALE)
                 newKeyFrame.scale_ = source.ReadVector3();
                 newKeyFrame.scale_ = source.ReadVector3();
         }
         }
     }
     }
@@ -141,6 +185,36 @@ bool Animation::BeginLoad(Deserializer& source)
         }
         }
 
 
         memoryUse += triggers_.Size() * sizeof(AnimationTriggerPoint);
         memoryUse += triggers_.Size() * sizeof(AnimationTriggerPoint);
+        SetMemoryUse(memoryUse);
+        return true;
+    }
+
+    // Optionally read triggers from a JSON file
+    String jsonName = ReplaceExtension(GetName(), ".json");
+
+    SharedPtr<JSONFile> jsonFile(cache->GetTempResource<JSONFile>(jsonName, false));
+    if (jsonFile)
+    {
+        const JSONValue& rootVal = jsonFile->GetRoot();
+        JSONArray triggerArray = rootVal.Get("triggers").GetArray();
+
+        for (unsigned i = 0; i < triggerArray.Size(); i++)
+        {
+            const JSONValue& triggerValue = triggerArray.At(i);
+            JSONValue normalizedTimeValue = triggerValue.Get("normalizedTime");
+            if (!normalizedTimeValue.IsNull())
+                AddTrigger(normalizedTimeValue.GetFloat(), true, triggerValue.GetVariant());
+            else
+            {
+                JSONValue timeVal = triggerValue.Get("time");
+                if (!timeVal.IsNull())
+                    AddTrigger(timeVal.GetFloat(), false, triggerValue.GetVariant());
+            }
+        }
+
+        memoryUse += triggers_.Size() * sizeof(AnimationTriggerPoint);
+        SetMemoryUse(memoryUse);
+        return true;
     }
     }
 
 
     SetMemoryUse(memoryUse);
     SetMemoryUse(memoryUse);
@@ -156,9 +230,9 @@ bool Animation::Save(Serializer& dest) const
 
 
     // Write tracks
     // Write tracks
     dest.WriteUInt(tracks_.Size());
     dest.WriteUInt(tracks_.Size());
-    for (unsigned i = 0; i < tracks_.Size(); ++i)
+    for (HashMap<StringHash, AnimationTrack>::ConstIterator i = tracks_.Begin(); i != tracks_.End(); ++i)
     {
     {
-        const AnimationTrack& track = tracks_[i];
+        const AnimationTrack& track = i->second_;
         dest.WriteString(track.name_);
         dest.WriteString(track.name_);
         dest.WriteUByte(track.channelMask_);
         dest.WriteUByte(track.channelMask_);
         dest.WriteUInt(track.keyFrames_.Size());
         dest.WriteUInt(track.keyFrames_.Size());
@@ -199,7 +273,7 @@ bool Animation::Save(Serializer& dest) const
             xml->Save(xmlFile);
             xml->Save(xmlFile);
         }
         }
         else
         else
-            LOGWARNING("Can not save animation trigger data when not saving into a file");
+            URHO3D_LOGWARNING("Can not save animation trigger data when not saving into a file");
     }
     }
 
 
     return true;
     return true;
@@ -216,9 +290,52 @@ void Animation::SetLength(float length)
     length_ = Max(length, 0.0f);
     length_ = Max(length, 0.0f);
 }
 }
 
 
-void Animation::SetTracks(const Vector<AnimationTrack>& tracks)
+AnimationTrack* Animation::CreateTrack(const String& name)
+{
+    /// \todo When tracks / keyframes are created dynamically, memory use is not updated
+    StringHash nameHash(name);
+    AnimationTrack* oldTrack = GetTrack(nameHash);
+    if (oldTrack)
+        return oldTrack;
+
+    AnimationTrack& newTrack = tracks_[nameHash];
+    newTrack.name_ = name;
+    newTrack.nameHash_ = nameHash;
+    return &newTrack;
+}
+
+bool Animation::RemoveTrack(const String& name)
+{
+    HashMap<StringHash, AnimationTrack>::Iterator i = tracks_.Find(StringHash(name));
+    if (i != tracks_.End())
+    {
+        tracks_.Erase(i);
+        return true;
+    }
+    else
+        return false;
+}
+
+void Animation::RemoveAllTracks()
+{
+    tracks_.Clear();
+}
+
+void Animation::SetTrigger(unsigned index, const AnimationTriggerPoint& trigger)
+{
+    if (index == triggers_.Size())
+        AddTrigger(trigger);
+    else if (index < triggers_.Size())
+    {
+        triggers_[index] = trigger;
+        Sort(triggers_.Begin(), triggers_.End(), CompareTriggers);
+    }
+}
+
+void Animation::AddTrigger(const AnimationTriggerPoint& trigger)
 {
 {
-    tracks_ = tracks;
+    triggers_.Push(trigger);
+    Sort(triggers_.Begin(), triggers_.End(), CompareTriggers);
 }
 }
 
 
 void Animation::AddTrigger(float time, bool timeIsNormalized, const Variant& data)
 void Animation::AddTrigger(float time, bool timeIsNormalized, const Variant& data)
@@ -247,31 +364,35 @@ void Animation::SetNumTriggers(unsigned num)
     triggers_.Resize(num);
     triggers_.Resize(num);
 }
 }
 
 
-const AnimationTrack* Animation::GetTrack(unsigned index) const
+SharedPtr<Animation> Animation::Clone(const String& cloneName) const
 {
 {
-    return index < tracks_.Size() ? &tracks_[index] : 0;
+    SharedPtr<Animation> ret(new Animation(context_));
+
+    ret->SetName(cloneName);
+    ret->SetAnimationName(animationName_);
+    ret->length_ = length_;
+    ret->tracks_ = tracks_;
+    ret->triggers_ = triggers_;
+    ret->SetMemoryUse(GetMemoryUse());
+    
+    return ret;
 }
 }
 
 
-const AnimationTrack* Animation::GetTrack(const String& name) const
+AnimationTrack* Animation::GetTrack(const String& name)
 {
 {
-    for (Vector<AnimationTrack>::ConstIterator i = tracks_.Begin(); i != tracks_.End(); ++i)
-    {
-        if (i->name_ == name)
-            return &(*i);
-    }
-
-    return 0;
+    HashMap<StringHash, AnimationTrack>::Iterator i = tracks_.Find(StringHash(name));
+    return i != tracks_.End() ? &i->second_ : (AnimationTrack*)0;
 }
 }
 
 
-const AnimationTrack* Animation::GetTrack(StringHash nameHash) const
+AnimationTrack* Animation::GetTrack(StringHash nameHash)
 {
 {
-    for (Vector<AnimationTrack>::ConstIterator i = tracks_.Begin(); i != tracks_.End(); ++i)
-    {
-        if (i->nameHash_ == nameHash)
-            return &(*i);
-    }
+    HashMap<StringHash, AnimationTrack>::Iterator i = tracks_.Find(nameHash);
+    return i != tracks_.End() ? &i->second_ : (AnimationTrack*)0;
+}
 
 
-    return 0;
+AnimationTriggerPoint* Animation::GetTrigger(unsigned index)
+{
+    return index < triggers_.Size() ? &triggers_[index] : (AnimationTriggerPoint*)0;
 }
 }
 
 
 }
 }

+ 56 - 17
Source/Atomic/Atomic3D/Animation.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -27,12 +27,19 @@
 #include "../Math/Vector3.h"
 #include "../Math/Vector3.h"
 #include "../Resource/Resource.h"
 #include "../Resource/Resource.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// Skeletal animation keyframe.
 /// Skeletal animation keyframe.
 struct AnimationKeyFrame
 struct AnimationKeyFrame
 {
 {
+    /// Construct.
+    AnimationKeyFrame() :
+        time_(0.0f),
+        scale_(Vector3::ONE)
+    {
+    }
+
     /// Keyframe time.
     /// Keyframe time.
     float time_;
     float time_;
     /// Bone position.
     /// Bone position.
@@ -44,14 +51,35 @@ struct AnimationKeyFrame
 };
 };
 
 
 /// Skeletal animation track, stores keyframes of a single bone.
 /// Skeletal animation track, stores keyframes of a single bone.
-struct AnimationTrack
+struct URHO3D_API AnimationTrack
 {
 {
+    /// Construct.
+    AnimationTrack() :
+        channelMask_(0)
+    {
+    }
+
+    /// Assign keyframe at index.
+    void SetKeyFrame(unsigned index, const AnimationKeyFrame& command);
+    /// Add a keyframe at the end.
+    void AddKeyFrame(const AnimationKeyFrame& keyFrame);
+    /// Insert a keyframe at index.
+    void InsertKeyFrame(unsigned index, const AnimationKeyFrame& keyFrame);
+    /// Remove a keyframe at index.
+    void RemoveKeyFrame(unsigned index);
+    /// Remove all keyframes.
+    void RemoveAllKeyFrames();
+
+    /// Return keyframe at index, or null if not found.
+    AnimationKeyFrame* GetKeyFrame(unsigned index);
+    /// Return number of keyframes.
+    unsigned GetNumKeyFrames() const { return keyFrames_.Size(); }
     /// Return keyframe index based on time and previous index.
     /// Return keyframe index based on time and previous index.
     void GetKeyFrameIndex(float time, unsigned& index) const;
     void GetKeyFrameIndex(float time, unsigned& index) const;
 
 
-    /// Bone name.
+    /// Bone or scene node name.
     String name_;
     String name_;
-    /// Bone name hash.
+    /// Name hash.
     StringHash nameHash_;
     StringHash nameHash_;
     /// Bitmask of included data (position, rotation, scale.)
     /// Bitmask of included data (position, rotation, scale.)
     unsigned char channelMask_;
     unsigned char channelMask_;
@@ -79,9 +107,9 @@ static const unsigned char CHANNEL_ROTATION = 0x2;
 static const unsigned char CHANNEL_SCALE = 0x4;
 static const unsigned char CHANNEL_SCALE = 0x4;
 
 
 /// Skeletal animation resource.
 /// Skeletal animation resource.
-class ATOMIC_API Animation : public Resource
+class URHO3D_API Animation : public Resource
 {
 {
-    OBJECT(Animation);
+    URHO3D_OBJECT(Animation, Resource);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -100,8 +128,16 @@ public:
     void SetAnimationName(const String& name);
     void SetAnimationName(const String& name);
     /// Set animation length.
     /// Set animation length.
     void SetLength(float length);
     void SetLength(float length);
-    /// Set all animation tracks.
-    void SetTracks(const Vector<AnimationTrack>& tracks);
+    /// Create and return a track by name. If track by same name already exists, returns the existing.
+    AnimationTrack* CreateTrack(const String& name);
+    /// Remove a track by name. Return true if was found and removed successfully. This is unsafe if the animation is currently used in playback.
+    bool RemoveTrack(const String& name);
+    /// Remove all tracks. This is unsafe if the animation is currently used in playback.
+    void RemoveAllTracks();
+    /// Set a trigger point at index.
+    void SetTrigger(unsigned index, const AnimationTriggerPoint& trigger);
+    /// Add a trigger point.
+    void AddTrigger(const AnimationTriggerPoint& trigger);
     /// Add a trigger point.
     /// Add a trigger point.
     void AddTrigger(float time, bool timeIsNormalized, const Variant& data);
     void AddTrigger(float time, bool timeIsNormalized, const Variant& data);
     /// Remove a trigger point by index.
     /// Remove a trigger point by index.
@@ -110,6 +146,8 @@ public:
     void RemoveAllTriggers();
     void RemoveAllTriggers();
     /// Resize trigger point vector.
     /// Resize trigger point vector.
     void SetNumTriggers(unsigned num);
     void SetNumTriggers(unsigned num);
+    /// Clone the animation.
+    SharedPtr<Animation> Clone(const String& cloneName = String::EMPTY) const;
 
 
     /// Return animation name.
     /// Return animation name.
     const String& GetAnimationName() const { return animationName_; }
     const String& GetAnimationName() const { return animationName_; }
@@ -121,17 +159,15 @@ public:
     float GetLength() const { return length_; }
     float GetLength() const { return length_; }
 
 
     /// Return all animation tracks.
     /// Return all animation tracks.
-    const Vector<AnimationTrack>& GetTracks() const { return tracks_; }
+    const HashMap<StringHash, AnimationTrack>& GetTracks() const { return tracks_; }
 
 
     /// Return number of animation tracks.
     /// Return number of animation tracks.
     unsigned GetNumTracks() const { return tracks_.Size(); }
     unsigned GetNumTracks() const { return tracks_.Size(); }
 
 
-    /// Return animation track by index.
-    const AnimationTrack* GetTrack(unsigned index) const;
-    /// Return animation track by bone name.
-    const AnimationTrack* GetTrack(const String& name) const;
-    /// Return animation track by bone name hash.
-    const AnimationTrack* GetTrack(StringHash nameHash) const;
+    /// Return animation track by name.
+    AnimationTrack* GetTrack(const String& name);
+    /// Return animation track by name hash.
+    AnimationTrack* GetTrack(StringHash nameHash);
 
 
     /// Return animation trigger points.
     /// Return animation trigger points.
     const Vector<AnimationTriggerPoint>& GetTriggers() const { return triggers_; }
     const Vector<AnimationTriggerPoint>& GetTriggers() const { return triggers_; }
@@ -139,6 +175,9 @@ public:
     /// Return number of animation trigger points.
     /// Return number of animation trigger points.
     unsigned GetNumTriggers() const { return triggers_.Size(); }
     unsigned GetNumTriggers() const { return triggers_.Size(); }
 
 
+    /// Return a trigger point by index.
+    AnimationTriggerPoint* GetTrigger(unsigned index);
+
 private:
 private:
     /// Animation name.
     /// Animation name.
     String animationName_;
     String animationName_;
@@ -147,7 +186,7 @@ private:
     /// Animation length.
     /// Animation length.
     float length_;
     float length_;
     /// Animation tracks.
     /// Animation tracks.
-    Vector<AnimationTrack> tracks_;
+    HashMap<StringHash, AnimationTrack> tracks_;
     /// Animation trigger points.
     /// Animation trigger points.
     Vector<AnimationTriggerPoint> triggers_;
     Vector<AnimationTriggerPoint> triggers_;
 };
 };

+ 60 - 159
Source/Atomic/Atomic3D/AnimationController.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,10 +24,10 @@
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
-#include "../Atomic3D/AnimatedModel.h"
-#include "../Atomic3D/Animation.h"
-#include "../Atomic3D/AnimationController.h"
-#include "../Atomic3D/AnimationState.h"
+#include "../Graphics/AnimatedModel.h"
+#include "../Graphics/Animation.h"
+#include "../Graphics/AnimationController.h"
+#include "../Graphics/AnimationState.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../IO/MemoryBuffer.h"
 #include "../IO/MemoryBuffer.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
@@ -36,7 +36,7 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 static const unsigned char CTRL_LOOPED = 0x1;
 static const unsigned char CTRL_LOOPED = 0x1;
@@ -45,6 +45,7 @@ static const unsigned char CTRL_AUTOFADE = 0x4;
 static const unsigned char CTRL_SETTIME = 0x08;
 static const unsigned char CTRL_SETTIME = 0x08;
 static const unsigned char CTRL_SETWEIGHT = 0x10;
 static const unsigned char CTRL_SETWEIGHT = 0x10;
 static const unsigned char CTRL_REMOVEONCOMPLETION = 0x20;
 static const unsigned char CTRL_REMOVEONCOMPLETION = 0x20;
+static const unsigned char CTRL_ADDITIVE = 0x40;
 static const float EXTRA_ANIM_FADEOUT_TIME = 0.1f;
 static const float EXTRA_ANIM_FADEOUT_TIME = 0.1f;
 static const float COMMAND_STAY_TIME = 0.25f;
 static const float COMMAND_STAY_TIME = 0.25f;
 static const unsigned MAX_NODE_ANIMATION_STATES = 256;
 static const unsigned MAX_NODE_ANIMATION_STATES = 256;
@@ -52,10 +53,7 @@ static const unsigned MAX_NODE_ANIMATION_STATES = 256;
 extern const char* LOGIC_CATEGORY;
 extern const char* LOGIC_CATEGORY;
 
 
 AnimationController::AnimationController(Context* context) :
 AnimationController::AnimationController(Context* context) :
-    Component(context),
-    animationResourcesAttr_(Animation::GetTypeStatic()),
-    autoPlay_(true),
-    autoPlayed_(false)
+    Component(context)
 {
 {
 }
 }
 
 
@@ -67,19 +65,13 @@ void AnimationController::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<AnimationController>(LOGIC_CATEGORY);
     context->RegisterFactory<AnimationController>(LOGIC_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Animations", GetAnimationsAttr, SetAnimationsAttr, VariantVector, Variant::emptyVariantVector,
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Animations", GetAnimationsAttr, SetAnimationsAttr, VariantVector, Variant::emptyVariantVector,
         AM_FILE | AM_NOEDIT);
         AM_FILE | AM_NOEDIT);
-    ACCESSOR_ATTRIBUTE("Network Animations", GetNetAnimationsAttr, SetNetAnimationsAttr, PODVector<unsigned char>,
+    URHO3D_ACCESSOR_ATTRIBUTE("Network Animations", GetNetAnimationsAttr, SetNetAnimationsAttr, PODVector<unsigned char>,
         Variant::emptyBuffer, AM_NET | AM_LATESTDATA | AM_NOEDIT);
         Variant::emptyBuffer, AM_NET | AM_LATESTDATA | AM_NOEDIT);
-    MIXED_ACCESSOR_ATTRIBUTE("Node Animation States", GetNodeAnimationStatesAttr, SetNodeAnimationStatesAttr, VariantVector,
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Node Animation States", GetNodeAnimationStatesAttr, SetNodeAnimationStatesAttr, VariantVector,
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
-
-    // ATOMIC BEGIN
-    MIXED_ACCESSOR_ATTRIBUTE("Animation", GetAnimationAttr, SetAnimationAttr, ResourceRef, ResourceRef(Animation::GetTypeStatic()), AM_DEFAULT);
-    ATTRIBUTE("Autoplay", bool, autoPlay_, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("AnimationResources", GetAnimationResourcesAttr, SetAnimationResourcesAttr, ResourceRefList, ResourceRefList(Animation::GetTypeStatic()), AM_DEFAULT);
-    // ATOMIC END
 }
 }
 
 
 void AnimationController::OnSetEnabled()
 void AnimationController::OnSetEnabled()
@@ -88,7 +80,7 @@ void AnimationController::OnSetEnabled()
     if (scene)
     if (scene)
     {
     {
         if (IsEnabledEffective())
         if (IsEnabledEffective())
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(AnimationController, HandleScenePostUpdate));
+            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(AnimationController, HandleScenePostUpdate));
         else
         else
             UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
             UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
     }
     }
@@ -96,33 +88,29 @@ void AnimationController::OnSetEnabled()
 
 
 void AnimationController::Update(float timeStep)
 void AnimationController::Update(float timeStep)
 {
 {
-    if (autoPlay_ && !autoPlayed_ && animation_.NotNull())
-    {
-        autoPlayed_ = true;
-        Play(animation_->GetAnimationName(), 0, true);
-    }
-
     // Loop through animations
     // Loop through animations
-    for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End();)
+    for (unsigned i = 0; i < animations_.Size();)
     {
     {
+        AnimationControl& ctrl = animations_[i];
+        AnimationState* state = GetAnimationState(ctrl.hash_);
         bool remove = false;
         bool remove = false;
-        AnimationState* state = GetAnimationState(i->hash_);
+
         if (!state)
         if (!state)
             remove = true;
             remove = true;
         else
         else
         {
         {
             // Advance the animation
             // Advance the animation
-            if (i->speed_ != 0.0f)
-                state->AddTime(i->speed_ * timeStep);
+            if (ctrl.speed_ != 0.0f)
+                state->AddTime(ctrl.speed_ * timeStep);
 
 
-            float targetWeight = i->targetWeight_;
-            float fadeTime = i->fadeTime_;
+            float targetWeight = ctrl.targetWeight_;
+            float fadeTime = ctrl.fadeTime_;
 
 
             // If non-looped animation at the end, activate autofade as applicable
             // If non-looped animation at the end, activate autofade as applicable
-            if (!state->IsLooped() && state->GetTime() >= state->GetLength() && i->autoFadeTime_ > 0.0f)
+            if (!state->IsLooped() && state->GetTime() >= state->GetLength() && ctrl.autoFadeTime_ > 0.0f)
             {
             {
                 targetWeight = 0.0f;
                 targetWeight = 0.0f;
-                fadeTime = i->autoFadeTime_;
+                fadeTime = ctrl.autoFadeTime_;
             }
             }
 
 
             // Process weight fade
             // Process weight fade
@@ -143,21 +131,21 @@ void AnimationController::Update(float timeStep)
             }
             }
 
 
             // Remove if weight zero and target weight zero
             // Remove if weight zero and target weight zero
-            if (state->GetWeight() == 0.0f && (targetWeight == 0.0f || fadeTime == 0.0f) && i->removeOnCompletion_)
+            if (state->GetWeight() == 0.0f && (targetWeight == 0.0f || fadeTime == 0.0f) && ctrl.removeOnCompletion_)
                 remove = true;
                 remove = true;
         }
         }
 
 
         // Decrement the command time-to-live values
         // Decrement the command time-to-live values
-        if (i->setTimeTtl_ > 0.0f)
-            i->setTimeTtl_ = Max(i->setTimeTtl_ - timeStep, 0.0f);
-        if (i->setWeightTtl_ > 0.0f)
-            i->setWeightTtl_ = Max(i->setWeightTtl_ - timeStep, 0.0f);
+        if (ctrl.setTimeTtl_ > 0.0f)
+            ctrl.setTimeTtl_ = Max(ctrl.setTimeTtl_ - timeStep, 0.0f);
+        if (ctrl.setWeightTtl_ > 0.0f)
+            ctrl.setWeightTtl_ = Max(ctrl.setWeightTtl_ - timeStep, 0.0f);
 
 
         if (remove)
         if (remove)
         {
         {
             if (state)
             if (state)
                 RemoveAnimationState(state);
                 RemoveAnimationState(state);
-            i = animations_.Erase(i);
+            animations_.Erase(i);
             MarkNetworkUpdate();
             MarkNetworkUpdate();
         }
         }
         else
         else
@@ -171,35 +159,19 @@ void AnimationController::Update(float timeStep)
 
 
 bool AnimationController::Play(const String& name, unsigned char layer, bool looped, float fadeInTime)
 bool AnimationController::Play(const String& name, unsigned char layer, bool looped, float fadeInTime)
 {
 {
+    // Get the animation resource first to be able to get the canonical resource name
+    // (avoids potential adding of duplicate animations)
+    Animation* newAnimation = GetSubsystem<ResourceCache>()->GetResource<Animation>(name);
+    if (!newAnimation)
+        return false;
+
     // Check if already exists
     // Check if already exists
     unsigned index;
     unsigned index;
     AnimationState* state;
     AnimationState* state;
-    FindAnimation(name, index, state);
+    FindAnimation(newAnimation->GetName(), index, state);
 
 
     if (!state)
     if (!state)
     {
     {
-        Animation* newAnimation = 0;
-
-        // Check if we're using attached animation resource
-        if (animation_.NotNull() && animation_->GetAnimationName() == name)
-        {
-            newAnimation = animation_;
-        }
-        else
-        {
-            for (unsigned i = 0; i < animationResources_.Size(); i++)
-            {
-                if (name == animationResources_[i]->GetAnimationName())
-                {
-                    newAnimation = animationResources_[i];
-                    break;
-                }
-            }
-        }
-
-        if (!newAnimation)
-            newAnimation = GetSubsystem<ResourceCache>()->GetResource<Animation>(name);
-
         state = AddAnimationState(newAnimation);
         state = AddAnimationState(newAnimation);
         if (!state)
         if (!state)
             return false;
             return false;
@@ -208,9 +180,8 @@ bool AnimationController::Play(const String& name, unsigned char layer, bool loo
     if (index == M_MAX_UNSIGNED)
     if (index == M_MAX_UNSIGNED)
     {
     {
         AnimationControl newControl;
         AnimationControl newControl;
-        Animation* animation = state->GetAnimation();
-        newControl.name_ = animation->GetName();
-        newControl.hash_ = animation->GetNameHash();
+        newControl.name_ = newAnimation->GetName();
+        newControl.hash_ = newAnimation->GetNameHash();
         animations_.Push(newControl);
         animations_.Push(newControl);
         index = animations_.Size() - 1;
         index = animations_.Size() - 1;
     }
     }
@@ -423,6 +394,17 @@ bool AnimationController::SetLooped(const String& name, bool enable)
     return true;
     return true;
 }
 }
 
 
+bool AnimationController::SetBlendMode(const String& name, AnimationBlendMode mode)
+{
+    AnimationState* state = GetAnimationState(name);
+    if (!state)
+        return false;
+
+    state->SetBlendMode(mode);
+    MarkNetworkUpdate();
+    return true;
+}
+
 bool AnimationController::SetAutoFade(const String& name, float fadeOutTime)
 bool AnimationController::SetAutoFade(const String& name, float fadeOutTime)
 {
 {
     unsigned index;
     unsigned index;
@@ -514,6 +496,12 @@ bool AnimationController::IsLooped(const String& name) const
     return state ? state->IsLooped() : false;
     return state ? state->IsLooped() : false;
 }
 }
 
 
+AnimationBlendMode AnimationController::GetBlendMode(const String& name) const
+{
+    AnimationState* state = GetAnimationState(name);
+    return state ? state->GetBlendMode() : ABM_LERP;
+}
+
 float AnimationController::GetLength(const String& name) const
 float AnimationController::GetLength(const String& name) const
 {
 {
     AnimationState* state = GetAnimationState(name);
     AnimationState* state = GetAnimationState(name);
@@ -625,7 +613,7 @@ void AnimationController::SetNetAnimationsAttr(const PODVector<unsigned char>& v
             state = AddAnimationState(newAnimation);
             state = AddAnimationState(newAnimation);
             if (!state)
             if (!state)
             {
             {
-                LOGERROR("Animation update applying aborted due to unknown animation");
+                URHO3D_LOGERROR("Animation update applying aborted due to unknown animation");
                 return;
                 return;
             }
             }
         }
         }
@@ -647,6 +635,7 @@ void AnimationController::SetNetAnimationsAttr(const PODVector<unsigned char>& v
         unsigned char ctrl = buf.ReadUByte();
         unsigned char ctrl = buf.ReadUByte();
         state->SetLayer(buf.ReadUByte());
         state->SetLayer(buf.ReadUByte());
         state->SetLooped((ctrl & CTRL_LOOPED) != 0);
         state->SetLooped((ctrl & CTRL_LOOPED) != 0);
+        state->SetBlendMode((ctrl & CTRL_ADDITIVE) != 0 ? ABM_ADDITIVE : ABM_LERP);
         animations_[index].speed_ = (float)buf.ReadShort() / 2048.0f; // 11 bits of decimal precision, max. 16x playback speed
         animations_[index].speed_ = (float)buf.ReadShort() / 2048.0f; // 11 bits of decimal precision, max. 16x playback speed
         animations_[index].targetWeight_ = (float)buf.ReadUByte() / 255.0f; // 8 bits of decimal precision
         animations_[index].targetWeight_ = (float)buf.ReadUByte() / 255.0f; // 8 bits of decimal precision
         animations_[index].fadeTime_ = (float)buf.ReadUByte() / 64.0f; // 6 bits of decimal precision, max. 4 seconds fade
         animations_[index].fadeTime_ = (float)buf.ReadUByte() / 64.0f; // 6 bits of decimal precision, max. 4 seconds fade
@@ -773,6 +762,8 @@ const PODVector<unsigned char>& AnimationController::GetNetAnimationsAttr() cons
         Bone* startBone = state->GetStartBone();
         Bone* startBone = state->GetStartBone();
         if (state->IsLooped())
         if (state->IsLooped())
             ctrl |= CTRL_LOOPED;
             ctrl |= CTRL_LOOPED;
+        if (state->GetBlendMode() == ABM_ADDITIVE)
+            ctrl |= CTRL_ADDITIVE;
         if (startBone && model && startBone != model->GetSkeleton().GetRootBone())
         if (startBone && model && startBone != model->GetSkeleton().GetRootBone())
             ctrl |= CTRL_STARTBONE;
             ctrl |= CTRL_STARTBONE;
         if (i->autoFadeTime_ > 0.0f)
         if (i->autoFadeTime_ > 0.0f)
@@ -828,9 +819,7 @@ VariantVector AnimationController::GetNodeAnimationStatesAttr() const
 void AnimationController::OnSceneSet(Scene* scene)
 void AnimationController::OnSceneSet(Scene* scene)
 {
 {
     if (scene && IsEnabledEffective())
     if (scene && IsEnabledEffective())
-    {
-        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(AnimationController, HandleScenePostUpdate));
-    }
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(AnimationController, HandleScenePostUpdate));
     else if (!scene)
     else if (!scene)
         UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
         UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
 }
 }
@@ -879,23 +868,6 @@ void AnimationController::FindAnimation(const String& name, unsigned& index, Ani
 {
 {
     StringHash nameHash(name);
     StringHash nameHash(name);
 
 
-    // Check if we're using attached animation resource
-    if (animation_.NotNull() && animation_->GetAnimationName() == name)
-    {
-        nameHash = animation_->GetName();
-    }
-    else
-    {
-        for (unsigned i = 0; i < animationResources_.Size(); i++)
-        {
-            if (name == animationResources_[i]->GetAnimationName())
-            {
-                nameHash = animationResources_[i]->GetName();
-                break;
-            }
-        }
-    }
-
     // Find the AnimationState
     // Find the AnimationState
     state = GetAnimationState(nameHash);
     state = GetAnimationState(nameHash);
     if (state)
     if (state)
@@ -923,75 +895,4 @@ void AnimationController::HandleScenePostUpdate(StringHash eventType, VariantMap
     Update(eventData[P_TIMESTEP].GetFloat());
     Update(eventData[P_TIMESTEP].GetFloat());
 }
 }
 
 
-// ATOMIC BEGIN
-
-void AnimationController::SetAnimationAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    animation_ = cache->GetResource<Animation>(value.name_);
-}
-
-ResourceRef AnimationController::GetAnimationAttr() const
-{
-    return GetResourceRef(animation_, Animation::GetTypeStatic());
-}
-
-void AnimationController::AddAnimationResource(Animation* animation)
-{
-    if (!animation)
-        return;
-
-    SharedPtr<Animation> anim(animation);
-
-    if (!animationResources_.Contains(anim))
-        animationResources_.Push(anim);
-}
-
-void AnimationController::RemoveAnimationResource(Animation* animation)
-{
-    if (!animation)
-        return;
-
-    animationResources_.Remove(SharedPtr<Animation>(animation));
-
-}
-
-void AnimationController::ClearAnimationResources()
-{
-    animationResources_.Clear();
-}
-
-void AnimationController::SetAnimationResourcesAttr(const ResourceRefList& value)
-{
-    animationResources_.Clear();
-
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    for (unsigned i = 0; i < value.names_.Size(); ++i)
-    {
-        Animation* animation = cache->GetResource<Animation>(value.names_[i]);
-        if (!animation)
-        {
-            //LOGERRORF("AnimationController::SetAnimationResourcesAttr - Unable to load animation: %s", value.names_[i].CString());
-            animationResources_.Push(SharedPtr<Animation>(0));
-        }
-        else
-        {
-            animationResources_.Push(SharedPtr<Animation>(animation));
-        }
-
-    }
-
-}
-
-const ResourceRefList& AnimationController::GetAnimationResourcesAttr() const
-{
-    animationResourcesAttr_.names_.Resize(animationResources_.Size());
-    for (unsigned i = 0; i < animationResources_.Size(); ++i)
-        animationResourcesAttr_.names_[i] = GetResourceName(animationResources_[i]);
-
-    return animationResourcesAttr_;
-}
-
-// ATOMIC END
-
 }
 }

+ 9 - 37
Source/Atomic/Atomic3D/AnimationController.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,13 +24,13 @@
 
 
 #include "../IO/VectorBuffer.h"
 #include "../IO/VectorBuffer.h"
 #include "../Scene/Component.h"
 #include "../Scene/Component.h"
+#include "../Graphics/AnimationState.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class AnimatedModel;
 class AnimatedModel;
 class Animation;
 class Animation;
-class AnimationState;
 struct Bone;
 struct Bone;
 
 
 /// Control data for an animation.
 /// Control data for an animation.
@@ -81,9 +81,9 @@ struct AnimationControl
 };
 };
 
 
 /// %Component that drives an AnimatedModel's animations.
 /// %Component that drives an AnimatedModel's animations.
-class ATOMIC_API AnimationController : public Component
+class URHO3D_API AnimationController : public Component
 {
 {
-    OBJECT(AnimationController);
+    URHO3D_OBJECT(AnimationController, Component);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -129,6 +129,8 @@ public:
     bool SetAutoFade(const String& name, float fadeOutTime);
     bool SetAutoFade(const String& name, float fadeOutTime);
     /// Set whether an animation auto-removes on completion.
     /// Set whether an animation auto-removes on completion.
     bool SetRemoveOnCompletion(const String& name, bool removeOnCompletion);
     bool SetRemoveOnCompletion(const String& name, bool removeOnCompletion);
+    /// Set animation blending mode. Return true on success.
+    bool SetBlendMode(const String& name, AnimationBlendMode mode);
 
 
     /// Return whether an animation is active. Note that non-looping animations that are being clamped at the end also return true.
     /// Return whether an animation is active. Note that non-looping animations that are being clamped at the end also return true.
     bool IsPlaying(const String& name) const;
     bool IsPlaying(const String& name) const;
@@ -150,6 +152,8 @@ public:
     float GetWeight(const String& name) const;
     float GetWeight(const String& name) const;
     /// Return animation looping.
     /// Return animation looping.
     bool IsLooped(const String& name) const;
     bool IsLooped(const String& name) const;
+    /// Return animation blending mode.
+    AnimationBlendMode GetBlendMode(const String& name) const;
     /// Return animation length.
     /// Return animation length.
     float GetLength(const String& name) const;
     float GetLength(const String& name) const;
     /// Return animation speed.
     /// Return animation speed.
@@ -180,25 +184,6 @@ public:
     /// Return node animation states attribute.
     /// Return node animation states attribute.
     VariantVector GetNodeAnimationStatesAttr() const;
     VariantVector GetNodeAnimationStatesAttr() const;
 
 
-    // ATOMIC BEGIN
-
-    void AddAnimationResource(Animation* animation);
-    void RemoveAnimationResource(Animation* animation);
-    void ClearAnimationResources();
-    const Vector<SharedPtr<Animation>>& GetAnimationResources() { return animationResources_; }
-
-    /// Set animation resources attribute.
-    void SetAnimationResourcesAttr(const ResourceRefList& value);
-    /// Return animation resources attribute.
-    const ResourceRefList& GetAnimationResourcesAttr() const;
-
-    /// Set animation attribute.
-    void SetAnimationAttr(const ResourceRef& value);
-    /// Return animation attribute.
-    ResourceRef GetAnimationAttr() const;
-
-    // ATOMIC END
-
 protected:
 protected:
     /// Handle scene being assigned.
     /// Handle scene being assigned.
     virtual void OnSceneSet(Scene* scene);
     virtual void OnSceneSet(Scene* scene);
@@ -219,19 +204,6 @@ private:
     Vector<SharedPtr<AnimationState> > nodeAnimationStates_;
     Vector<SharedPtr<AnimationState> > nodeAnimationStates_;
     /// Attribute buffer for network replication.
     /// Attribute buffer for network replication.
     mutable VectorBuffer attrBuffer_;
     mutable VectorBuffer attrBuffer_;
-
-    // ATOMIC BEGIN
-
-    SharedPtr<Animation> animation_;
-    bool autoPlay_;
-    bool autoPlayed_;
-
-    /// animation resources    
-    Vector<SharedPtr<Animation>> animationResources_;
-    mutable ResourceRefList animationResourcesAttr_;
-
-    // ATOMIC END
-
 };
 };
 
 
 }
 }

+ 121 - 135
Source/Atomic/Atomic3D/AnimationState.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
-#include "../Atomic3D/AnimatedModel.h"
-#include "../Atomic3D/Animation.h"
-#include "../Atomic3D/AnimationState.h"
+#include "../Graphics/AnimatedModel.h"
+#include "../Graphics/Animation.h"
+#include "../Graphics/AnimationState.h"
 #include "../Graphics/DrawableEvents.h"
 #include "../Graphics/DrawableEvents.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 AnimationStateTrack::AnimationStateTrack() :
 AnimationStateTrack::AnimationStateTrack() :
@@ -52,7 +52,8 @@ AnimationState::AnimationState(AnimatedModel* model, Animation* animation) :
     looped_(false),
     looped_(false),
     weight_(0.0f),
     weight_(0.0f),
     time_(0.0f),
     time_(0.0f),
-    layer_(0)
+    layer_(0),
+    blendingMode_(ABM_LERP)
 {
 {
     // Set default start bone (use all tracks.)
     // Set default start bone (use all tracks.)
     SetStartBone(0);
     SetStartBone(0);
@@ -65,21 +66,22 @@ AnimationState::AnimationState(Node* node, Animation* animation) :
     looped_(false),
     looped_(false),
     weight_(1.0f),
     weight_(1.0f),
     time_(0.0f),
     time_(0.0f),
-    layer_(0)
+    layer_(0),
+    blendingMode_(ABM_LERP)
 {
 {
     if (animation_)
     if (animation_)
     {
     {
         // Setup animation track to scene node mapping
         // Setup animation track to scene node mapping
         if (node_)
         if (node_)
         {
         {
-            const Vector<AnimationTrack>& tracks = animation_->GetTracks();
+            const HashMap<StringHash, AnimationTrack>& tracks = animation_->GetTracks();
             stateTracks_.Clear();
             stateTracks_.Clear();
 
 
-            for (unsigned i = 0; i < tracks.Size(); ++i)
+            for (HashMap<StringHash, AnimationTrack>::ConstIterator i = tracks.Begin(); i != tracks.End(); ++i)
             {
             {
-                const StringHash& nameHash = tracks[i].nameHash_;
+                const StringHash& nameHash = i->second_.nameHash_;
                 AnimationStateTrack stateTrack;
                 AnimationStateTrack stateTrack;
-                stateTrack.track_ = &tracks[i];
+                stateTrack.track_ = &i->second_;
 
 
                 if (node_->GetNameHash() == nameHash || tracks.Size() == 1)
                 if (node_->GetNameHash() == nameHash || tracks.Size() == 1)
                     stateTrack.node_ = node_;
                     stateTrack.node_ = node_;
@@ -89,7 +91,7 @@ AnimationState::AnimationState(Node* node, Animation* animation) :
                     if (targetNode)
                     if (targetNode)
                         stateTrack.node_ = targetNode;
                         stateTrack.node_ = targetNode;
                     else
                     else
-                        LOGWARNING("Node " + tracks[i].name_ + " not found for node animation " + animation_->GetName());
+                        URHO3D_LOGWARNING("Node " + i->second_.name_ + " not found for node animation " + animation_->GetName());
                 }
                 }
 
 
                 if (stateTrack.node_)
                 if (stateTrack.node_)
@@ -124,20 +126,20 @@ void AnimationState::SetStartBone(Bone* startBone)
 
 
     startBone_ = startBone;
     startBone_ = startBone;
 
 
-    const Vector<AnimationTrack>& tracks = animation_->GetTracks();
+    const HashMap<StringHash, AnimationTrack>& tracks = animation_->GetTracks();
     stateTracks_.Clear();
     stateTracks_.Clear();
 
 
     if (!startBone->node_)
     if (!startBone->node_)
         return;
         return;
 
 
-    for (unsigned i = 0; i < tracks.Size(); ++i)
+    for (HashMap<StringHash, AnimationTrack>::ConstIterator i = tracks.Begin(); i != tracks.End(); ++i)
     {
     {
         AnimationStateTrack stateTrack;
         AnimationStateTrack stateTrack;
-        stateTrack.track_ = &tracks[i];
+        stateTrack.track_ = &i->second_;
 
 
         // Include those tracks that are either the start bone itself, or its children
         // Include those tracks that are either the start bone itself, or its children
         Bone* trackBone = 0;
         Bone* trackBone = 0;
-        const StringHash& nameHash = tracks[i].nameHash_;
+        const StringHash& nameHash = i->second_.nameHash_;
 
 
         if (nameHash == startBone->nameHash_)
         if (nameHash == startBone->nameHash_)
             trackBone = startBone;
             trackBone = startBone;
@@ -178,6 +180,18 @@ void AnimationState::SetWeight(float weight)
     }
     }
 }
 }
 
 
+void AnimationState::SetBlendMode(AnimationBlendMode mode)
+{
+    if (model_)
+    {
+        if (blendingMode_ != mode)
+        {
+            blendingMode_ = mode;
+            model_->MarkAnimationDirty();
+        }
+    }
+}
+
 void AnimationState::SetTime(float time)
 void AnimationState::SetTime(float time)
 {
 {
     if (!animation_)
     if (!animation_)
@@ -249,18 +263,54 @@ void AnimationState::AddTime(float delta)
     if (delta == 0.0f || length == 0.0f)
     if (delta == 0.0f || length == 0.0f)
         return;
         return;
 
 
+    bool sendFinishEvent = false;
+
     float oldTime = GetTime();
     float oldTime = GetTime();
     float time = oldTime + delta;
     float time = oldTime + delta;
     if (looped_)
     if (looped_)
     {
     {
         while (time >= length)
         while (time >= length)
+        {
             time -= length;
             time -= length;
+            sendFinishEvent = true;
+        }
         while (time < 0.0f)
         while (time < 0.0f)
+        {
             time += length;
             time += length;
+            sendFinishEvent = true;
+        }
     }
     }
 
 
     SetTime(time);
     SetTime(time);
 
 
+    if (!looped_)
+    {
+        if (delta > 0.0f && oldTime < length && GetTime() == length)
+            sendFinishEvent = true;
+        else if (delta < 0.0f && oldTime > 0.0f && GetTime() == 0.0f)
+            sendFinishEvent = true;
+    }
+
+    // Process finish event
+    if (sendFinishEvent)
+    {
+        using namespace AnimationFinished;
+
+        WeakPtr<AnimationState> self(this);
+        WeakPtr<Node> senderNode(model_ ? model_->GetNode() : node_);
+
+        VariantMap& eventData = senderNode->GetEventDataMap();
+        eventData[P_NODE] = senderNode;
+        eventData[P_ANIMATION] = animation_;
+        eventData[P_NAME] = animation_->GetAnimationName();
+        eventData[P_LOOPED] = looped_;
+
+        // Note: this may cause arbitrary deletion of animation states, including the one we are currently processing
+        senderNode->SendEvent(E_ANIMATIONFINISHED, eventData);
+        if (senderNode.Expired() || self.Expired())
+            return;
+    }
+
     // Process animation triggers
     // Process animation triggers
     if (animation_->GetNumTriggers())
     if (animation_->GetNumTriggers())
     {
     {
@@ -296,14 +346,20 @@ void AnimationState::AddTime(float delta)
             {
             {
                 using namespace AnimationTrigger;
                 using namespace AnimationTrigger;
 
 
-                Node* senderNode = model_ ? model_->GetNode() : node_;
+                WeakPtr<AnimationState> self(this);
+                WeakPtr<Node> senderNode(model_ ? model_->GetNode() : node_);
 
 
                 VariantMap& eventData = senderNode->GetEventDataMap();
                 VariantMap& eventData = senderNode->GetEventDataMap();
                 eventData[P_NODE] = senderNode;
                 eventData[P_NODE] = senderNode;
+                eventData[P_ANIMATION] = animation_;
                 eventData[P_NAME] = animation_->GetAnimationName();
                 eventData[P_NAME] = animation_->GetAnimationName();
                 eventData[P_TIME] = i->time_;
                 eventData[P_TIME] = i->time_;
                 eventData[P_DATA] = i->data_;
                 eventData[P_DATA] = i->data_;
+
+                // Note: this may cause arbitrary deletion of animation states, including the one we are currently processing
                 senderNode->SendEvent(E_ANIMATIONTRIGGER, eventData);
                 senderNode->SendEvent(E_ANIMATIONTRIGGER, eventData);
+                if (senderNode.Expired() || self.Expired())
+                    return;
             }
             }
         }
         }
     }
     }
@@ -410,11 +466,8 @@ void AnimationState::ApplyToModel()
         // Do not apply if zero effective weight or the bone has animation disabled
         // Do not apply if zero effective weight or the bone has animation disabled
         if (Equals(finalWeight, 0.0f) || !stateTrack.bone_->animated_)
         if (Equals(finalWeight, 0.0f) || !stateTrack.bone_->animated_)
             continue;
             continue;
-
-        if (Equals(finalWeight, 1.0f))
-            ApplyTrackFullWeightSilent(stateTrack);
-        else
-            ApplyTrackBlendedSilent(stateTrack, finalWeight);
+            
+        ApplyTrack(stateTrack, finalWeight, true);
     }
     }
 }
 }
 
 
@@ -422,10 +475,10 @@ void AnimationState::ApplyToNodes()
 {
 {
     // When applying to a node hierarchy, can only use full weight (nothing to blend to)
     // When applying to a node hierarchy, can only use full weight (nothing to blend to)
     for (Vector<AnimationStateTrack>::Iterator i = stateTracks_.Begin(); i != stateTracks_.End(); ++i)
     for (Vector<AnimationStateTrack>::Iterator i = stateTracks_.Begin(); i != stateTracks_.End(); ++i)
-        ApplyTrackFullWeight(*i);
+        ApplyTrack(*i, 1.0f, false);
 }
 }
 
 
-void AnimationState::ApplyTrackFullWeight(AnimationStateTrack& stateTrack)
+void AnimationState::ApplyTrack(AnimationStateTrack& stateTrack, float weight, bool silent)
 {
 {
     const AnimationTrack* track = stateTrack.track_;
     const AnimationTrack* track = stateTrack.track_;
     Node* node = stateTrack.node_;
     Node* node = stateTrack.node_;
@@ -453,17 +506,11 @@ void AnimationState::ApplyTrackFullWeight(AnimationStateTrack& stateTrack)
     const AnimationKeyFrame* keyFrame = &track->keyFrames_[frame];
     const AnimationKeyFrame* keyFrame = &track->keyFrames_[frame];
     unsigned char channelMask = track->channelMask_;
     unsigned char channelMask = track->channelMask_;
 
 
-    if (!interpolate)
-    {
-        // No interpolation, full weight
-        if (channelMask & CHANNEL_POSITION)
-            node->SetPosition(keyFrame->position_);
-        if (channelMask & CHANNEL_ROTATION)
-            node->SetRotation(keyFrame->rotation_);
-        if (channelMask & CHANNEL_SCALE)
-            node->SetScale(keyFrame->scale_);
-    }
-    else
+    Vector3 newPosition;
+    Quaternion newRotation;
+    Vector3 newScale;
+
+    if (interpolate)
     {
     {
         const AnimationKeyFrame* nextKeyFrame = &track->keyFrames_[nextFrame];
         const AnimationKeyFrame* nextKeyFrame = &track->keyFrames_[nextFrame];
         float timeInterval = nextKeyFrame->time_ - keyFrame->time_;
         float timeInterval = nextKeyFrame->time_ - keyFrame->time_;
@@ -471,134 +518,73 @@ void AnimationState::ApplyTrackFullWeight(AnimationStateTrack& stateTrack)
             timeInterval += animation_->GetLength();
             timeInterval += animation_->GetLength();
         float t = timeInterval > 0.0f ? (time_ - keyFrame->time_) / timeInterval : 1.0f;
         float t = timeInterval > 0.0f ? (time_ - keyFrame->time_) / timeInterval : 1.0f;
 
 
-        // Interpolation, full weight
         if (channelMask & CHANNEL_POSITION)
         if (channelMask & CHANNEL_POSITION)
-            node->SetPosition(keyFrame->position_.Lerp(nextKeyFrame->position_, t));
+            newPosition = keyFrame->position_.Lerp(nextKeyFrame->position_, t);
         if (channelMask & CHANNEL_ROTATION)
         if (channelMask & CHANNEL_ROTATION)
-            node->SetRotation(keyFrame->rotation_.Slerp(nextKeyFrame->rotation_, t));
+            newRotation = keyFrame->rotation_.Slerp(nextKeyFrame->rotation_, t);
         if (channelMask & CHANNEL_SCALE)
         if (channelMask & CHANNEL_SCALE)
-            node->SetScale(keyFrame->scale_.Lerp(nextKeyFrame->scale_, t));
+            newScale = keyFrame->scale_.Lerp(nextKeyFrame->scale_, t);
     }
     }
-}
-
-void AnimationState::ApplyTrackFullWeightSilent(AnimationStateTrack& stateTrack)
-{
-    const AnimationTrack* track = stateTrack.track_;
-    Node* node = stateTrack.node_;
-
-    if (track->keyFrames_.Empty() || !node)
-        return;
-
-    unsigned& frame = stateTrack.keyFrame_;
-    track->GetKeyFrameIndex(time_, frame);
-
-    // Check if next frame to interpolate to is valid, or if wrapping is needed (looping animation only)
-    unsigned nextFrame = frame + 1;
-    bool interpolate = true;
-    if (nextFrame >= track->keyFrames_.Size())
-    {
-        if (!looped_)
-        {
-            nextFrame = frame;
-            interpolate = false;
-        }
-        else
-            nextFrame = 0;
-    }
-
-    const AnimationKeyFrame* keyFrame = &track->keyFrames_[frame];
-    unsigned char channelMask = track->channelMask_;
-
-    if (!interpolate)
+    else
     {
     {
-        // No interpolation, full weight
         if (channelMask & CHANNEL_POSITION)
         if (channelMask & CHANNEL_POSITION)
-            node->SetPositionSilent(keyFrame->position_);
+            newPosition = keyFrame->position_;
         if (channelMask & CHANNEL_ROTATION)
         if (channelMask & CHANNEL_ROTATION)
-            node->SetRotationSilent(keyFrame->rotation_);
+            newRotation = keyFrame->rotation_;
         if (channelMask & CHANNEL_SCALE)
         if (channelMask & CHANNEL_SCALE)
-            node->SetScaleSilent(keyFrame->scale_);
+            newScale = keyFrame->scale_;
     }
     }
-    else
+    
+    if (blendingMode_ == ABM_ADDITIVE) // not ABM_LERP
     {
     {
-        const AnimationKeyFrame* nextKeyFrame = &track->keyFrames_[nextFrame];
-        float timeInterval = nextKeyFrame->time_ - keyFrame->time_;
-        if (timeInterval < 0.0f)
-            timeInterval += animation_->GetLength();
-        float t = timeInterval > 0.0f ? (time_ - keyFrame->time_) / timeInterval : 1.0f;
-
-        // Interpolation, full weight
         if (channelMask & CHANNEL_POSITION)
         if (channelMask & CHANNEL_POSITION)
-            node->SetPositionSilent(keyFrame->position_.Lerp(nextKeyFrame->position_, t));
+        {
+            Vector3 delta = newPosition - stateTrack.bone_->initialPosition_;
+            newPosition = node->GetPosition() + delta * weight;
+        }
         if (channelMask & CHANNEL_ROTATION)
         if (channelMask & CHANNEL_ROTATION)
-            node->SetRotationSilent(keyFrame->rotation_.Slerp(nextKeyFrame->rotation_, t));
+        {
+            Quaternion delta = newRotation * stateTrack.bone_->initialRotation_.Inverse();
+            newRotation = (delta * node->GetRotation()).Normalized();
+            if (!Equals(weight, 1.0f))
+                newRotation = node->GetRotation().Slerp(newRotation, weight);
+        }
         if (channelMask & CHANNEL_SCALE)
         if (channelMask & CHANNEL_SCALE)
-            node->SetScaleSilent(keyFrame->scale_.Lerp(nextKeyFrame->scale_, t));
+        {
+            Vector3 delta = newScale - stateTrack.bone_->initialScale_;
+            newScale = node->GetScale() + delta * weight;
+        }
     }
     }
-}
-
-void AnimationState::ApplyTrackBlendedSilent(AnimationStateTrack& stateTrack, float weight)
-{
-    const AnimationTrack* track = stateTrack.track_;
-    Node* node = stateTrack.node_;
-
-    if (track->keyFrames_.Empty() || !node)
-        return;
-
-    unsigned& frame = stateTrack.keyFrame_;
-    track->GetKeyFrameIndex(time_, frame);
-
-    // Check if next frame to interpolate to is valid, or if wrapping is needed (looping animation only)
-    unsigned nextFrame = frame + 1;
-    bool interpolate = true;
-    if (nextFrame >= track->keyFrames_.Size())
+    else
     {
     {
-        if (!looped_)
+        if (!Equals(weight, 1.0f)) // not full weight
         {
         {
-            nextFrame = frame;
-            interpolate = false;
+            if (channelMask & CHANNEL_POSITION)
+                newPosition = node->GetPosition().Lerp(newPosition, weight);
+            if (channelMask & CHANNEL_ROTATION)
+                newRotation = node->GetRotation().Slerp(newRotation, weight);
+            if (channelMask & CHANNEL_SCALE)
+                newScale = node->GetScale().Lerp(newScale, weight);
         }
         }
-        else
-            nextFrame = 0;
     }
     }
-
-    const AnimationKeyFrame* keyFrame = &track->keyFrames_[frame];
-    unsigned char channelMask = track->channelMask_;
-
-    if (!interpolate)
+    
+    if (silent)
     {
     {
-        // No interpolation, blend between old transform & animation
         if (channelMask & CHANNEL_POSITION)
         if (channelMask & CHANNEL_POSITION)
-            node->SetPositionSilent(node->GetPosition().Lerp(keyFrame->position_, weight));
+            node->SetPositionSilent(newPosition);
         if (channelMask & CHANNEL_ROTATION)
         if (channelMask & CHANNEL_ROTATION)
-            node->SetRotationSilent(node->GetRotation().Slerp(keyFrame->rotation_, weight));
+            node->SetRotationSilent(newRotation);
         if (channelMask & CHANNEL_SCALE)
         if (channelMask & CHANNEL_SCALE)
-            node->SetScaleSilent(node->GetScale().Lerp(keyFrame->scale_, weight));
+            node->SetScaleSilent(newScale);
     }
     }
     else
     else
     {
     {
-        const AnimationKeyFrame* nextKeyFrame = &track->keyFrames_[nextFrame];
-        float timeInterval = nextKeyFrame->time_ - keyFrame->time_;
-        if (timeInterval < 0.0f)
-            timeInterval += animation_->GetLength();
-        float t = timeInterval > 0.0f ? (time_ - keyFrame->time_) / timeInterval : 1.0f;
-
-        // Interpolation, blend between old transform & animation
         if (channelMask & CHANNEL_POSITION)
         if (channelMask & CHANNEL_POSITION)
-        {
-            node->SetPositionSilent(node->GetPosition().Lerp(
-                keyFrame->position_.Lerp(nextKeyFrame->position_, t), weight));
-        }
+            node->SetPosition(newPosition);
         if (channelMask & CHANNEL_ROTATION)
         if (channelMask & CHANNEL_ROTATION)
-        {
-            node->SetRotationSilent(node->GetRotation().Slerp(
-                keyFrame->rotation_.Slerp(nextKeyFrame->rotation_, t), weight));
-        }
+            node->SetRotation(newRotation);
         if (channelMask & CHANNEL_SCALE)
         if (channelMask & CHANNEL_SCALE)
-        {
-            node->SetScaleSilent(node->GetScale().Lerp(
-                keyFrame->scale_.Lerp(nextKeyFrame->scale_, t), weight));
-        }
+            node->SetScale(newScale);
     }
     }
 }
 }
 
 

+ 21 - 11
Source/Atomic/Atomic3D/AnimationState.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,7 @@
 #include "../Container/HashMap.h"
 #include "../Container/HashMap.h"
 #include "../Container/Ptr.h"
 #include "../Container/Ptr.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class Animation;
 class Animation;
@@ -36,6 +36,15 @@ class Skeleton;
 struct AnimationTrack;
 struct AnimationTrack;
 struct Bone;
 struct Bone;
 
 
+/// %Animation blending mode.
+enum AnimationBlendMode
+{
+    // Lerp blending (default)
+    ABM_LERP = 0,
+    // Additive blending based on difference from bind pose
+    ABM_ADDITIVE
+};
+
 /// %Animation instance per-track data.
 /// %Animation instance per-track data.
 struct AnimationStateTrack
 struct AnimationStateTrack
 {
 {
@@ -57,10 +66,8 @@ struct AnimationStateTrack
 };
 };
 
 
 /// %Animation instance.
 /// %Animation instance.
-class ATOMIC_API AnimationState : public RefCounted
+class URHO3D_API AnimationState : public RefCounted
 {
 {
-    REFCOUNTED(AnimationState)
-
 public:
 public:
     /// Construct with animated model and animation pointers.
     /// Construct with animated model and animation pointers.
     AnimationState(AnimatedModel* model, Animation* animation);
     AnimationState(AnimatedModel* model, Animation* animation);
@@ -75,6 +82,8 @@ public:
     void SetLooped(bool looped);
     void SetLooped(bool looped);
     /// Set blending weight.
     /// Set blending weight.
     void SetWeight(float weight);
     void SetWeight(float weight);
+    /// Set blending mode.
+    void SetBlendMode(AnimationBlendMode mode);
     /// Set time position. Does not fire animation triggers.
     /// Set time position. Does not fire animation triggers.
     void SetTime(float time);
     void SetTime(float time);
     /// Set per-bone blending weight by track index. Default is 1.0 (full), is multiplied  with the state's blending weight when applying the animation. Optionally recurses to child bones.
     /// Set per-bone blending weight by track index. Default is 1.0 (full), is multiplied  with the state's blending weight when applying the animation. Optionally recurses to child bones.
@@ -121,6 +130,9 @@ public:
     /// Return blending weight.
     /// Return blending weight.
     float GetWeight() const { return weight_; }
     float GetWeight() const { return weight_; }
 
 
+    /// Return blending mode.
+    AnimationBlendMode GetBlendMode() const { return blendingMode_; }
+
     /// Return time position.
     /// Return time position.
     float GetTime() const { return time_; }
     float GetTime() const { return time_; }
 
 
@@ -138,12 +150,8 @@ private:
     void ApplyToModel();
     void ApplyToModel();
     /// Apply animation to a scene node hierarchy.
     /// Apply animation to a scene node hierarchy.
     void ApplyToNodes();
     void ApplyToNodes();
-    /// Apply animation track to a scene node, full weight.
-    void ApplyTrackFullWeight(AnimationStateTrack& stateTrack);
-    /// Apply animation track to a scene node, full weight. Apply transform changes silently without marking the node dirty.
-    void ApplyTrackFullWeightSilent(AnimationStateTrack& stateTrack);
-    /// Apply animation track to a scene node, blended with current node transform. Apply transform changes silently without marking the node dirty.
-    void ApplyTrackBlendedSilent(AnimationStateTrack& stateTrack, float weight);
+    /// Apply track.
+    void ApplyTrack(AnimationStateTrack& stateTrack, float weight, bool silent);
 
 
     /// Animated model (model mode.)
     /// Animated model (model mode.)
     WeakPtr<AnimatedModel> model_;
     WeakPtr<AnimatedModel> model_;
@@ -163,6 +171,8 @@ private:
     float time_;
     float time_;
     /// Blending layer.
     /// Blending layer.
     unsigned char layer_;
     unsigned char layer_;
+    /// Blending mode.
+    AnimationBlendMode blendingMode_;
 };
 };
 
 
 }
 }

+ 312 - 147
Source/Atomic/Atomic3D/BillboardSet.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,12 +25,11 @@
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
 #include "../Graphics/Batch.h"
 #include "../Graphics/Batch.h"
-#include "../Atomic3D/BillboardSet.h"
+#include "../Graphics/BillboardSet.h"
 #include "../Graphics/Camera.h"
 #include "../Graphics/Camera.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/Graphics.h"
 #include "../Graphics/Graphics.h"
 #include "../Graphics/IndexBuffer.h"
 #include "../Graphics/IndexBuffer.h"
-#include "../Graphics/Material.h"
 #include "../Graphics/OctreeQuery.h"
 #include "../Graphics/OctreeQuery.h"
 #include "../Graphics/VertexBuffer.h"
 #include "../Graphics/VertexBuffer.h"
 #include "../IO/MemoryBuffer.h"
 #include "../IO/MemoryBuffer.h"
@@ -39,7 +38,7 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const char* GEOMETRY_CATEGORY;
 extern const char* GEOMETRY_CATEGORY;
@@ -53,22 +52,13 @@ const char* faceCameraModeNames[] =
     "Rotate Y",
     "Rotate Y",
     "LookAt XYZ",
     "LookAt XYZ",
     "LookAt Y",
     "LookAt Y",
+    "Direction",
     0
     0
 };
 };
 
 
 inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
 inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
 {
 {
-    return lhs->GetSortDistance() > rhs->GetSortDistance();
-}
-
-Billboard::Billboard() 
-{
-
-}
-
-Billboard::~Billboard() 
-{
-
+    return lhs->sortDistance_ > rhs->sortDistance_;
 }
 }
 
 
 BillboardSet::BillboardSet(Context* context) :
 BillboardSet::BillboardSet(Context* context) :
@@ -78,6 +68,7 @@ BillboardSet::BillboardSet(Context* context) :
     relative_(true),
     relative_(true),
     scaled_(true),
     scaled_(true),
     sorted_(false),
     sorted_(false),
+    fixedScreenSize_(false),
     faceCameraMode_(FC_ROTATE_XYZ),
     faceCameraMode_(FC_ROTATE_XYZ),
     geometry_(new Geometry(context)),
     geometry_(new Geometry(context)),
     vertexBuffer_(new VertexBuffer(context_)),
     vertexBuffer_(new VertexBuffer(context_)),
@@ -85,11 +76,13 @@ BillboardSet::BillboardSet(Context* context) :
     bufferSizeDirty_(true),
     bufferSizeDirty_(true),
     bufferDirty_(true),
     bufferDirty_(true),
     forceUpdate_(false),
     forceUpdate_(false),
+    geometryTypeUpdate_(false),
     sortThisFrame_(false),
     sortThisFrame_(false),
+    hasOrthoCamera_(false),
     sortFrameNumber_(0),
     sortFrameNumber_(0),
     previousOffset_(Vector3::ZERO)
     previousOffset_(Vector3::ZERO)
 {
 {
-    geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1 | MASK_TEXCOORD2);
+    geometry_->SetVertexBuffer(0, vertexBuffer_);
     geometry_->SetIndexBuffer(indexBuffer_);
     geometry_->SetIndexBuffer(indexBuffer_);
 
 
     batches_.Resize(1);
     batches_.Resize(1);
@@ -106,22 +99,23 @@ void BillboardSet::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<BillboardSet>(GEOMETRY_CATEGORY);
     context->RegisterFactory<BillboardSet>(GEOMETRY_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()),
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()),
         AM_DEFAULT);
         AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Relative Position", IsRelative, SetRelative, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Relative Scale", IsScaled, SetScaled, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Sort By Distance", IsSorted, SetSorted, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
-    ENUM_ATTRIBUTE("Face Camera Mode", faceCameraMode_, faceCameraModeNames, FC_ROTATE_XYZ, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Drawable);
-    MIXED_ACCESSOR_ATTRIBUTE("Billboards", GetBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector,
+    URHO3D_ACCESSOR_ATTRIBUTE("Relative Position", IsRelative, SetRelative, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Relative Scale", IsScaled, SetScaled, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Sort By Distance", IsSorted, SetSorted, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Fixed Screen Size", IsFixedScreenSize, SetFixedScreenSize, bool, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
+    URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Face Camera Mode", GetFaceCameraMode, SetFaceCameraMode, FaceCameraMode, faceCameraModeNames, FC_ROTATE_XYZ, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Billboards", GetBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector,
         AM_FILE);
         AM_FILE);
-    ACCESSOR_ATTRIBUTE("Network Billboards", GetNetBillboardsAttr, SetNetBillboardsAttr, PODVector<unsigned char>,
+    URHO3D_ACCESSOR_ATTRIBUTE("Network Billboards", GetNetBillboardsAttr, SetNetBillboardsAttr, PODVector<unsigned char>,
         Variant::emptyBuffer, AM_NET | AM_NOEDIT);
         Variant::emptyBuffer, AM_NET | AM_NOEDIT);
 }
 }
 
 
@@ -144,12 +138,14 @@ void BillboardSet::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQue
 
 
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     {
     {
-        if (!billboards_[i]->enabled_)
+        if (!billboards_[i].enabled_)
             continue;
             continue;
 
 
         // Approximate the billboards as spheres for raycasting
         // Approximate the billboards as spheres for raycasting
-        float size = INV_SQRT_TWO * (billboards_[i]->size_.x_ * billboardScale.x_ + billboards_[i]->size_.y_ * billboardScale.y_);
-        Vector3 center = billboardTransform * billboards_[i]->position_;
+        float size = INV_SQRT_TWO * (billboards_[i].size_.x_ * billboardScale.x_ + billboards_[i].size_.y_ * billboardScale.y_);
+        if (fixedScreenSize_)
+            size *= billboards_[i].screenScaleFactor_;
+        Vector3 center = billboardTransform * billboards_[i].position_;
         Sphere billboardSphere(center, size);
         Sphere billboardSphere(center, size);
 
 
         float distance = query.ray_.HitDistance(billboardSphere);
         float distance = query.ray_.HitDistance(billboardSphere);
@@ -180,8 +176,19 @@ void BillboardSet::UpdateBatches(const FrameInfo& frame)
     Vector3 worldPos = node_->GetWorldPosition();
     Vector3 worldPos = node_->GetWorldPosition();
     Vector3 offset = (worldPos - frame.camera_->GetNode()->GetWorldPosition());
     Vector3 offset = (worldPos - frame.camera_->GetNode()->GetWorldPosition());
     // Sort if position relative to camera has changed
     // Sort if position relative to camera has changed
-    if (offset != previousOffset_ & sorted_)
-        sortThisFrame_ = true;
+    if (offset != previousOffset_ || frame.camera_->IsOrthographic() != hasOrthoCamera_)
+    {
+        if (sorted_)
+            sortThisFrame_ = true;
+        if (faceCameraMode_ == FC_DIRECTION)
+            bufferDirty_ = true;
+
+        hasOrthoCamera_ = frame.camera_->IsOrthographic();
+
+        // Calculate fixed screen size scale factor for billboards if needed
+        if (fixedScreenSize_)
+            CalculateFixedScreenSize(frame);
+    }
 
 
     distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center());
     distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center());
 
 
@@ -204,11 +211,9 @@ void BillboardSet::UpdateBatches(const FrameInfo& frame)
 
 
 void BillboardSet::UpdateGeometry(const FrameInfo& frame)
 void BillboardSet::UpdateGeometry(const FrameInfo& frame)
 {
 {
-    if (bufferSizeDirty_ || indexBuffer_->IsDataLost())
-        UpdateBufferSize();
-
-    if (bufferDirty_ || sortThisFrame_ || vertexBuffer_->IsDataLost())
-        UpdateVertexBuffer(frame);
+    // If rendering from multiple views and fixed screen size is in use, re-update scale factors before each render
+    if (fixedScreenSize_ && viewCameras_.Size() > 1)
+        CalculateFixedScreenSize(frame);
 
 
     // If using camera facing, re-update the rotation for the current view now
     // If using camera facing, re-update the rotation for the current view now
     if (faceCameraMode_ != FC_NONE)
     if (faceCameraMode_ != FC_NONE)
@@ -216,15 +221,20 @@ void BillboardSet::UpdateGeometry(const FrameInfo& frame)
         transforms_[1] = Matrix3x4(Vector3::ZERO, frame.camera_->GetFaceCameraRotation(node_->GetWorldPosition(),
         transforms_[1] = Matrix3x4(Vector3::ZERO, frame.camera_->GetFaceCameraRotation(node_->GetWorldPosition(),
             node_->GetWorldRotation(), faceCameraMode_), Vector3::ONE);
             node_->GetWorldRotation(), faceCameraMode_), Vector3::ONE);
     }
     }
+
+    if (bufferSizeDirty_ || indexBuffer_->IsDataLost())
+        UpdateBufferSize();
+
+    if (bufferDirty_ || sortThisFrame_ || vertexBuffer_->IsDataLost())
+        UpdateVertexBuffer(frame);
 }
 }
 
 
 UpdateGeometryType BillboardSet::GetUpdateGeometryType()
 UpdateGeometryType BillboardSet::GetUpdateGeometryType()
 {
 {
     // If using camera facing, always need some kind of geometry update, in case the billboard set is rendered from several views
     // If using camera facing, always need some kind of geometry update, in case the billboard set is rendered from several views
-    if (bufferDirty_ || bufferSizeDirty_ || vertexBuffer_->IsDataLost() || indexBuffer_->IsDataLost() || sortThisFrame_)
+    if (bufferDirty_ || bufferSizeDirty_ || vertexBuffer_->IsDataLost() || indexBuffer_->IsDataLost() || sortThisFrame_ ||
+        faceCameraMode_ != FC_NONE || fixedScreenSize_)
         return UPDATE_MAIN_THREAD;
         return UPDATE_MAIN_THREAD;
-    else if (faceCameraMode_ != FC_NONE)
-        return UPDATE_WORKER_THREAD;
     else
     else
         return UPDATE_NONE;
         return UPDATE_NONE;
 }
 }
@@ -244,20 +254,22 @@ void BillboardSet::SetNumBillboards(unsigned num)
         num = MAX_BILLBOARDS;
         num = MAX_BILLBOARDS;
 
 
     unsigned oldNum = billboards_.Size();
     unsigned oldNum = billboards_.Size();
+    if (num == oldNum)
+        return;
 
 
     billboards_.Resize(num);
     billboards_.Resize(num);
 
 
     // Set default values to new billboards
     // Set default values to new billboards
     for (unsigned i = oldNum; i < num; ++i)
     for (unsigned i = oldNum; i < num; ++i)
     {
     {
-        SharedPtr<Billboard> bb(new Billboard());
-        bb->position_ = Vector3::ZERO;
-        bb->size_ = Vector2::ONE;
-        bb->uv_ = Rect::POSITIVE;
-        bb->color_ = Color(1.0f, 1.0f, 1.0f);
-        bb->rotation_ = 0.0f;
-        bb->enabled_ = false;
-        billboards_[i] = bb;
+        billboards_[i].position_ = Vector3::ZERO;
+        billboards_[i].size_ = Vector2::ONE;
+        billboards_[i].uv_ = Rect::POSITIVE;
+        billboards_[i].color_ = Color(1.0f, 1.0f, 1.0f);
+        billboards_[i].rotation_ = 0.0f;
+        billboards_[i].direction_ = Vector3::UP;
+        billboards_[i].enabled_ = false;
+        billboards_[i].screenScaleFactor_ = 1.0f;
     }
     }
 
 
     bufferSizeDirty_ = true;
     bufferSizeDirty_ = true;
@@ -282,10 +294,30 @@ void BillboardSet::SetSorted(bool enable)
     Commit();
     Commit();
 }
 }
 
 
+void BillboardSet::SetFixedScreenSize(bool enable)
+{
+    fixedScreenSize_ = enable;
+    Commit();
+}
+
 void BillboardSet::SetFaceCameraMode(FaceCameraMode mode)
 void BillboardSet::SetFaceCameraMode(FaceCameraMode mode)
 {
 {
-    faceCameraMode_ = mode;
-    MarkNetworkUpdate();
+    if ((faceCameraMode_ != FC_DIRECTION && mode == FC_DIRECTION) || (faceCameraMode_ == FC_DIRECTION && mode != FC_DIRECTION))
+    {
+        faceCameraMode_ = mode;
+        if (faceCameraMode_ == FC_DIRECTION)
+            batches_[0].geometryType_ = GEOM_DIRBILLBOARD;
+        else
+            batches_[0].geometryType_ = GEOM_BILLBOARD;
+        geometryTypeUpdate_ = true;
+        bufferSizeDirty_ = true;
+        Commit();
+    }
+    else
+    {
+        faceCameraMode_ = mode;
+        MarkNetworkUpdate();
+    }
 }
 }
 
 
 void BillboardSet::SetAnimationLodBias(float bias)
 void BillboardSet::SetAnimationLodBias(float bias)
@@ -307,7 +339,7 @@ Material* BillboardSet::GetMaterial() const
 
 
 Billboard* BillboardSet::GetBillboard(unsigned index)
 Billboard* BillboardSet::GetBillboard(unsigned index)
 {
 {
-    return index < billboards_.Size() ? billboards_[index] : (Billboard*)0;
+    return index < billboards_.Size() ? &billboards_[index] : (Billboard*)0;
 }
 }
 
 
 void BillboardSet::SetMaterialAttr(const ResourceRef& value)
 void BillboardSet::SetMaterialAttr(const ResourceRef& value)
@@ -322,16 +354,34 @@ void BillboardSet::SetBillboardsAttr(const VariantVector& value)
     unsigned numBillboards = index < value.Size() ? value[index++].GetUInt() : 0;
     unsigned numBillboards = index < value.Size() ? value[index++].GetUInt() : 0;
     SetNumBillboards(numBillboards);
     SetNumBillboards(numBillboards);
 
 
-    for (Vector<SharedPtr<Billboard>>::Iterator i = billboards_.Begin(); i != billboards_.End() && index < value.Size(); ++i)
+    // Dealing with old billboard format
+    if (value.Size() == billboards_.Size() * 6 + 1)
     {
     {
-        Billboard *bb = i->Get();
-        bb->position_ = value[index++].GetVector3();
-        bb->size_ = value[index++].GetVector2();
-        Vector4 uv = value[index++].GetVector4();
-        bb->uv_ = Rect(uv.x_, uv.y_, uv.z_, uv.w_);
-        bb->color_ = value[index++].GetColor();
-        bb->rotation_ = value[index++].GetFloat();
-        bb->enabled_ = value[index++].GetBool();
+        for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End() && index < value.Size(); ++i)
+        {
+            i->position_ = value[index++].GetVector3();
+            i->size_ = value[index++].GetVector2();
+            Vector4 uv = value[index++].GetVector4();
+            i->uv_ = Rect(uv.x_, uv.y_, uv.z_, uv.w_);
+            i->color_ = value[index++].GetColor();
+            i->rotation_ = value[index++].GetFloat();
+            i->enabled_ = value[index++].GetBool();
+        }
+    }
+    // New billboard format
+    else
+    {
+        for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End() && index < value.Size(); ++i)
+        {
+            i->position_ = value[index++].GetVector3();
+            i->size_ = value[index++].GetVector2();
+            Vector4 uv = value[index++].GetVector4();
+            i->uv_ = Rect(uv.x_, uv.y_, uv.z_, uv.w_);
+            i->color_ = value[index++].GetColor();
+            i->rotation_ = value[index++].GetFloat();
+            i->direction_ = value[index++].GetVector3();
+            i->enabled_ = value[index++].GetBool();
+        }
     }
     }
 
 
     Commit();
     Commit();
@@ -343,15 +393,15 @@ void BillboardSet::SetNetBillboardsAttr(const PODVector<unsigned char>& value)
     unsigned numBillboards = buf.ReadVLE();
     unsigned numBillboards = buf.ReadVLE();
     SetNumBillboards(numBillboards);
     SetNumBillboards(numBillboards);
 
 
-    for (Vector<SharedPtr<Billboard>>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
     {
     {
-        Billboard *bb = i->Get();
-        bb->position_ = buf.ReadVector3();
-        bb->size_ = buf.ReadVector2();
-        bb->uv_ = buf.ReadRect();
-        bb->color_ = buf.ReadColor();
-        bb->rotation_ = buf.ReadFloat();
-        bb->enabled_ = buf.ReadBool();
+        i->position_ = buf.ReadVector3();
+        i->size_ = buf.ReadVector2();
+        i->uv_ = buf.ReadRect();
+        i->color_ = buf.ReadColor();
+        i->rotation_ = buf.ReadFloat();
+        i->direction_ = buf.ReadVector3();
+        i->enabled_ = buf.ReadBool();
     }
     }
 
 
     Commit();
     Commit();
@@ -365,18 +415,18 @@ ResourceRef BillboardSet::GetMaterialAttr() const
 VariantVector BillboardSet::GetBillboardsAttr() const
 VariantVector BillboardSet::GetBillboardsAttr() const
 {
 {
     VariantVector ret;
     VariantVector ret;
-    ret.Reserve(billboards_.Size() * 6 + 1);
+    ret.Reserve(billboards_.Size() * 7 + 1);
     ret.Push(billboards_.Size());
     ret.Push(billboards_.Size());
 
 
-    for (Vector<SharedPtr<Billboard>>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
     {
     {
-        Billboard *bb = i->Get();
-        ret.Push(bb->position_);
-        ret.Push(bb->size_);
-        ret.Push(Vector4(bb->uv_.min_.x_, bb->uv_.min_.y_, bb->uv_.max_.x_, bb->uv_.max_.y_));
-        ret.Push(bb->color_);
-        ret.Push(bb->rotation_);
-        ret.Push(bb->enabled_);
+        ret.Push(i->position_);
+        ret.Push(i->size_);
+        ret.Push(Vector4(i->uv_.min_.x_, i->uv_.min_.y_, i->uv_.max_.x_, i->uv_.max_.y_));
+        ret.Push(i->color_);
+        ret.Push(i->rotation_);
+        ret.Push(i->direction_);
+        ret.Push(i->enabled_);
     }
     }
 
 
     return ret;
     return ret;
@@ -387,15 +437,15 @@ const PODVector<unsigned char>& BillboardSet::GetNetBillboardsAttr() const
     attrBuffer_.Clear();
     attrBuffer_.Clear();
     attrBuffer_.WriteVLE(billboards_.Size());
     attrBuffer_.WriteVLE(billboards_.Size());
 
 
-    for (Vector<SharedPtr<Billboard>>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
     {
     {
-        Billboard *bb = i->Get();
-        attrBuffer_.WriteVector3(bb->position_);
-        attrBuffer_.WriteVector2(bb->size_);
-        attrBuffer_.WriteRect(bb->uv_);
-        attrBuffer_.WriteColor(bb->color_);
-        attrBuffer_.WriteFloat(bb->rotation_);
-        attrBuffer_.WriteBool(bb->enabled_);
+        attrBuffer_.WriteVector3(i->position_);
+        attrBuffer_.WriteVector2(i->size_);
+        attrBuffer_.WriteRect(i->uv_);
+        attrBuffer_.WriteColor(i->color_);
+        attrBuffer_.WriteFloat(i->rotation_);
+        attrBuffer_.WriteVector3(i->direction_);
+        attrBuffer_.WriteBool(i->enabled_);
     }
     }
 
 
     return attrBuffer_.GetBuffer();
     return attrBuffer_.GetBuffer();
@@ -411,11 +461,14 @@ void BillboardSet::OnWorldBoundingBoxUpdate()
 
 
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     {
     {
-        if (!billboards_[i]->enabled_)
+        if (!billboards_[i].enabled_)
             continue;
             continue;
 
 
-        float size = INV_SQRT_TWO * (billboards_[i]->size_.x_ * billboardScale.x_ + billboards_[i]->size_.y_ * billboardScale.y_);
-        Vector3 center = billboardTransform * billboards_[i]->position_;
+        float size = INV_SQRT_TWO * (billboards_[i].size_.x_ * billboardScale.x_ + billboards_[i].size_.y_ * billboardScale.y_);
+        if (fixedScreenSize_)
+            size *= billboards_[i].screenScaleFactor_;
+
+        Vector3 center = billboardTransform * billboards_[i].position_;
         Vector3 edge = Vector3::ONE * size;
         Vector3 edge = Vector3::ONE * size;
         worldBox.Merge(BoundingBox(center - edge, center + edge));
         worldBox.Merge(BoundingBox(center - edge, center + edge));
 
 
@@ -432,8 +485,21 @@ void BillboardSet::UpdateBufferSize()
 {
 {
     unsigned numBillboards = billboards_.Size();
     unsigned numBillboards = billboards_.Size();
 
 
-    if (vertexBuffer_->GetVertexCount() != numBillboards * 4)
-        vertexBuffer_->SetSize(numBillboards * 4, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1 | MASK_TEXCOORD2, true);
+    if (vertexBuffer_->GetVertexCount() != numBillboards * 4 || geometryTypeUpdate_)
+    {
+        if (faceCameraMode_ == FC_DIRECTION)
+        {
+            vertexBuffer_->SetSize(numBillboards * 4, MASK_POSITION | MASK_NORMAL | MASK_COLOR | MASK_TEXCOORD1 | MASK_TEXCOORD2, true);
+            geometry_->SetVertexBuffer(0, vertexBuffer_);
+
+        }
+        else
+        {
+            vertexBuffer_->SetSize(numBillboards * 4, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1 | MASK_TEXCOORD2, true);
+            geometry_->SetVertexBuffer(0, vertexBuffer_);
+        }
+        geometryTypeUpdate_ = false;
+    }
     if (indexBuffer_->GetIndexCount() != numBillboards * 6)
     if (indexBuffer_->GetIndexCount() != numBillboards * 6)
         indexBuffer_->SetSize(numBillboards * 6, false);
         indexBuffer_->SetSize(numBillboards * 6, false);
 
 
@@ -492,7 +558,7 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
     // First check number of enabled billboards
     // First check number of enabled billboards
     for (unsigned i = 0; i < numBillboards; ++i)
     for (unsigned i = 0; i < numBillboards; ++i)
     {
     {
-        if (billboards_[i]->enabled_)
+        if (billboards_[i].enabled_)
             ++enabledBillboards;
             ++enabledBillboards;
     }
     }
 
 
@@ -502,12 +568,12 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
     // Then set initial sort order and distances
     // Then set initial sort order and distances
     for (unsigned i = 0; i < numBillboards; ++i)
     for (unsigned i = 0; i < numBillboards; ++i)
     {
     {
-        SharedPtr<Billboard> billboard = billboards_[i];
-        if (billboard->enabled_)
+        Billboard& billboard = billboards_[i];
+        if (billboard.enabled_)
         {
         {
-            sortedBillboards_[index++] = billboard;
+            sortedBillboards_[index++] = &billboard;
             if (sorted_)
             if (sorted_)
-                billboard->sortDistance_ = frame.camera_->GetDistanceSquared(billboardTransform * billboards_[i]->position_);
+                billboard.sortDistance_ = frame.camera_->GetDistanceSquared(billboardTransform * billboards_[i].position_);
         }
         }
     }
     }
 
 
@@ -530,56 +596,127 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
     if (!dest)
     if (!dest)
         return;
         return;
 
 
-    for (unsigned i = 0; i < enabledBillboards; ++i)
+    if (faceCameraMode_ != FC_DIRECTION)
+    {
+        for (unsigned i = 0; i < enabledBillboards; ++i)
+        {
+            Billboard& billboard = *sortedBillboards_[i];
+
+            Vector2 size(billboard.size_.x_ * billboardScale.x_, billboard.size_.y_ * billboardScale.y_);
+            unsigned color = billboard.color_.ToUInt();
+            if (fixedScreenSize_)
+                size *= billboard.screenScaleFactor_;
+
+            float rotationMatrix[2][2];
+            SinCos(billboard.rotation_, rotationMatrix[0][1], rotationMatrix[0][0]);
+            rotationMatrix[1][0] = -rotationMatrix[0][1];
+            rotationMatrix[1][1] = rotationMatrix[0][0];
+
+            dest[0] = billboard.position_.x_;
+            dest[1] = billboard.position_.y_;
+            dest[2] = billboard.position_.z_;
+            ((unsigned&)dest[3]) = color;
+            dest[4] = billboard.uv_.min_.x_;
+            dest[5] = billboard.uv_.min_.y_;
+            dest[6] = -size.x_ * rotationMatrix[0][0] + size.y_ * rotationMatrix[0][1];
+            dest[7] = -size.x_ * rotationMatrix[1][0] + size.y_ * rotationMatrix[1][1];
+
+            dest[8] = billboard.position_.x_;
+            dest[9] = billboard.position_.y_;
+            dest[10] = billboard.position_.z_;
+            ((unsigned&)dest[11]) = color;
+            dest[12] = billboard.uv_.max_.x_;
+            dest[13] = billboard.uv_.min_.y_;
+            dest[14] = size.x_ * rotationMatrix[0][0] + size.y_ * rotationMatrix[0][1];
+            dest[15] = size.x_ * rotationMatrix[1][0] + size.y_ * rotationMatrix[1][1];
+
+            dest[16] = billboard.position_.x_;
+            dest[17] = billboard.position_.y_;
+            dest[18] = billboard.position_.z_;
+            ((unsigned&)dest[19]) = color;
+            dest[20] = billboard.uv_.max_.x_;
+            dest[21] = billboard.uv_.max_.y_;
+            dest[22] = size.x_ * rotationMatrix[0][0] - size.y_ * rotationMatrix[0][1];
+            dest[23] = size.x_ * rotationMatrix[1][0] - size.y_ * rotationMatrix[1][1];
+
+            dest[24] = billboard.position_.x_;
+            dest[25] = billboard.position_.y_;
+            dest[26] = billboard.position_.z_;
+            ((unsigned&)dest[27]) = color;
+            dest[28] = billboard.uv_.min_.x_;
+            dest[29] = billboard.uv_.max_.y_;
+            dest[30] = -size.x_ * rotationMatrix[0][0] - size.y_ * rotationMatrix[0][1];
+            dest[31] = -size.x_ * rotationMatrix[1][0] - size.y_ * rotationMatrix[1][1];
+
+            dest += 32;
+        }
+    }
+    else
     {
     {
-        Billboard& billboard = *sortedBillboards_[i];
-
-        Vector2 size(billboard.size_.x_ * billboardScale.x_, billboard.size_.y_ * billboardScale.y_);
-        unsigned color = billboard.color_.ToUInt();
-
-        float rotationMatrix[2][2];
-        rotationMatrix[0][0] = Cos(billboard.rotation_);
-        rotationMatrix[0][1] = Sin(billboard.rotation_);
-        rotationMatrix[1][0] = -rotationMatrix[0][1];
-        rotationMatrix[1][1] = rotationMatrix[0][0];
-
-        dest[0] = billboard.position_.x_;
-        dest[1] = billboard.position_.y_;
-        dest[2] = billboard.position_.z_;
-        ((unsigned&)dest[3]) = color;
-        dest[4] = billboard.uv_.min_.x_;
-        dest[5] = billboard.uv_.min_.y_;
-        dest[6] = -size.x_ * rotationMatrix[0][0] + size.y_ * rotationMatrix[0][1];
-        dest[7] = -size.x_ * rotationMatrix[1][0] + size.y_ * rotationMatrix[1][1];
-
-        dest[8] = billboard.position_.x_;
-        dest[9] = billboard.position_.y_;
-        dest[10] = billboard.position_.z_;
-        ((unsigned&)dest[11]) = color;
-        dest[12] = billboard.uv_.max_.x_;
-        dest[13] = billboard.uv_.min_.y_;
-        dest[14] = size.x_ * rotationMatrix[0][0] + size.y_ * rotationMatrix[0][1];
-        dest[15] = size.x_ * rotationMatrix[1][0] + size.y_ * rotationMatrix[1][1];
-
-        dest[16] = billboard.position_.x_;
-        dest[17] = billboard.position_.y_;
-        dest[18] = billboard.position_.z_;
-        ((unsigned&)dest[19]) = color;
-        dest[20] = billboard.uv_.max_.x_;
-        dest[21] = billboard.uv_.max_.y_;
-        dest[22] = size.x_ * rotationMatrix[0][0] - size.y_ * rotationMatrix[0][1];
-        dest[23] = size.x_ * rotationMatrix[1][0] - size.y_ * rotationMatrix[1][1];
-
-        dest[24] = billboard.position_.x_;
-        dest[25] = billboard.position_.y_;
-        dest[26] = billboard.position_.z_;
-        ((unsigned&)dest[27]) = color;
-        dest[28] = billboard.uv_.min_.x_;
-        dest[29] = billboard.uv_.max_.y_;
-        dest[30] = -size.x_ * rotationMatrix[0][0] - size.y_ * rotationMatrix[0][1];
-        dest[31] = -size.x_ * rotationMatrix[1][0] - size.y_ * rotationMatrix[1][1];
-
-        dest += 32;
+        for (unsigned i = 0; i < enabledBillboards; ++i)
+        {
+            Billboard& billboard = *sortedBillboards_[i];
+
+            Vector2 size(billboard.size_.x_ * billboardScale.x_, billboard.size_.y_ * billboardScale.y_);
+            unsigned color = billboard.color_.ToUInt();
+            if (fixedScreenSize_)
+                size *= billboard.screenScaleFactor_;
+
+            float rot2D[2][2];
+            SinCos(billboard.rotation_, rot2D[0][1], rot2D[0][0]);
+            rot2D[1][0] = -rot2D[0][1];
+            rot2D[1][1] = rot2D[0][0];
+
+            dest[0] = billboard.position_.x_;
+            dest[1] = billboard.position_.y_;
+            dest[2] = billboard.position_.z_;
+            dest[3] = billboard.direction_.x_;
+            dest[4] = billboard.direction_.y_;
+            dest[5] = billboard.direction_.z_;
+            ((unsigned&)dest[6]) = color;
+            dest[7] = billboard.uv_.min_.x_;
+            dest[8] = billboard.uv_.min_.y_;
+            dest[9] = -size.x_ * rot2D[0][0] + size.y_ * rot2D[0][1];
+            dest[10] = -size.x_ * rot2D[1][0] + size.y_ * rot2D[1][1];
+
+            dest[11] = billboard.position_.x_;
+            dest[12] = billboard.position_.y_;
+            dest[13] = billboard.position_.z_;
+            dest[14] = billboard.direction_.x_;
+            dest[15] = billboard.direction_.y_;
+            dest[16] = billboard.direction_.z_;
+            ((unsigned&)dest[17]) = color;
+            dest[18] = billboard.uv_.max_.x_;
+            dest[19] = billboard.uv_.min_.y_;
+            dest[20] = size.x_ * rot2D[0][0] + size.y_ * rot2D[0][1];
+            dest[21] = size.x_ * rot2D[1][0] + size.y_ * rot2D[1][1];
+
+            dest[22] = billboard.position_.x_;
+            dest[23] = billboard.position_.y_;
+            dest[24] = billboard.position_.z_;
+            dest[25] = billboard.direction_.x_;
+            dest[26] = billboard.direction_.y_;
+            dest[27] = billboard.direction_.z_;
+            ((unsigned&)dest[28]) = color;
+            dest[29] = billboard.uv_.max_.x_;
+            dest[30] = billboard.uv_.max_.y_;
+            dest[31] = size.x_ * rot2D[0][0] - size.y_ * rot2D[0][1];
+            dest[32] = size.x_ * rot2D[1][0] - size.y_ * rot2D[1][1];
+
+            dest[33] = billboard.position_.x_;
+            dest[34] = billboard.position_.y_;
+            dest[35] = billboard.position_.z_;
+            dest[36] = billboard.direction_.x_;
+            dest[37] = billboard.direction_.y_;
+            dest[38] = billboard.direction_.z_;
+            ((unsigned&)dest[39]) = color;
+            dest[40] = billboard.uv_.min_.x_;
+            dest[41] = billboard.uv_.max_.y_;
+            dest[42] = -size.x_ * rot2D[0][0] - size.y_ * rot2D[0][1];
+            dest[43] = -size.x_ * rot2D[1][0] - size.y_ * rot2D[1][1];
+
+            dest += 44;
+        }
     }
     }
 
 
     vertexBuffer_->Unlock();
     vertexBuffer_->Unlock();
@@ -592,4 +729,32 @@ void BillboardSet::MarkPositionsDirty()
     bufferDirty_ = true;
     bufferDirty_ = true;
 }
 }
 
 
+void BillboardSet::CalculateFixedScreenSize(const FrameInfo& frame)
+{
+    float invViewHeight = 1.0f / frame.viewSize_.y_;
+    float halfViewWorldSize = frame.camera_->GetHalfViewSize();
+
+    if (!frame.camera_->IsOrthographic())
+    {
+        Matrix4 viewProj(frame.camera_->GetProjection(false) * frame.camera_->GetView());
+        const Matrix3x4& worldTransform = node_->GetWorldTransform();
+        Matrix3x4 billboardTransform = relative_ ? worldTransform : Matrix3x4::IDENTITY;
+
+        for (unsigned i = 0; i < billboards_.Size(); ++i)
+        {
+            Vector4 projPos(viewProj * Vector4(billboardTransform * billboards_[i].position_, 1.0f));
+            billboards_[i].screenScaleFactor_ = invViewHeight * halfViewWorldSize * projPos.w_;
+        }
+    }
+    else
+    {
+        for (unsigned i = 0; i < billboards_.Size(); ++i)
+            billboards_[i].screenScaleFactor_ = invViewHeight * halfViewWorldSize;
+    }
+
+    bufferDirty_ = true;
+    forceUpdate_ = true;
+    worldBoundingBoxDirty_ = true;
+}
+
 }
 }

+ 26 - 41
Source/Atomic/Atomic3D/BillboardSet.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -28,48 +28,18 @@
 #include "../Math/Matrix3x4.h"
 #include "../Math/Matrix3x4.h"
 #include "../Math/Rect.h"
 #include "../Math/Rect.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class IndexBuffer;
 class IndexBuffer;
 class VertexBuffer;
 class VertexBuffer;
 
 
 /// One billboard in the billboard set.
 /// One billboard in the billboard set.
-class ATOMIC_API Billboard : public RefCounted
+struct URHO3D_API Billboard
 {
 {
-    friend class BillboardSet;
-    friend class ParticleEmitter;
-    REFCOUNTED(Billboard);
-
-public:
-    Billboard();
-    virtual ~Billboard();
-
-    Vector3 GetPosition() { return position_; }
-    void SetPosition(Vector3 &position) { position_ = position; }
-
-    Vector3 GetSize() { return size_; }
-    void SetSize(Vector2 &size) { size_ = size; }
-
-    Rect GetUV() { return uv_; }
-    void SetUV(Rect &uv) { uv_ = uv; }
-
-    Color GetColor() { return color_; }
-    void SetColor(Color &color) { color_ = color; }
-
-    float GetRotation() { return rotation_; }
-    void SetRotation(float rotation) { rotation_ = rotation; }
-
-    bool IsEnabled() { return enabled_; }
-    void SetEnabled(bool enabled) { enabled_ = enabled; }
-
-    float GetSortDistance() { return sortDistance_; }
-    void SetSortDistance(float sortDistance) { sortDistance_ = sortDistance; }
-
-private:
     /// Position.
     /// Position.
     Vector3 position_;
     Vector3 position_;
-    /// Two-dimensional size.
+    /// Two-dimensional size. If BillboardSet has fixed screen size enabled, this is measured in pixels instead of world units.
     Vector2 size_;
     Vector2 size_;
     /// UV coordinates.
     /// UV coordinates.
     Rect uv_;
     Rect uv_;
@@ -77,18 +47,22 @@ private:
     Color color_;
     Color color_;
     /// Rotation.
     /// Rotation.
     float rotation_;
     float rotation_;
+    /// Direction (For direction based billboard only).
+    Vector3 direction_;
     /// Enabled flag.
     /// Enabled flag.
     bool enabled_;
     bool enabled_;
-    /// Sort distance.
+    /// Sort distance. Used internally.
     float sortDistance_;
     float sortDistance_;
+    /// Scale factor for fixed screen size mode. Used internally.
+    float screenScaleFactor_;
 };
 };
 
 
 static const unsigned MAX_BILLBOARDS = 65536 / 4;
 static const unsigned MAX_BILLBOARDS = 65536 / 4;
 
 
 /// %Billboard component.
 /// %Billboard component.
-class ATOMIC_API BillboardSet : public Drawable
+class URHO3D_API BillboardSet : public Drawable
 {
 {
-    OBJECT(BillboardSet);
+    URHO3D_OBJECT(BillboardSet, Drawable);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -117,6 +91,8 @@ public:
     void SetScaled(bool enable);
     void SetScaled(bool enable);
     /// Set whether billboards are sorted by distance. Default false.
     /// Set whether billboards are sorted by distance. Default false.
     void SetSorted(bool enable);
     void SetSorted(bool enable);
+    /// Set whether billboards have fixed size on screen (measured in pixels) regardless of distance to camera. Default false.
+    void SetFixedScreenSize(bool enable);
     /// Set how the billboards should rotate in relation to the camera. Default is to follow camera rotation on all axes (FC_ROTATE_XYZ.)
     /// Set how the billboards should rotate in relation to the camera. Default is to follow camera rotation on all axes (FC_ROTATE_XYZ.)
     void SetFaceCameraMode(FaceCameraMode mode);
     void SetFaceCameraMode(FaceCameraMode mode);
     /// Set animation LOD bias.
     /// Set animation LOD bias.
@@ -131,7 +107,7 @@ public:
     unsigned GetNumBillboards() const { return billboards_.Size(); }
     unsigned GetNumBillboards() const { return billboards_.Size(); }
 
 
     /// Return all billboards.
     /// Return all billboards.
-    Vector<SharedPtr<Billboard>>& GetBillboards() { return billboards_; }
+    PODVector<Billboard>& GetBillboards() { return billboards_; }
 
 
     /// Return billboard by index.
     /// Return billboard by index.
     Billboard* GetBillboard(unsigned index);
     Billboard* GetBillboard(unsigned index);
@@ -145,6 +121,9 @@ public:
     /// Return whether billboards are sorted.
     /// Return whether billboards are sorted.
     bool IsSorted() const { return sorted_; }
     bool IsSorted() const { return sorted_; }
 
 
+    /// Return whether billboards are fixed screen size.
+    bool IsFixedScreenSize() const { return fixedScreenSize_; }
+
     /// Return how the billboards rotate in relation to the camera.
     /// Return how the billboards rotate in relation to the camera.
     FaceCameraMode GetFaceCameraMode() const { return faceCameraMode_; }
     FaceCameraMode GetFaceCameraMode() const { return faceCameraMode_; }
 
 
@@ -171,9 +150,7 @@ protected:
     void MarkPositionsDirty();
     void MarkPositionsDirty();
 
 
     /// Billboards.
     /// Billboards.
-    Vector<SharedPtr<Billboard>> billboards_;
-    /// Coordinate axes on which camera facing is done.
-    Vector3 faceCameraAxes_;
+    PODVector<Billboard> billboards_;
     /// Animation LOD bias.
     /// Animation LOD bias.
     float animationLodBias_;
     float animationLodBias_;
     /// Animation LOD timer.
     /// Animation LOD timer.
@@ -184,6 +161,8 @@ protected:
     bool scaled_;
     bool scaled_;
     /// Billboards sorted flag.
     /// Billboards sorted flag.
     bool sorted_;
     bool sorted_;
+    /// Billboards fixed screen size flag.
+    bool fixedScreenSize_;
     /// Billboard rotation mode in relation to the camera.
     /// Billboard rotation mode in relation to the camera.
     FaceCameraMode faceCameraMode_;
     FaceCameraMode faceCameraMode_;
 
 
@@ -192,6 +171,8 @@ private:
     void UpdateBufferSize();
     void UpdateBufferSize();
     /// Rewrite billboard vertex buffer.
     /// Rewrite billboard vertex buffer.
     void UpdateVertexBuffer(const FrameInfo& frame);
     void UpdateVertexBuffer(const FrameInfo& frame);
+    /// Calculate billboard scale factors in fixed screen size mode.
+    void CalculateFixedScreenSize(const FrameInfo& frame);
 
 
     /// Geometry.
     /// Geometry.
     SharedPtr<Geometry> geometry_;
     SharedPtr<Geometry> geometry_;
@@ -207,8 +188,12 @@ private:
     bool bufferDirty_;
     bool bufferDirty_;
     /// Force update flag (ignore animation LOD momentarily.)
     /// Force update flag (ignore animation LOD momentarily.)
     bool forceUpdate_;
     bool forceUpdate_;
+    /// Update billboard geometry type
+    bool geometryTypeUpdate_;
     /// Sorting flag. Triggers a vertex buffer rewrite for each view this billboard set is rendered from.
     /// Sorting flag. Triggers a vertex buffer rewrite for each view this billboard set is rendered from.
     bool sortThisFrame_;
     bool sortThisFrame_;
+    /// Whether was last rendered from an ortho camera.
+    bool hasOrthoCamera_;
     /// Frame number on which was last sorted.
     /// Frame number on which was last sorted.
     unsigned sortFrameNumber_;
     unsigned sortFrameNumber_;
     /// Previous offset to camera for determining whether sorting is necessary.
     /// Previous offset to camera for determining whether sorting is necessary.

+ 29 - 26
Source/Atomic/Atomic3D/CustomGeometry.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,7 +26,7 @@
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
 #include "../Graphics/Batch.h"
 #include "../Graphics/Batch.h"
 #include "../Graphics/Camera.h"
 #include "../Graphics/Camera.h"
-#include "../Atomic3D/CustomGeometry.h"
+#include "../Graphics/CustomGeometry.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/Material.h"
 #include "../Graphics/Material.h"
 #include "../Graphics/OcclusionBuffer.h"
 #include "../Graphics/OcclusionBuffer.h"
@@ -39,7 +39,7 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const char* GEOMETRY_CATEGORY;
 extern const char* GEOMETRY_CATEGORY;
@@ -64,19 +64,19 @@ void CustomGeometry::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<CustomGeometry>(GEOMETRY_CATEGORY);
     context->RegisterFactory<CustomGeometry>(GEOMETRY_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ATTRIBUTE("Dynamic Vertex Buffer", bool, dynamic_, false, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Geometry Data", GetGeometryDataAttr, SetGeometryDataAttr, PODVector<unsigned char>,
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Dynamic Vertex Buffer", bool, dynamic_, false, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Geometry Data", GetGeometryDataAttr, SetGeometryDataAttr, PODVector<unsigned char>,
         Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
         Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
-    ACCESSOR_ATTRIBUTE("Materials", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()),
+    URHO3D_ACCESSOR_ATTRIBUTE("Materials", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()),
         AM_DEFAULT);
         AM_DEFAULT);
-    ATTRIBUTE("Is Occluder", bool, occluder_, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Drawable);
+    URHO3D_ATTRIBUTE("Is Occluder", bool, occluder_, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
 }
 }
 
 
 void CustomGeometry::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
 void CustomGeometry::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
@@ -132,7 +132,7 @@ void CustomGeometry::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQ
         break;
         break;
 
 
     case RAY_TRIANGLE_UV:
     case RAY_TRIANGLE_UV:
-        LOGWARNING("RAY_TRIANGLE_UV query level is not supported for CustomGeometry component");
+        URHO3D_LOGWARNING("RAY_TRIANGLE_UV query level is not supported for CustomGeometry component");
         break;
         break;
     }
     }
 }
 }
@@ -188,15 +188,15 @@ bool CustomGeometry::DrawOcclusion(OcclusionBuffer* buffer)
         unsigned vertexSize;
         unsigned vertexSize;
         const unsigned char* indexData;
         const unsigned char* indexData;
         unsigned indexSize;
         unsigned indexSize;
-        unsigned elementMask;
+        const PODVector<VertexElement>* elements;
 
 
-        geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask);
+        geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elements);
         // Check for valid geometry data
         // Check for valid geometry data
-        if (!vertexData)
+        if (!vertexData || !elements || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0)
             continue;
             continue;
 
 
         // Draw and check for running out of triangles
         // Draw and check for running out of triangles
-        success = buffer->Draw(node_->GetWorldTransform(), vertexData, vertexSize, geometry->GetVertexStart(),
+        success = buffer->AddTriangles(node_->GetWorldTransform(), vertexData, vertexSize, geometry->GetVertexStart(),
             geometry->GetVertexCount());
             geometry->GetVertexCount());
 
 
         if (!success)
         if (!success)
@@ -242,7 +242,7 @@ void CustomGeometry::BeginGeometry(unsigned index, PrimitiveType type)
 {
 {
     if (index > geometries_.Size())
     if (index > geometries_.Size())
     {
     {
-        LOGERROR("Geometry index out of bounds");
+        URHO3D_LOGERROR("Geometry index out of bounds");
         return;
         return;
     }
     }
 
 
@@ -305,7 +305,7 @@ void CustomGeometry::DefineGeometry(unsigned index, PrimitiveType type, unsigned
 {
 {
     if (index > geometries_.Size())
     if (index > geometries_.Size())
     {
     {
-        LOGERROR("Geometry index out of bounds");
+        URHO3D_LOGERROR("Geometry index out of bounds");
         return;
         return;
     }
     }
 
 
@@ -328,7 +328,7 @@ void CustomGeometry::DefineGeometry(unsigned index, PrimitiveType type, unsigned
 
 
 void CustomGeometry::Commit()
 void CustomGeometry::Commit()
 {
 {
-    PROFILE(CommitCustomGeometry);
+    URHO3D_PROFILE(CommitCustomGeometry);
 
 
     unsigned totalVertices = 0;
     unsigned totalVertices = 0;
     boundingBox_.Clear();
     boundingBox_.Clear();
@@ -341,6 +341,9 @@ void CustomGeometry::Commit()
             boundingBox_.Merge(vertices_[i][j].position_);
             boundingBox_.Merge(vertices_[i][j].position_);
     }
     }
 
 
+    // Make sure world-space bounding box will be updated
+    OnMarkedDirty(node_);
+
     // Resize (recreate) the vertex buffer only if necessary
     // Resize (recreate) the vertex buffer only if necessary
     if (vertexBuffer_->GetVertexCount() != totalVertices || vertexBuffer_->GetElementMask() != elementMask_ ||
     if (vertexBuffer_->GetVertexCount() != totalVertices || vertexBuffer_->GetElementMask() != elementMask_ ||
         vertexBuffer_->IsDynamic() != dynamic_)
         vertexBuffer_->IsDynamic() != dynamic_)
@@ -386,7 +389,7 @@ void CustomGeometry::Commit()
                     ++vertexCount;
                     ++vertexCount;
                 }
                 }
 
 
-                geometries_[i]->SetVertexBuffer(0, vertexBuffer_, elementMask_);
+                geometries_[i]->SetVertexBuffer(0, vertexBuffer_);
                 geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, vertexStart, vertexCount);
                 geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, vertexStart, vertexCount);
                 vertexStart += vertexCount;
                 vertexStart += vertexCount;
             }
             }
@@ -394,13 +397,13 @@ void CustomGeometry::Commit()
             vertexBuffer_->Unlock();
             vertexBuffer_->Unlock();
         }
         }
         else
         else
-            LOGERROR("Failed to lock custom geometry vertex buffer");
+            URHO3D_LOGERROR("Failed to lock custom geometry vertex buffer");
     }
     }
     else
     else
     {
     {
         for (unsigned i = 0; i < geometries_.Size(); ++i)
         for (unsigned i = 0; i < geometries_.Size(); ++i)
         {
         {
-            geometries_[i]->SetVertexBuffer(0, vertexBuffer_, elementMask_);
+            geometries_[i]->SetVertexBuffer(0, vertexBuffer_);
             geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, 0, 0);
             geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, 0, 0);
         }
         }
     }
     }
@@ -420,7 +423,7 @@ bool CustomGeometry::SetMaterial(unsigned index, Material* material)
 {
 {
     if (index >= batches_.Size())
     if (index >= batches_.Size())
     {
     {
-        LOGERROR("Material index out of bounds");
+        URHO3D_LOGERROR("Material index out of bounds");
         return false;
         return false;
     }
     }
 
 

+ 4 - 4
Source/Atomic/Atomic3D/CustomGeometry.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,7 +24,7 @@
 
 
 #include "../Graphics/Drawable.h"
 #include "../Graphics/Drawable.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// Custom geometry vertex.
 /// Custom geometry vertex.
@@ -45,9 +45,9 @@ struct CustomGeometryVertex
 class VertexBuffer;
 class VertexBuffer;
 
 
 /// Custom geometry component.
 /// Custom geometry component.
-class ATOMIC_API CustomGeometry : public Drawable
+class URHO3D_API CustomGeometry : public Drawable
 {
 {
-    OBJECT(CustomGeometry);
+    URHO3D_OBJECT(CustomGeometry, Drawable);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 27 - 27
Source/Atomic/Atomic3D/DecalSet.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,10 +24,10 @@
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
-#include "../Atomic3D/AnimatedModel.h"
+#include "../Graphics/AnimatedModel.h"
 #include "../Graphics/Batch.h"
 #include "../Graphics/Batch.h"
 #include "../Graphics/Camera.h"
 #include "../Graphics/Camera.h"
-#include "../Atomic3D/DecalSet.h"
+#include "../Graphics/DecalSet.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/Graphics.h"
 #include "../Graphics/Graphics.h"
 #include "../Graphics/IndexBuffer.h"
 #include "../Graphics/IndexBuffer.h"
@@ -46,7 +46,7 @@
 #pragma warning(disable:6293)
 #pragma warning(disable:6293)
 #endif
 #endif
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const char* GEOMETRY_CATEGORY;
 extern const char* GEOMETRY_CATEGORY;
@@ -183,15 +183,15 @@ void DecalSet::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<DecalSet>(GEOMETRY_CATEGORY);
     context->RegisterFactory<DecalSet>(GEOMETRY_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()),
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()),
         AM_DEFAULT);
         AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Vertices", GetMaxVertices, SetMaxVertices, unsigned, DEFAULT_MAX_VERTICES, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Max Indices", GetMaxIndices, SetMaxIndices, unsigned, DEFAULT_MAX_INDICES, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    COPY_BASE_ATTRIBUTES(Drawable);
-    MIXED_ACCESSOR_ATTRIBUTE("Decals", GetDecalsAttr, SetDecalsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Vertices", GetMaxVertices, SetMaxVertices, unsigned, DEFAULT_MAX_VERTICES, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Max Indices", GetMaxIndices, SetMaxIndices, unsigned, DEFAULT_MAX_INDICES, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Decals", GetDecalsAttr, SetDecalsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
         AM_FILE | AM_NOEDIT);
         AM_FILE | AM_NOEDIT);
 }
 }
 
 
@@ -258,7 +258,7 @@ void DecalSet::SetMaterial(Material* material)
 void DecalSet::SetMaxVertices(unsigned num)
 void DecalSet::SetMaxVertices(unsigned num)
 {
 {
     // Never expand to 32 bit indices
     // Never expand to 32 bit indices
-    num = (unsigned)Clamp((int)num, MIN_VERTICES, MAX_VERTICES);
+    num = (unsigned)Clamp(num, MIN_VERTICES, MAX_VERTICES);
 
 
     if (num != maxVertices_)
     if (num != maxVertices_)
     {
     {
@@ -293,7 +293,7 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
     float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV, float timeToLive, float normalCutoff,
     float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV, float timeToLive, float normalCutoff,
     unsigned subGeometry)
     unsigned subGeometry)
 {
 {
-    PROFILE(AddDecal);
+    URHO3D_PROFILE(AddDecal);
 
 
     // Do not add decals in headless mode
     // Do not add decals in headless mode
     if (!node_ || !GetSubsystem<Graphics>())
     if (!node_ || !GetSubsystem<Graphics>())
@@ -301,7 +301,7 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
 
 
     if (!target || !target->GetNode())
     if (!target || !target->GetNode())
     {
     {
-        LOGERROR("Null target drawable for decal");
+        URHO3D_LOGERROR("Null target drawable for decal");
         return false;
         return false;
     }
     }
 
 
@@ -426,14 +426,14 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
 
 
     if (newDecal.vertices_.Size() > maxVertices_)
     if (newDecal.vertices_.Size() > maxVertices_)
     {
     {
-        LOGWARNING("Can not add decal, vertex count " + String(newDecal.vertices_.Size()) + " exceeds maximum " +
+        URHO3D_LOGWARNING("Can not add decal, vertex count " + String(newDecal.vertices_.Size()) + " exceeds maximum " +
                    String(maxVertices_));
                    String(maxVertices_));
         decals_.Pop();
         decals_.Pop();
         return false;
         return false;
     }
     }
     if (newDecal.indices_.Size() > maxIndices_)
     if (newDecal.indices_.Size() > maxIndices_)
     {
     {
-        LOGWARNING("Can not add decal, index count " + String(newDecal.indices_.Size()) + " exceeds maximum " +
+        URHO3D_LOGWARNING("Can not add decal, index count " + String(newDecal.indices_.Size()) + " exceeds maximum " +
                    String(maxIndices_));
                    String(maxIndices_));
         decals_.Pop();
         decals_.Pop();
         return false;
         return false;
@@ -463,7 +463,7 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
     while (decals_.Size() && (numVertices_ > maxVertices_ || numIndices_ > maxIndices_))
     while (decals_.Size() && (numVertices_ > maxVertices_ || numIndices_ > maxIndices_))
         RemoveDecals(1);
         RemoveDecals(1);
 
 
-    LOGDEBUG("Added decal with " + String(newDecal.vertices_.Size()) + " vertices");
+    URHO3D_LOGDEBUG("Added decal with " + String(newDecal.vertices_.Size()) + " vertices");
 
 
     // If new decal is time limited, subscribe to scene post-update
     // If new decal is time limited, subscribe to scene post-update
     if (newDecal.timeToLive_ > 0.0f && !subscribed_)
     if (newDecal.timeToLive_ > 0.0f && !subscribed_)
@@ -717,7 +717,7 @@ void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Drawable* target
         if (!vb)
         if (!vb)
             continue;
             continue;
 
 
-        unsigned elementMask = geometry->GetVertexElementMask(i);
+        unsigned elementMask = vb->GetElementMask();
         unsigned char* data = vb->GetShadowData();
         unsigned char* data = vb->GetShadowData();
         if (!data)
         if (!data)
             continue;
             continue;
@@ -729,12 +729,12 @@ void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Drawable* target
         }
         }
         if (elementMask & MASK_NORMAL)
         if (elementMask & MASK_NORMAL)
         {
         {
-            normalData = data + vb->GetElementOffset(ELEMENT_NORMAL);
+            normalData = data + vb->GetElementOffset(SEM_NORMAL);
             normalStride = vb->GetVertexSize();
             normalStride = vb->GetVertexSize();
         }
         }
         if (elementMask & MASK_BLENDWEIGHTS)
         if (elementMask & MASK_BLENDWEIGHTS)
         {
         {
-            skinningData = data + vb->GetElementOffset(ELEMENT_BLENDWEIGHTS);
+            skinningData = data + vb->GetElementOffset(SEM_BLENDWEIGHTS);
             skinningStride = vb->GetVertexSize();
             skinningStride = vb->GetVertexSize();
         }
         }
     }
     }
@@ -743,11 +743,11 @@ void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Drawable* target
     if (!positionData)
     if (!positionData)
     {
     {
         // As a fallback, try to get the geometry's raw vertex/index data
         // As a fallback, try to get the geometry's raw vertex/index data
-        unsigned elementMask;
-        geometry->GetRawData(positionData, positionStride, indexData, indexStride, elementMask);
+        const PODVector<VertexElement>* elements;
+        geometry->GetRawData(positionData, positionStride, indexData, indexStride, elements);
         if (!positionData)
         if (!positionData)
         {
         {
-            LOGWARNING("Can not add decal, target drawable has no CPU-side geometry data");
+            URHO3D_LOGWARNING("Can not add decal, target drawable has no CPU-side geometry data");
             return;
             return;
         }
         }
     }
     }
@@ -896,7 +896,7 @@ bool DecalSet::GetBones(Drawable* target, unsigned batchIndex, const float* blen
 
 
             if (!bone)
             if (!bone)
             {
             {
-                LOGWARNING("Out of range bone index for skinned decal");
+                URHO3D_LOGWARNING("Out of range bone index for skinned decal");
                 return false;
                 return false;
             }
             }
 
 
@@ -921,7 +921,7 @@ bool DecalSet::GetBones(Drawable* target, unsigned batchIndex, const float* blen
             {
             {
                 if (bones_.Size() >= Graphics::GetMaxBones())
                 if (bones_.Size() >= Graphics::GetMaxBones())
                 {
                 {
-                    LOGWARNING("Maximum skinned decal bone count reached");
+                    URHO3D_LOGWARNING("Maximum skinned decal bone count reached");
                     return false;
                     return false;
                 }
                 }
                 else
                 else
@@ -1139,7 +1139,7 @@ void DecalSet::UpdateEventSubscription(bool checkAllDecals)
 
 
     if (enabled && !subscribed_)
     if (enabled && !subscribed_)
     {
     {
-        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(DecalSet, HandleScenePostUpdate));
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(DecalSet, HandleScenePostUpdate));
         subscribed_ = true;
         subscribed_ = true;
     }
     }
     else if (!enabled && subscribed_)
     else if (!enabled && subscribed_)

+ 6 - 6
Source/Atomic/Atomic3D/DecalSet.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,10 +24,10 @@
 
 
 #include "../Container/List.h"
 #include "../Container/List.h"
 #include "../Graphics/Drawable.h"
 #include "../Graphics/Drawable.h"
-#include "../Atomic3D/Skeleton.h"
+#include "../Graphics/Skeleton.h"
 #include "../Math/Frustum.h"
 #include "../Math/Frustum.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class IndexBuffer;
 class IndexBuffer;
@@ -48,7 +48,7 @@ struct DecalVertex
     {
     {
     }
     }
 
 
-    // Construct with position, normal and skinning information.
+    /// Construct with position, normal and skinning information.
     DecalVertex(const Vector3& position, const Vector3& normal, const float* blendWeights, const unsigned char* blendIndices) :
     DecalVertex(const Vector3& position, const Vector3& normal, const float* blendWeights, const unsigned char* blendIndices) :
         position_(position),
         position_(position),
         normal_(normal)
         normal_(normal)
@@ -102,9 +102,9 @@ struct Decal
 };
 };
 
 
 /// %Decal renderer component.
 /// %Decal renderer component.
-class ATOMIC_API DecalSet : public Drawable
+class URHO3D_API DecalSet : public Drawable
 {
 {
-    OBJECT(DecalSet);
+    URHO3D_OBJECT(DecalSet, Drawable);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

+ 65 - 104
Source/Atomic/Atomic3D/Model.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,17 +26,15 @@
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/IndexBuffer.h"
 #include "../Graphics/IndexBuffer.h"
-#include "../Atomic3D/Model.h"
+#include "../Graphics/Model.h"
 #include "../Graphics/Graphics.h"
 #include "../Graphics/Graphics.h"
 #include "../Graphics/VertexBuffer.h"
 #include "../Graphics/VertexBuffer.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
+#include "../IO/File.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-#include "Animation.h"
-#include "../Resource/ResourceCache.h"
-
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 unsigned LookupVertexBuffer(VertexBuffer* buffer, const Vector<SharedPtr<VertexBuffer> >& buffers)
 unsigned LookupVertexBuffer(VertexBuffer* buffer, const Vector<SharedPtr<VertexBuffer> >& buffers)
@@ -76,22 +74,18 @@ void Model::RegisterObject(Context* context)
 bool Model::BeginLoad(Deserializer& source)
 bool Model::BeginLoad(Deserializer& source)
 {
 {
     // Check ID
     // Check ID
-
-    String id = source.ReadFileID();
-    bool umdl = false;
-    if (id == "UMDL") // we only support UMDL for some current legacy mdl's (ToonTown)
-        umdl = true;
-
-    if (!umdl && id != "AMDL")
+    String fileID = source.ReadFileID();
+    if (fileID != "UMDL" && fileID != "UMD2")
     {
     {
-        LOGERROR(source.GetName() + " is not a valid model file");
+        URHO3D_LOGERROR(source.GetName() + " is not a valid model file");
         return false;
         return false;
     }
     }
 
 
+    bool hasVertexDeclarations = (fileID == "UMD2");
+
     geometries_.Clear();
     geometries_.Clear();
     geometryBoneMappings_.Clear();
     geometryBoneMappings_.Clear();
     geometryCenters_.Clear();
     geometryCenters_.Clear();
-    geometryNames_.Clear();
     morphs_.Clear();
     morphs_.Clear();
     vertexBuffers_.Clear();
     vertexBuffers_.Clear();
     indexBuffers_.Clear();
     indexBuffers_.Clear();
@@ -107,35 +101,53 @@ bool Model::BeginLoad(Deserializer& source)
     loadVBData_.Resize(numVertexBuffers);
     loadVBData_.Resize(numVertexBuffers);
     for (unsigned i = 0; i < numVertexBuffers; ++i)
     for (unsigned i = 0; i < numVertexBuffers; ++i)
     {
     {
-        unsigned vertexCount = source.ReadUInt();
-        unsigned elementMask = source.ReadUInt();
+        VertexBufferDesc& desc = loadVBData_[i];
+
+        desc.vertexCount_ = source.ReadUInt();
+        if (!hasVertexDeclarations)
+        {
+            unsigned elementMask = source.ReadUInt();
+            desc.vertexElements_ = VertexBuffer::GetElements(elementMask);
+        }
+        else
+        {
+            desc.vertexElements_.Clear();
+            unsigned numElements = source.ReadUInt();
+            for (unsigned j = 0; j < numElements; ++j)
+            {
+                unsigned elementDesc = source.ReadUInt();
+                VertexElementType type = (VertexElementType)(elementDesc & 0xff);
+                VertexElementSemantic semantic = (VertexElementSemantic)((elementDesc >> 8) & 0xff);
+                unsigned char index = (unsigned char)((elementDesc >> 16) & 0xff);
+                desc.vertexElements_.Push(VertexElement(type, semantic, index));
+            }
+        }
+
         morphRangeStarts_[i] = source.ReadUInt();
         morphRangeStarts_[i] = source.ReadUInt();
         morphRangeCounts_[i] = source.ReadUInt();
         morphRangeCounts_[i] = source.ReadUInt();
 
 
         SharedPtr<VertexBuffer> buffer(new VertexBuffer(context_));
         SharedPtr<VertexBuffer> buffer(new VertexBuffer(context_));
-        unsigned vertexSize = VertexBuffer::GetVertexSize(elementMask);
+        unsigned vertexSize = VertexBuffer::GetVertexSize(desc.vertexElements_);
+        desc.dataSize_ = desc.vertexCount_ * vertexSize;
 
 
         // Prepare vertex buffer data to be uploaded during EndLoad()
         // Prepare vertex buffer data to be uploaded during EndLoad()
         if (async)
         if (async)
         {
         {
-            loadVBData_[i].vertexCount_ = vertexCount;
-            loadVBData_[i].elementMask_ = elementMask;
-            loadVBData_[i].dataSize_ = vertexCount * vertexSize;
-            loadVBData_[i].data_ = new unsigned char[loadVBData_[i].dataSize_];
-            source.Read(loadVBData_[i].data_.Get(), loadVBData_[i].dataSize_);
+            desc.data_ = new unsigned char[desc.dataSize_];
+            source.Read(desc.data_.Get(), desc.dataSize_);
         }
         }
         else
         else
         {
         {
             // If not async loading, use locking to avoid extra allocation & copy
             // If not async loading, use locking to avoid extra allocation & copy
-            loadVBData_[i].data_.Reset(); // Make sure no previous data
+            desc.data_.Reset(); // Make sure no previous data
             buffer->SetShadowed(true);
             buffer->SetShadowed(true);
-            buffer->SetSize(vertexCount, elementMask);
-            void* dest = buffer->Lock(0, vertexCount);
-            source.Read(dest, vertexCount * vertexSize);
+            buffer->SetSize(desc.vertexCount_, desc.vertexElements_);
+            void* dest = buffer->Lock(0, desc.vertexCount_);
+            source.Read(dest, desc.vertexCount_ * vertexSize);
             buffer->Unlock();
             buffer->Unlock();
         }
         }
 
 
-        memoryUse += sizeof(VertexBuffer) + vertexCount * vertexSize;
+        memoryUse += sizeof(VertexBuffer) + desc.vertexCount_ * vertexSize;
         vertexBuffers_.Push(buffer);
         vertexBuffers_.Push(buffer);
     }
     }
 
 
@@ -206,7 +218,7 @@ bool Model::BeginLoad(Deserializer& source)
 
 
             if (vbRef >= vertexBuffers_.Size())
             if (vbRef >= vertexBuffers_.Size())
             {
             {
-                LOGERROR("Vertex buffer index out of bounds");
+                URHO3D_LOGERROR("Vertex buffer index out of bounds");
                 loadVBData_.Clear();
                 loadVBData_.Clear();
                 loadIBData_.Clear();
                 loadIBData_.Clear();
                 loadGeometries_.Clear();
                 loadGeometries_.Clear();
@@ -214,7 +226,7 @@ bool Model::BeginLoad(Deserializer& source)
             }
             }
             if (ibRef >= indexBuffers_.Size())
             if (ibRef >= indexBuffers_.Size())
             {
             {
-                LOGERROR("Index buffer index out of bounds");
+                URHO3D_LOGERROR("Index buffer index out of bounds");
                 loadVBData_.Clear();
                 loadVBData_.Clear();
                 loadIBData_.Clear();
                 loadIBData_.Clear();
                 loadGeometries_.Clear();
                 loadGeometries_.Clear();
@@ -294,22 +306,6 @@ bool Model::BeginLoad(Deserializer& source)
         geometryCenters_.Push(Vector3::ZERO);
         geometryCenters_.Push(Vector3::ZERO);
     memoryUse += sizeof(Vector3) * geometries_.Size();
     memoryUse += sizeof(Vector3) * geometries_.Size();
 
 
-    if (umdl)
-    {
-        SetMemoryUse(memoryUse);
-        return true;
-    }
-
-    // MODEL_VERSION
-    unsigned version = source.ReadUInt();
-
-    // Read geometry names
-    geometryNames_.Resize(geometries_.Size());
-    for (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        geometryNames_[i] = source.ReadString();
-    }
-
     SetMemoryUse(memoryUse);
     SetMemoryUse(memoryUse);
     return true;
     return true;
 }
 }
@@ -324,7 +320,7 @@ bool Model::EndLoad()
         if (desc.data_)
         if (desc.data_)
         {
         {
             buffer->SetShadowed(true);
             buffer->SetShadowed(true);
-            buffer->SetSize(desc.vertexCount_, desc.elementMask_);
+            buffer->SetSize(desc.vertexCount_, desc.vertexElements_);
             buffer->SetData(desc.data_.Get());
             buffer->SetData(desc.data_.Get());
         }
         }
     }
     }
@@ -364,7 +360,7 @@ bool Model::EndLoad()
 bool Model::Save(Serializer& dest) const
 bool Model::Save(Serializer& dest) const
 {
 {
     // Write ID
     // Write ID
-    if (!dest.WriteFileID("AMDL")) // atomic model specifier
+    if (!dest.WriteFileID("UMD2"))
         return false;
         return false;
 
 
     // Write vertex buffers
     // Write vertex buffers
@@ -373,7 +369,15 @@ bool Model::Save(Serializer& dest) const
     {
     {
         VertexBuffer* buffer = vertexBuffers_[i];
         VertexBuffer* buffer = vertexBuffers_[i];
         dest.WriteUInt(buffer->GetVertexCount());
         dest.WriteUInt(buffer->GetVertexCount());
-        dest.WriteUInt(buffer->GetElementMask());
+        const PODVector<VertexElement>& elements = buffer->GetElements();
+        dest.WriteUInt(elements.Size());
+        for (unsigned j = 0; j < elements.Size(); ++j)
+        {
+            unsigned elementDesc = ((unsigned)elements[j].type_) |
+                (((unsigned)elements[j].semantic_) << 8) |
+                (((unsigned)elements[j].index_) << 16);
+            dest.WriteUInt(elementDesc);
+        }
         dest.WriteUInt(morphRangeStarts_[i]);
         dest.WriteUInt(morphRangeStarts_[i]);
         dest.WriteUInt(morphRangeCounts_[i]);
         dest.WriteUInt(morphRangeCounts_[i]);
         dest.Write(buffer->GetShadowData(), buffer->GetVertexCount() * buffer->GetVertexSize());
         dest.Write(buffer->GetShadowData(), buffer->GetVertexCount() * buffer->GetVertexSize());
@@ -449,17 +453,6 @@ bool Model::Save(Serializer& dest) const
     for (unsigned i = 0; i < geometryCenters_.Size(); ++i)
     for (unsigned i = 0; i < geometryCenters_.Size(); ++i)
         dest.WriteVector3(geometryCenters_[i]);
         dest.WriteVector3(geometryCenters_[i]);
 
 
-    // ATOMIC BEGIN
-
-    dest.WriteUInt(MODEL_VERSION);
-
-    // Write geometry names
-    for (unsigned i = 0; i < geometryNames_.Size(); ++i)
-        dest.WriteString(geometryNames_[i]);
-
-    // ATOMIC END
-
-
     return true;
     return true;
 }
 }
 
 
@@ -475,12 +468,12 @@ bool Model::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, co
     {
     {
         if (!buffers[i])
         if (!buffers[i])
         {
         {
-            LOGERROR("Null model vertex buffers specified");
+            URHO3D_LOGERROR("Null model vertex buffers specified");
             return false;
             return false;
         }
         }
         if (!buffers[i]->IsShadowed())
         if (!buffers[i]->IsShadowed())
         {
         {
-            LOGERROR("Model vertex buffers must be shadowed");
+            URHO3D_LOGERROR("Model vertex buffers must be shadowed");
             return false;
             return false;
         }
         }
     }
     }
@@ -505,12 +498,12 @@ bool Model::SetIndexBuffers(const Vector<SharedPtr<IndexBuffer> >& buffers)
     {
     {
         if (!buffers[i])
         if (!buffers[i])
         {
         {
-            LOGERROR("Null model index buffers specified");
+            URHO3D_LOGERROR("Null model index buffers specified");
             return false;
             return false;
         }
         }
         if (!buffers[i]->IsShadowed())
         if (!buffers[i]->IsShadowed())
         {
         {
-            LOGERROR("Model index buffers must be shadowed");
+            URHO3D_LOGERROR("Model index buffers must be shadowed");
             return false;
             return false;
         }
         }
     }
     }
@@ -524,7 +517,6 @@ void Model::SetNumGeometries(unsigned num)
     geometries_.Resize(num);
     geometries_.Resize(num);
     geometryBoneMappings_.Resize(num);
     geometryBoneMappings_.Resize(num);
     geometryCenters_.Resize(num);
     geometryCenters_.Resize(num);
-    geometryNames_.Resize(num);
 
 
     // For easier creation of from-scratch geometry, ensure that all geometries start with at least 1 LOD level (0 makes no sense)
     // For easier creation of from-scratch geometry, ensure that all geometries start with at least 1 LOD level (0 makes no sense)
     for (unsigned i = 0; i < geometries_.Size(); ++i)
     for (unsigned i = 0; i < geometries_.Size(); ++i)
@@ -538,12 +530,12 @@ bool Model::SetNumGeometryLodLevels(unsigned index, unsigned num)
 {
 {
     if (index >= geometries_.Size())
     if (index >= geometries_.Size())
     {
     {
-        LOGERROR("Geometry index out of bounds");
+        URHO3D_LOGERROR("Geometry index out of bounds");
         return false;
         return false;
     }
     }
     if (!num)
     if (!num)
     {
     {
-        LOGERROR("Zero LOD levels not allowed");
+        URHO3D_LOGERROR("Zero LOD levels not allowed");
         return false;
         return false;
     }
     }
 
 
@@ -555,12 +547,12 @@ bool Model::SetGeometry(unsigned index, unsigned lodLevel, Geometry* geometry)
 {
 {
     if (index >= geometries_.Size())
     if (index >= geometries_.Size())
     {
     {
-        LOGERROR("Geometry index out of bounds");
+        URHO3D_LOGERROR("Geometry index out of bounds");
         return false;
         return false;
     }
     }
     if (lodLevel >= geometries_[index].Size())
     if (lodLevel >= geometries_[index].Size())
     {
     {
-        LOGERROR("LOD level index out of bounds");
+        URHO3D_LOGERROR("LOD level index out of bounds");
         return false;
         return false;
     }
     }
 
 
@@ -572,7 +564,7 @@ bool Model::SetGeometryCenter(unsigned index, const Vector3& center)
 {
 {
     if (index >= geometryCenters_.Size())
     if (index >= geometryCenters_.Size())
     {
     {
-        LOGERROR("Geometry index out of bounds");
+        URHO3D_LOGERROR("Geometry index out of bounds");
         return false;
         return false;
     }
     }
 
 
@@ -628,7 +620,7 @@ SharedPtr<Model> Model::Clone(const String& cloneName) const
                 if (origData)
                 if (origData)
                     cloneBuffer->SetData(origData);
                     cloneBuffer->SetData(origData);
                 else
                 else
-                    LOGERROR("Failed to lock original vertex buffer for copying");
+                    URHO3D_LOGERROR("Failed to lock original vertex buffer for copying");
             }
             }
             vbMapping[origBuffer] = cloneBuffer;
             vbMapping[origBuffer] = cloneBuffer;
         }
         }
@@ -656,7 +648,7 @@ SharedPtr<Model> Model::Clone(const String& cloneName) const
                 if (origData)
                 if (origData)
                     cloneBuffer->SetData(origData);
                     cloneBuffer->SetData(origData);
                 else
                 else
-                    LOGERROR("Failed to lock original index buffer for copying");
+                    URHO3D_LOGERROR("Failed to lock original index buffer for copying");
             }
             }
             ibMapping[origBuffer] = cloneBuffer;
             ibMapping[origBuffer] = cloneBuffer;
         }
         }
@@ -666,11 +658,8 @@ SharedPtr<Model> Model::Clone(const String& cloneName) const
 
 
     // Deep copy all the geometry LOD levels and refer to the copied vertex/index buffers
     // Deep copy all the geometry LOD levels and refer to the copied vertex/index buffers
     ret->geometries_.Resize(geometries_.Size());
     ret->geometries_.Resize(geometries_.Size());
-    ret->geometryNames_.Resize(geometryNames_.Size());
     for (unsigned i = 0; i < geometries_.Size(); ++i)
     for (unsigned i = 0; i < geometries_.Size(); ++i)
     {
     {
-        ret->geometryNames_[i] = geometryNames_[i];
-
         ret->geometries_[i].Resize(geometries_[i].Size());
         ret->geometries_[i].Resize(geometries_[i].Size());
         for (unsigned j = 0; j < geometries_[i].Size(); ++j)
         for (unsigned j = 0; j < geometries_[i].Size(); ++j)
         {
         {
@@ -684,8 +673,7 @@ SharedPtr<Model> Model::Clone(const String& cloneName) const
                 unsigned numVbs = origGeometry->GetNumVertexBuffers();
                 unsigned numVbs = origGeometry->GetNumVertexBuffers();
                 for (unsigned k = 0; k < numVbs; ++k)
                 for (unsigned k = 0; k < numVbs; ++k)
                 {
                 {
-                    cloneGeometry->SetVertexBuffer(k, vbMapping[origGeometry->GetVertexBuffer(k)],
-                        origGeometry->GetVertexElementMask(k));
+                    cloneGeometry->SetVertexBuffer(k, vbMapping[origGeometry->GetVertexBuffer(k)]);
                 }
                 }
                 cloneGeometry->SetDrawRange(origGeometry->GetPrimitiveType(), origGeometry->GetIndexStart(),
                 cloneGeometry->SetDrawRange(origGeometry->GetPrimitiveType(), origGeometry->GetIndexStart(),
                     origGeometry->GetIndexCount(), origGeometry->GetVertexStart(), origGeometry->GetVertexCount(), false);
                     origGeometry->GetIndexCount(), origGeometry->GetVertexStart(), origGeometry->GetVertexCount(), false);
@@ -765,31 +753,4 @@ unsigned Model::GetMorphRangeCount(unsigned bufferIndex) const
     return bufferIndex < vertexBuffers_.Size() ? morphRangeCounts_[bufferIndex] : 0;
     return bufferIndex < vertexBuffers_.Size() ? morphRangeCounts_[bufferIndex] : 0;
 }
 }
 
 
-// ATOMIC BEGIN
-
-bool Model::SetGeometryName(unsigned index, const String& name)
-{
-    if (index >= geometryNames_.Size())
-    {
-        LOGERROR("Geometry name index out of bounds");
-        return false;
-    }
-
-    geometryNames_[index] = name;
-
-    return true;
-
-}
-
-const String& Model::GetGeometryName(unsigned index) const
-{
-    if (index >= geometryNames_.Size())
-        return String::EMPTY;
-
-    return geometryNames_[index];
-
-}
-
-// ATOMIC END
-
 }
 }

+ 7 - 28
Source/Atomic/Atomic3D/Model.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,13 +25,11 @@
 #include "../Container/ArrayPtr.h"
 #include "../Container/ArrayPtr.h"
 #include "../Container/Ptr.h"
 #include "../Container/Ptr.h"
 #include "../Graphics/GraphicsDefs.h"
 #include "../Graphics/GraphicsDefs.h"
-#include "../Atomic3D/Skeleton.h"
+#include "../Graphics/Skeleton.h"
 #include "../Math/BoundingBox.h"
 #include "../Math/BoundingBox.h"
 #include "../Resource/Resource.h"
 #include "../Resource/Resource.h"
 
 
-#include "Animation.h"
-
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class Geometry;
 class Geometry;
@@ -70,8 +68,8 @@ struct VertexBufferDesc
 {
 {
     /// Vertex count.
     /// Vertex count.
     unsigned vertexCount_;
     unsigned vertexCount_;
-    /// Element mask.
-    unsigned elementMask_;
+    /// Vertex declaration.
+    PODVector<VertexElement> vertexElements_;
     /// Vertex data size.
     /// Vertex data size.
     unsigned dataSize_;
     unsigned dataSize_;
     /// Vertex data.
     /// Vertex data.
@@ -106,16 +104,10 @@ struct GeometryDesc
     unsigned indexCount_;
     unsigned indexCount_;
 };
 };
 
 
-// ATOMIC BEGIN
-
-static const unsigned MODEL_VERSION = 1;
-
-// ATOMIC END
-
 /// 3D model resource.
 /// 3D model resource.
-class ATOMIC_API Model : public Resource
+class URHO3D_API Model : public Resource
 {
 {
-    OBJECT(Model);
+    URHO3D_OBJECT(Model, Resource);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -209,14 +201,6 @@ public:
     /// Return vertex buffer morph range vertex count.
     /// Return vertex buffer morph range vertex count.
     unsigned GetMorphRangeCount(unsigned bufferIndex) const;
     unsigned GetMorphRangeCount(unsigned bufferIndex) const;
 
 
-    // ATOMIC BEGIN
-
-    bool SetGeometryName(unsigned index, const String& name);
-    const String& GetGeometryName(unsigned index) const;
-    const Vector<String>& GetGeometryNames() const { return geometryNames_; }
-
-    // ATOMIC END
-
 private:
 private:
     /// Bounding box.
     /// Bounding box.
     BoundingBox boundingBox_;
     BoundingBox boundingBox_;
@@ -244,11 +228,6 @@ private:
     Vector<IndexBufferDesc> loadIBData_;
     Vector<IndexBufferDesc> loadIBData_;
     /// Geometry definitions for asynchronous loading.
     /// Geometry definitions for asynchronous loading.
     Vector<PODVector<GeometryDesc> > loadGeometries_;
     Vector<PODVector<GeometryDesc> > loadGeometries_;
-
-    // ATOMIC BEGIN
-    Vector<String> geometryNames_;
-    // ATOMIC END
-
 };
 };
 
 
 }
 }

+ 95 - 191
Source/Atomic/Atomic3D/ParticleEffect.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,17 +23,21 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
+#include "../Core/StringUtils.h"
 #include "../Graphics/Material.h"
 #include "../Graphics/Material.h"
-#include "../Atomic3D/ParticleEffect.h"
+#include "../Graphics/ParticleEffect.h"
+#include "../Graphics/BillboardSet.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/XMLFile.h"
 #include "../Resource/XMLFile.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
+extern const char* faceCameraModeNames[];
+
 static const char* emitterTypeNames[] =
 static const char* emitterTypeNames[] =
 {
 {
     "Sphere",
     "Sphere",
@@ -56,6 +60,7 @@ ParticleEffect::ParticleEffect(Context* context) :
     relative_(true),
     relative_(true),
     scaled_(true),
     scaled_(true),
     sorted_(false),
     sorted_(false),
+    fixedScreenSize_(false),
     animationLodBias_(0.0f),
     animationLodBias_(0.0f),
     emitterType_(EMITTER_SPHERE),
     emitterType_(EMITTER_SPHERE),
     emitterSize_(Vector3::ZERO),
     emitterSize_(Vector3::ZERO),
@@ -78,7 +83,8 @@ ParticleEffect::ParticleEffect(Context* context) :
     rotationSpeedMin_(0.0f),
     rotationSpeedMin_(0.0f),
     rotationSpeedMax_(0.0f),
     rotationSpeedMax_(0.0f),
     sizeAdd_(0.0f),
     sizeAdd_(0.0f),
-    sizeMul_(1.0f)
+    sizeMul_(1.0f),
+    faceCameraMode_(FC_ROTATE_XYZ)
 {
 {
 }
 }
 
 
@@ -98,188 +104,16 @@ bool ParticleEffect::BeginLoad(Deserializer& source)
     XMLFile file(context_);
     XMLFile file(context_);
     if (!file.Load(source))
     if (!file.Load(source))
     {
     {
-        LOGERROR("Load particle effect file failed");
+        URHO3D_LOGERROR("Load particle effect file failed");
         return false;
         return false;
     }
     }
 
 
     XMLElement rootElem = file.GetRoot();
     XMLElement rootElem = file.GetRoot();
-    if (!rootElem)
-    {
-        LOGERROR("Particle emitter parameter file does not have a valid root element");
-        return false;
-    }
-
-    // Reset to defaults first so that missing parameters in case of a live reload behave as expected
-    material_.Reset();
-    numParticles_ = DEFAULT_NUM_PARTICLES;
-    updateInvisible_ = false;
-    relative_ = true;
-    scaled_ = true;
-    sorted_ = false;
-    animationLodBias_ = 0.0f;
-    emitterType_ = EMITTER_SPHERE;
-    emitterSize_ = Vector3::ZERO;
-    directionMin_ = DEFAULT_DIRECTION_MIN;
-    directionMax_ = DEFAULT_DIRECTION_MAX;
-    constantForce_ = Vector3::ZERO;
-    dampingForce_ = 0.0f;
-    activeTime_ = 0.0f;
-    inactiveTime_ = 0.0;
-    emissionRateMin_ = DEFAULT_EMISSION_RATE;
-    emissionRateMax_ = DEFAULT_EMISSION_RATE;
-    sizeMin_ = DEFAULT_PARTICLE_SIZE;
-    sizeMax_ = DEFAULT_PARTICLE_SIZE;
-    timeToLiveMin_ = DEFAULT_TIME_TO_LIVE;
-    timeToLiveMax_ = DEFAULT_TIME_TO_LIVE;
-    velocityMin_ = DEFAULT_VELOCITY;
-    velocityMax_ = DEFAULT_VELOCITY;
-    rotationMin_ = 0.0f;
-    rotationMax_ = 0.0f;
-    rotationSpeedMin_ = 0.0f;
-    rotationSpeedMax_ = 0.0f;
-    sizeAdd_ = 0.0f;
-    sizeMul_ = 1.0f;
-    colorFrames_.Clear();
-    textureFrames_.Clear();
-
-    if (rootElem.HasChild("material"))
-    {
-        loadMaterialName_ = rootElem.GetChild("material").GetAttribute("name");
-        // If async loading, can not GetResource() the material. But can do a background request for it
-        if (GetAsyncLoadState() == ASYNC_LOADING)
-            GetSubsystem<ResourceCache>()->BackgroundLoadResource<Material>(loadMaterialName_, true, this);
-    }
-
-    if (rootElem.HasChild("numparticles"))
-        SetNumParticles((unsigned)rootElem.GetChild("numparticles").GetInt("value"));
-
-    if (rootElem.HasChild("updateinvisible"))
-        updateInvisible_ = rootElem.GetChild("updateinvisible").GetBool("enable");
-
-    if (rootElem.HasChild("relative"))
-        relative_ = rootElem.GetChild("relative").GetBool("enable");
-
-    if (rootElem.HasChild("scaled"))
-        scaled_ = rootElem.GetChild("scaled").GetBool("enable");
-
-    if (rootElem.HasChild("sorted"))
-        sorted_ = rootElem.GetChild("sorted").GetBool("enable");
-
-    if (rootElem.HasChild("animlodbias"))
-        SetAnimationLodBias(rootElem.GetChild("animlodbias").GetFloat("value"));
-
-    if (rootElem.HasChild("emittertype"))
-    {
-        String type = rootElem.GetChild("emittertype").GetAttributeLower("value");
-        if (type == "point")
-        {
-            // Point emitter type is deprecated, handled as zero sized sphere
-            emitterType_ = EMITTER_SPHERE;
-            emitterSize_ = Vector3::ZERO;
-        }
-        else if (type == "box")
-            emitterType_ = EMITTER_BOX;
-        else if (type == "sphere")
-            emitterType_ = EMITTER_SPHERE;
-        else
-            LOGERROR("Unknown particle emitter type " + type);
-    }
-
-    if (rootElem.HasChild("emittersize"))
-        emitterSize_ = rootElem.GetChild("emittersize").GetVector3("value");
-
-    if (rootElem.HasChild("emitterradius"))
-        emitterSize_.x_ = emitterSize_.y_ = emitterSize_.z_ = rootElem.GetChild("emitterradius").GetFloat("value");
-
-    if (rootElem.HasChild("direction"))
-        GetVector3MinMax(rootElem.GetChild("direction"), directionMin_, directionMax_);
-
-    if (rootElem.HasChild("constantforce"))
-        constantForce_ = rootElem.GetChild("constantforce").GetVector3("value");
-
-    if (rootElem.HasChild("dampingforce"))
-        dampingForce_ = rootElem.GetChild("dampingforce").GetFloat("value");
-
-    if (rootElem.HasChild("activetime"))
-        activeTime_ = rootElem.GetChild("activetime").GetFloat("value");
-    if (activeTime_ < 0.0f)
-        activeTime_ = M_INFINITY;
-
-    if (rootElem.HasChild("inactivetime"))
-        inactiveTime_ = rootElem.GetChild("inactivetime").GetFloat("value");
-    if (inactiveTime_ < 0.0f)
-        inactiveTime_ = M_INFINITY;
-
-    if (rootElem.HasChild("emissionrate"))
-        GetFloatMinMax(rootElem.GetChild("emissionrate"), emissionRateMin_, emissionRateMax_);
-
-    if (rootElem.HasChild("interval"))
-    {
-        float intervalMin = 0.0f;
-        float intervalMax = 0.0f;
-        GetFloatMinMax(rootElem.GetChild("interval"), intervalMin, intervalMax);
-        emissionRateMax_ = 1.0f / intervalMin;
-        emissionRateMin_ = 1.0f / intervalMax;
-    }
-
-    if (rootElem.HasChild("particlesize"))
-        GetVector2MinMax(rootElem.GetChild("particlesize"), sizeMin_, sizeMax_);
-
-    if (rootElem.HasChild("timetolive"))
-        GetFloatMinMax(rootElem.GetChild("timetolive"), timeToLiveMin_, timeToLiveMax_);
-
-    if (rootElem.HasChild("velocity"))
-        GetFloatMinMax(rootElem.GetChild("velocity"), velocityMin_, velocityMax_);
-
-    if (rootElem.HasChild("rotation"))
-        GetFloatMinMax(rootElem.GetChild("rotation"), rotationMin_, rotationMax_);
 
 
-    if (rootElem.HasChild("rotationspeed"))
-        GetFloatMinMax(rootElem.GetChild("rotationspeed"), rotationSpeedMin_, rotationSpeedMax_);
-
-    if (rootElem.HasChild("sizedelta"))
-    {
-        XMLElement deltaElem = rootElem.GetChild("sizedelta");
-        if (deltaElem.HasAttribute("add"))
-            sizeAdd_ = deltaElem.GetFloat("add");
-        if (deltaElem.HasAttribute("mul"))
-            sizeMul_ = deltaElem.GetFloat("mul");
-    }
-
-    if (rootElem.HasChild("color"))
-    {
-        ColorFrame colorFrame(rootElem.GetChild("color").GetColor("value"));
-        SetColorFrame(0, colorFrame);
-    }
-
-    if (rootElem.HasChild("colorfade"))
-    {
-        Vector<ColorFrame> fades;
-        for (XMLElement colorFadeElem = rootElem.GetChild("colorfade"); colorFadeElem;
-             colorFadeElem = colorFadeElem.GetNext("colorfade"))
-            fades.Push(ColorFrame(colorFadeElem.GetColor("color"), colorFadeElem.GetFloat("time")));
-
-        SetColorFrames(fades);
-    }
-
-    if (colorFrames_.Empty())
-        colorFrames_.Push(ColorFrame(Color::WHITE));
-
-    if (rootElem.HasChild("texanim"))
-    {
-        Vector<TextureFrame> animations;
-        for (XMLElement animElem = rootElem.GetChild("texanim"); animElem; animElem = animElem.GetNext("texanim"))
-        {
-            TextureFrame animation;
-            animation.uv_ = animElem.GetRect("uv");
-            animation.time_ = animElem.GetFloat("time");
-            animations.Push(animation);
-        }
-
-        SetTextureFrames(animations);
-    }
-
-    return true;
+    bool success = Load(rootElem);
+    if (success)
+        SetMemoryUse(source.GetSize());
+    return success;
 }
 }
 
 
 bool ParticleEffect::EndLoad()
 bool ParticleEffect::EndLoad()
@@ -303,6 +137,7 @@ bool ParticleEffect::Load(const XMLElement& source)
     relative_ = true;
     relative_ = true;
     scaled_ = true;
     scaled_ = true;
     sorted_ = false;
     sorted_ = false;
+    fixedScreenSize_ = false;
     animationLodBias_ = 0.0f;
     animationLodBias_ = 0.0f;
     emitterType_ = EMITTER_SPHERE;
     emitterType_ = EMITTER_SPHERE;
     emitterSize_ = Vector3::ZERO;
     emitterSize_ = Vector3::ZERO;
@@ -328,16 +163,20 @@ bool ParticleEffect::Load(const XMLElement& source)
     sizeMul_ = 1.0f;
     sizeMul_ = 1.0f;
     colorFrames_.Clear();
     colorFrames_.Clear();
     textureFrames_.Clear();
     textureFrames_.Clear();
+    faceCameraMode_ = FC_ROTATE_XYZ;
 
 
     if (source.IsNull())
     if (source.IsNull())
     {
     {
-        LOGERROR("Can not load particle effect from null XML element");
+        URHO3D_LOGERROR("Can not load particle effect from null XML element");
         return false;
         return false;
     }
     }
 
 
     if (source.HasChild("material"))
     if (source.HasChild("material"))
     {
     {
-        material_ = GetSubsystem<ResourceCache>()->GetResource<Material>(source.GetChild("material").GetAttribute("name"));
+        loadMaterialName_ = source.GetChild("material").GetAttribute("name");
+        // If async loading, can not GetResource() the material. But can do a background request for it
+        if (GetAsyncLoadState() == ASYNC_LOADING)
+            GetSubsystem<ResourceCache>()->BackgroundLoadResource<Material>(loadMaterialName_, true, this);
     }
     }
 
 
     if (source.HasChild("numparticles"))
     if (source.HasChild("numparticles"))
@@ -355,6 +194,9 @@ bool ParticleEffect::Load(const XMLElement& source)
     if (source.HasChild("sorted"))
     if (source.HasChild("sorted"))
         sorted_ = source.GetChild("sorted").GetBool("enable");
         sorted_ = source.GetChild("sorted").GetBool("enable");
 
 
+    if (source.HasChild("fixedscreensize"))
+        fixedScreenSize_ = source.GetChild("fixedscreensize").GetBool("enable");
+
     if (source.HasChild("animlodbias"))
     if (source.HasChild("animlodbias"))
         SetAnimationLodBias(source.GetChild("animlodbias").GetFloat("value"));
         SetAnimationLodBias(source.GetChild("animlodbias").GetFloat("value"));
 
 
@@ -367,12 +209,8 @@ bool ParticleEffect::Load(const XMLElement& source)
             emitterType_ = EMITTER_SPHERE;
             emitterType_ = EMITTER_SPHERE;
             emitterSize_ = Vector3::ZERO;
             emitterSize_ = Vector3::ZERO;
         }
         }
-        else if (type == "box")
-            emitterType_ = EMITTER_BOX;
-        else if (type == "sphere")
-            emitterType_ = EMITTER_SPHERE;
         else
         else
-            LOGERROR("Unknown particle emitter type " + type);
+            emitterType_ = (EmitterType)GetStringListIndex(type.CString(), emitterTypeNames, EMITTER_SPHERE);
     }
     }
 
 
     if (source.HasChild("emittersize"))
     if (source.HasChild("emittersize"))
@@ -427,6 +265,12 @@ bool ParticleEffect::Load(const XMLElement& source)
     if (source.HasChild("rotationspeed"))
     if (source.HasChild("rotationspeed"))
         GetFloatMinMax(source.GetChild("rotationspeed"), rotationSpeedMin_, rotationSpeedMax_);
         GetFloatMinMax(source.GetChild("rotationspeed"), rotationSpeedMin_, rotationSpeedMax_);
 
 
+    if (source.HasChild("faceCameraMode"))
+    {
+        String type = source.GetChild("faceCameraMode").GetAttributeLower("value");
+        faceCameraMode_ = (FaceCameraMode)GetStringListIndex(type.CString(), faceCameraModeNames, FC_ROTATE_XYZ);
+    }
+
     if (source.HasChild("sizedelta"))
     if (source.HasChild("sizedelta"))
     {
     {
         XMLElement deltaElem = source.GetChild("sizedelta");
         XMLElement deltaElem = source.GetChild("sizedelta");
@@ -485,7 +329,7 @@ bool ParticleEffect::Save(XMLElement& dest) const
 {
 {
     if (dest.IsNull())
     if (dest.IsNull())
     {
     {
-        LOGERROR("Can not save particle effect to null XML element");
+        URHO3D_LOGERROR("Can not save particle effect to null XML element");
         return false;
         return false;
     }
     }
 
 
@@ -507,6 +351,9 @@ bool ParticleEffect::Save(XMLElement& dest) const
     childElem = dest.CreateChild("sorted");
     childElem = dest.CreateChild("sorted");
     childElem.SetBool("enable", sorted_);
     childElem.SetBool("enable", sorted_);
 
 
+    childElem = dest.CreateChild("fixedscreensize");
+    childElem.SetBool("enable", fixedScreenSize_);
+
     childElem = dest.CreateChild("animlodbias");
     childElem = dest.CreateChild("animlodbias");
     childElem.SetFloat("value", animationLodBias_);
     childElem.SetFloat("value", animationLodBias_);
 
 
@@ -560,6 +407,9 @@ bool ParticleEffect::Save(XMLElement& dest) const
     childElem.SetFloat("add", sizeAdd_);
     childElem.SetFloat("add", sizeAdd_);
     childElem.SetFloat("mul", sizeMul_);
     childElem.SetFloat("mul", sizeMul_);
 
 
+    childElem = dest.CreateChild("faceCameraMode");
+    childElem.SetAttribute("value", faceCameraModeNames[faceCameraMode_]);
+
     if (colorFrames_.Size() == 1)
     if (colorFrames_.Size() == 1)
     {
     {
         childElem = dest.CreateChild("color");
         childElem = dest.CreateChild("color");
@@ -593,7 +443,7 @@ void ParticleEffect::SetMaterial(Material* material)
 
 
 void ParticleEffect::SetNumParticles(unsigned num)
 void ParticleEffect::SetNumParticles(unsigned num)
 {
 {
-    numParticles_ = (unsigned)Max(0, num);
+    numParticles_ = Max(0U, num);
 }
 }
 
 
 void ParticleEffect::SetUpdateInvisible(bool enable)
 void ParticleEffect::SetUpdateInvisible(bool enable)
@@ -616,6 +466,11 @@ void ParticleEffect::SetSorted(bool enable)
     sorted_ = enable;
     sorted_ = enable;
 }
 }
 
 
+void ParticleEffect::SetFixedScreenSize(bool enable)
+{
+    fixedScreenSize_ = enable;
+}
+
 void ParticleEffect::SetAnimationLodBias(float lodBias)
 void ParticleEffect::SetAnimationLodBias(float lodBias)
 {
 {
     animationLodBias_ = lodBias;
     animationLodBias_ = lodBias;
@@ -794,6 +649,11 @@ void ParticleEffect::SetNumColorFrames(unsigned number)
         colorFrames_.Resize(number);
         colorFrames_.Resize(number);
 }
 }
 
 
+void ParticleEffect::SetFaceCameraMode(FaceCameraMode mode)
+{
+    faceCameraMode_ = mode;
+}
+
 void ParticleEffect::SortColorFrames()
 void ParticleEffect::SortColorFrames()
 {
 {
     Vector<ColorFrame> cf = colorFrames_;
     Vector<ColorFrame> cf = colorFrames_;
@@ -822,7 +682,7 @@ void ParticleEffect::AddTextureTime(const Rect& uv, const float time)
         }
         }
     }
     }
 
 
-    // highest time, add last:
+    // Highest time, add last
     textureFrames_[s].uv_ = uv;
     textureFrames_[s].uv_ = uv;
     textureFrames_[s].time_ = time;
     textureFrames_[s].time_ = time;
 }
 }
@@ -872,6 +732,50 @@ void ParticleEffect::SortTextureFrames()
         AddTextureFrame(tf[i]);
         AddTextureFrame(tf[i]);
 }
 }
 
 
+SharedPtr<ParticleEffect> ParticleEffect::Clone(const String& cloneName) const
+{
+    SharedPtr<ParticleEffect> ret(new ParticleEffect(context_));
+
+    ret->SetName(cloneName);
+    ret->material_ = material_;
+    ret->numParticles_ = numParticles_;
+    ret->updateInvisible_ = updateInvisible_;
+    ret->relative_ = relative_;
+    ret->scaled_ = scaled_;
+    ret->sorted_ = sorted_;
+    ret->fixedScreenSize_ = fixedScreenSize_;
+    ret->animationLodBias_ = animationLodBias_;
+    ret->emitterType_ = emitterType_;
+    ret->emitterSize_ = emitterSize_;
+    ret->directionMin_ = directionMin_;
+    ret->directionMax_ = directionMax_;
+    ret->constantForce_ = constantForce_;
+    ret->dampingForce_ = dampingForce_;
+    ret->activeTime_ = activeTime_;
+    ret->inactiveTime_ = inactiveTime_;
+    ret->emissionRateMin_ = emissionRateMin_;
+    ret->emissionRateMax_ = emissionRateMax_;
+    ret->sizeMin_ = sizeMin_;
+    ret->sizeMax_ = sizeMax_;
+    ret->timeToLiveMin_ = timeToLiveMin_;
+    ret->timeToLiveMax_ = timeToLiveMax_;
+    ret->velocityMin_ = velocityMin_;
+    ret->velocityMax_ = velocityMax_;
+    ret->rotationMin_ = rotationMin_;
+    ret->rotationMax_ = rotationMax_;
+    ret->rotationSpeedMin_ = rotationSpeedMin_;
+    ret->rotationSpeedMax_ = rotationSpeedMax_;
+    ret->sizeAdd_ = sizeAdd_;
+    ret->sizeMul_ = sizeMul_;
+    ret->colorFrames_ = colorFrames_;
+    ret->textureFrames_ = textureFrames_;
+    ret->faceCameraMode_ = faceCameraMode_;
+    /// \todo Zero if source was created programmatically
+    ret->SetMemoryUse(GetMemoryUse());
+
+    return ret;
+}
+
 const ColorFrame* ParticleEffect::GetColorFrame(unsigned index) const
 const ColorFrame* ParticleEffect::GetColorFrame(unsigned index) const
 {
 {
     return index < colorFrames_.Size() ? &colorFrames_[index] : (ColorFrame*)0;
     return index < colorFrames_.Size() ? &colorFrames_[index] : (ColorFrame*)0;

+ 25 - 8
Source/Atomic/Atomic3D/ParticleEffect.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,16 @@
 
 
 #pragma once
 #pragma once
 
 
+#include "../Graphics/GraphicsDefs.h"
 #include "../Resource/Resource.h"
 #include "../Resource/Resource.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// Particle emitter shapes.
 /// Particle emitter shapes.
 enum EmitterType
 enum EmitterType
 {
 {
-    EMITTER_SPHERE,
+    EMITTER_SPHERE = 0,
     EMITTER_BOX
     EMITTER_BOX
 };
 };
 
 
@@ -99,9 +100,9 @@ class XMLFile;
 class XMLElement;
 class XMLElement;
 
 
 /// %Particle effect definition.
 /// %Particle effect definition.
-class ATOMIC_API ParticleEffect : public Resource
+class URHO3D_API ParticleEffect : public Resource
 {
 {
-    OBJECT(ParticleEffect);
+    URHO3D_OBJECT(ParticleEffect, Resource);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -128,12 +129,14 @@ public:
     void SetNumParticles(unsigned num);
     void SetNumParticles(unsigned num);
     /// Set whether to update when particles are not visible.
     /// Set whether to update when particles are not visible.
     void SetUpdateInvisible(bool enable);
     void SetUpdateInvisible(bool enable);
-    /// Set whether billboards are relative to the scene node. Default true.
+    /// Set whether billboards are relative to the scene node.
     void SetRelative(bool enable);
     void SetRelative(bool enable);
-    /// Set scaled.
+    /// Set whether scene node scale affects billboards' size.
     void SetScaled(bool enable);
     void SetScaled(bool enable);
-    /// Set sorted.
+    /// Set whether billboards are sorted by distance.
     void SetSorted(bool enable);
     void SetSorted(bool enable);
+    /// Set whether billboards have fixed size on screen (measured in pixels) regardless of distance to camera.
+    void SetFixedScreenSize(bool enable);
     /// Set animation LOD bias.
     /// Set animation LOD bias.
     void SetAnimationLodBias(float lodBias);
     void SetAnimationLodBias(float lodBias);
     /// Set emitter type.
     /// Set emitter type.
@@ -180,6 +183,8 @@ public:
     void SetSizeAdd(float sizeAdd);
     void SetSizeAdd(float sizeAdd);
     /// Set particle size multiplicative modifier.
     /// Set particle size multiplicative modifier.
     void SetSizeMul(float sizeMul);
     void SetSizeMul(float sizeMul);
+    /// Set how the particles should rotate in relation to the camera. Default is to follow camera rotation on all axes (FC_ROTATE_XYZ.)
+    void SetFaceCameraMode(FaceCameraMode mode);
 
 
     /// Add a color frame sorted in the correct position based on time.
     /// Add a color frame sorted in the correct position based on time.
     void AddColorTime(const Color& color, const float time);
     void AddColorTime(const Color& color, const float time);
@@ -210,6 +215,8 @@ public:
     void SetNumTextureFrames(unsigned number);
     void SetNumTextureFrames(unsigned number);
     /// Sort the list of texture frames based on time.
     /// Sort the list of texture frames based on time.
     void SortTextureFrames();
     void SortTextureFrames();
+    /// Clone the particle effect.
+    SharedPtr<ParticleEffect> Clone(const String& cloneName = String::EMPTY) const;
 
 
     /// Return material.
     /// Return material.
     Material* GetMaterial() const { return material_; }
     Material* GetMaterial() const { return material_; }
@@ -229,6 +236,9 @@ public:
     /// Return whether billboards are sorted.
     /// Return whether billboards are sorted.
     bool IsSorted() const { return sorted_; }
     bool IsSorted() const { return sorted_; }
 
 
+    /// Return whether billboards are fixed screen size.
+    bool IsFixedScreenSize() const { return fixedScreenSize_; }
+
     /// Return animation Lod bias.
     /// Return animation Lod bias.
     float GetAnimationLodBias() const { return animationLodBias_; }
     float GetAnimationLodBias() const { return animationLodBias_; }
 
 
@@ -316,6 +326,9 @@ public:
     /// Return a texture animation frame, or null if outside range.
     /// Return a texture animation frame, or null if outside range.
     const TextureFrame* GetTextureFrame(unsigned index) const;
     const TextureFrame* GetTextureFrame(unsigned index) const;
 
 
+    /// Return how the particles rotate in relation to the camera.
+    FaceCameraMode GetFaceCameraMode() const { return faceCameraMode_; }
+
     /// Return random direction.
     /// Return random direction.
     Vector3 GetRandomDirection() const;
     Vector3 GetRandomDirection() const;
     /// Return random size.
     /// Return random size.
@@ -349,6 +362,8 @@ private:
     bool scaled_;
     bool scaled_;
     /// Billboards sorted flag.
     /// Billboards sorted flag.
     bool sorted_;
     bool sorted_;
+    /// Billboards fixed screen size flag.
+    bool fixedScreenSize_;
     /// Animation LOD bias.
     /// Animation LOD bias.
     float animationLodBias_;
     float animationLodBias_;
     /// Emitter shape.
     /// Emitter shape.
@@ -401,6 +416,8 @@ private:
     Vector<TextureFrame> textureFrames_;
     Vector<TextureFrame> textureFrames_;
     /// Material name acquired during BeginLoad().
     /// Material name acquired during BeginLoad().
     String loadMaterialName_;
     String loadMaterialName_;
+    /// Particle rotation mode in relation to the camera.
+    FaceCameraMode faceCameraMode_;
 };
 };
 
 
 }
 }

+ 108 - 61
Source/Atomic/Atomic3D/ParticleEmitter.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -24,8 +24,9 @@
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
-#include "../Atomic3D/ParticleEffect.h"
-#include "../Atomic3D/ParticleEmitter.h"
+#include "../Graphics/DrawableEvents.h"
+#include "../Graphics/ParticleEffect.h"
+#include "../Graphics/ParticleEmitter.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceEvents.h"
 #include "../Resource/ResourceEvents.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
@@ -33,7 +34,7 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const char* GEOMETRY_CATEGORY;
 extern const char* GEOMETRY_CATEGORY;
@@ -46,7 +47,10 @@ ParticleEmitter::ParticleEmitter(Context* context) :
     emissionTimer_(0.0f),
     emissionTimer_(0.0f),
     lastTimeStep_(0.0f),
     lastTimeStep_(0.0f),
     lastUpdateFrameNumber_(M_MAX_UNSIGNED),
     lastUpdateFrameNumber_(M_MAX_UNSIGNED),
-    serializeParticles_(true)
+    emitting_(true),
+    needUpdate_(false),
+    serializeParticles_(true),
+    sendFinishEvent_(true)
 {
 {
     SetNumParticles(DEFAULT_NUM_PARTICLES);
     SetNumParticles(DEFAULT_NUM_PARTICLES);
 }
 }
@@ -59,24 +63,23 @@ void ParticleEmitter::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<ParticleEmitter>(GEOMETRY_CATEGORY);
     context->RegisterFactory<ParticleEmitter>(GEOMETRY_CATEGORY);
 
 
-    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("Effect", GetEffectAttr, SetEffectAttr, ResourceRef, ResourceRef(ParticleEffect::GetTypeStatic()),
+    URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Effect", GetEffectAttr, SetEffectAttr, ResourceRef, ResourceRef(ParticleEffect::GetTypeStatic()),
         AM_DEFAULT);
         AM_DEFAULT);
-    ENUM_ATTRIBUTE("Face Camera Mode", faceCameraMode_, faceCameraModeNames, FC_ROTATE_XYZ, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
-    ATTRIBUTE("Is Emitting", bool, emitting_, true, AM_FILE);
-    ATTRIBUTE("Period Timer", float, periodTimer_, 0.0f, AM_FILE | AM_NOEDIT);
-    ATTRIBUTE("Emission Timer", float, emissionTimer_, 0.0f, AM_FILE | AM_NOEDIT);
-    COPY_BASE_ATTRIBUTES(Drawable);
-    MIXED_ACCESSOR_ATTRIBUTE("Particles", GetParticlesAttr, SetParticlesAttr, VariantVector, Variant::emptyVariantVector,
+    URHO3D_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Is Emitting", bool, emitting_, true, AM_FILE);
+    URHO3D_ATTRIBUTE("Period Timer", float, periodTimer_, 0.0f, AM_FILE | AM_NOEDIT);
+    URHO3D_ATTRIBUTE("Emission Timer", float, emissionTimer_, 0.0f, AM_FILE | AM_NOEDIT);
+    URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Particles", GetParticlesAttr, SetParticlesAttr, VariantVector, Variant::emptyVariantVector,
         AM_FILE | AM_NOEDIT);
         AM_FILE | AM_NOEDIT);
-    MIXED_ACCESSOR_ATTRIBUTE("Billboards", GetParticleBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector,
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Billboards", GetParticleBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector,
         AM_FILE | AM_NOEDIT);
         AM_FILE | AM_NOEDIT);
-    ATTRIBUTE("Serialize Particles", bool, serializeParticles_, true, AM_FILE);
+    URHO3D_ATTRIBUTE("Serialize Particles", bool, serializeParticles_, true, AM_FILE);
 }
 }
 
 
 void ParticleEmitter::OnSetEnabled()
 void ParticleEmitter::OnSetEnabled()
@@ -87,7 +90,7 @@ void ParticleEmitter::OnSetEnabled()
     if (scene)
     if (scene)
     {
     {
         if (IsEnabledEffective())
         if (IsEnabledEffective())
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter, HandleScenePostUpdate));
+            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(ParticleEmitter, HandleScenePostUpdate));
         else
         else
             UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
             UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
     }
     }
@@ -125,6 +128,7 @@ void ParticleEmitter::Update(const FrameInfo& frame)
         if (inactiveTime && periodTimer_ >= inactiveTime)
         if (inactiveTime && periodTimer_ >= inactiveTime)
         {
         {
             emitting_ = true;
             emitting_ = true;
+            sendFinishEvent_ = true;
             periodTimer_ -= inactiveTime;
             periodTimer_ -= inactiveTime;
         }
         }
         // If emitter has an indefinite stop interval, keep period timer reset to allow restarting emission in the editor
         // If emitter has an indefinite stop interval, keep period timer reset to allow restarting emission in the editor
@@ -169,16 +173,16 @@ void ParticleEmitter::Update(const FrameInfo& frame)
     for (unsigned i = 0; i < particles_.Size(); ++i)
     for (unsigned i = 0; i < particles_.Size(); ++i)
     {
     {
         Particle& particle = particles_[i];
         Particle& particle = particles_[i];
-        SharedPtr<Billboard> billboard = billboards_[i];
+        Billboard& billboard = billboards_[i];
 
 
-        if (billboard->enabled_)
+        if (billboard.enabled_)
         {
         {
             needCommit = true;
             needCommit = true;
 
 
             // Time to live
             // Time to live
             if (particle.timer_ >= particle.timeToLive_)
             if (particle.timer_ >= particle.timeToLive_)
             {
             {
-                billboard->enabled_ = false;
+                billboard.enabled_ = false;
                 continue;
                 continue;
             }
             }
             particle.timer_ += lastTimeStep_;
             particle.timer_ += lastTimeStep_;
@@ -199,10 +203,11 @@ void ParticleEmitter::Update(const FrameInfo& frame)
                 Vector3 force = -dampingForce * particle.velocity_;
                 Vector3 force = -dampingForce * particle.velocity_;
                 particle.velocity_ += lastTimeStep_ * force;
                 particle.velocity_ += lastTimeStep_ * force;
             }
             }
-            billboard->position_ += lastTimeStep_ * particle.velocity_ * scaleVector;
+            billboard.position_ += lastTimeStep_ * particle.velocity_ * scaleVector;
+            billboard.direction_ = particle.velocity_.Normalized();
 
 
             // Rotation
             // Rotation
-            billboard->rotation_ += lastTimeStep_ * particle.rotationSpeed_;
+            billboard.rotation_ += lastTimeStep_ * particle.rotationSpeed_;
 
 
             // Scaling
             // Scaling
             float sizeAdd = effect_->GetSizeAdd();
             float sizeAdd = effect_->GetSizeAdd();
@@ -214,7 +219,7 @@ void ParticleEmitter::Update(const FrameInfo& frame)
                     particle.scale_ = 0.0f;
                     particle.scale_ = 0.0f;
                 if (sizeMul != 1.0f)
                 if (sizeMul != 1.0f)
                     particle.scale_ *= (lastTimeStep_ * (sizeMul - 1.0f)) + 1.0f;
                     particle.scale_ *= (lastTimeStep_ * (sizeMul - 1.0f)) + 1.0f;
-                billboard->size_ = particle.size_ * particle.scale_;
+                billboard.size_ = particle.size_ * particle.scale_;
             }
             }
 
 
             // Color interpolation
             // Color interpolation
@@ -228,9 +233,9 @@ void ParticleEmitter::Update(const FrameInfo& frame)
                         ++index;
                         ++index;
                 }
                 }
                 if (index < colorFrames_.Size() - 1)
                 if (index < colorFrames_.Size() - 1)
-                    billboard->color_ = colorFrames_[index].Interpolate(colorFrames_[index + 1], particle.timer_);
+                    billboard.color_ = colorFrames_[index].Interpolate(colorFrames_[index + 1], particle.timer_);
                 else
                 else
-                    billboard->color_ = colorFrames_[index].color_;
+                    billboard.color_ = colorFrames_[index].color_;
             }
             }
 
 
             // Texture animation
             // Texture animation
@@ -240,7 +245,7 @@ void ParticleEmitter::Update(const FrameInfo& frame)
             {
             {
                 if (particle.timer_ >= textureFrames_[texIndex + 1].time_)
                 if (particle.timer_ >= textureFrames_[texIndex + 1].time_)
                 {
                 {
-                    billboard->uv_ = textureFrames_[texIndex + 1].uv_;
+                    billboard.uv_ = textureFrames_[texIndex + 1].uv_;
                     ++texIndex;
                     ++texIndex;
                 }
                 }
             }
             }
@@ -267,7 +272,7 @@ void ParticleEmitter::SetEffect(ParticleEffect* effect)
     effect_ = effect;
     effect_ = effect;
 
 
     if (effect_)
     if (effect_)
-        SubscribeToEvent(effect_, E_RELOADFINISHED, HANDLER(ParticleEmitter, HandleEffectReloadFinished));
+        SubscribeToEvent(effect_, E_RELOADFINISHED, URHO3D_HANDLER(ParticleEmitter, HandleEffectReloadFinished));
 
 
     ApplyEffect();
     ApplyEffect();
     MarkNetworkUpdate();
     MarkNetworkUpdate();
@@ -290,6 +295,7 @@ void ParticleEmitter::SetEmitting(bool enable)
     if (enable != emitting_)
     if (enable != emitting_)
     {
     {
         emitting_ = enable;
         emitting_ = enable;
+        sendFinishEvent_ = enable;
         periodTimer_ = 0.0f;
         periodTimer_ = 0.0f;
         // Note: network update does not need to be marked as this is a file only attribute
         // Note: network update does not need to be marked as this is a file only attribute
     }
     }
@@ -308,8 +314,8 @@ void ParticleEmitter::ResetEmissionTimer()
 
 
 void ParticleEmitter::RemoveAllParticles()
 void ParticleEmitter::RemoveAllParticles()
 {
 {
-    for (Vector<SharedPtr<Billboard>>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
-        i->Get()->enabled_ = false;
+    for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+        i->enabled_ = false;
 
 
     Commit();
     Commit();
 }
 }
@@ -331,7 +337,14 @@ void ParticleEmitter::ApplyEffect()
     SetRelative(effect_->IsRelative());
     SetRelative(effect_->IsRelative());
     SetScaled(effect_->IsScaled());
     SetScaled(effect_->IsScaled());
     SetSorted(effect_->IsSorted());
     SetSorted(effect_->IsSorted());
+    SetFixedScreenSize(effect_->IsFixedScreenSize());
     SetAnimationLodBias(effect_->GetAnimationLodBias());
     SetAnimationLodBias(effect_->GetAnimationLodBias());
+    SetFaceCameraMode(effect_->GetFaceCameraMode());
+}
+
+ParticleEffect* ParticleEmitter::GetEffect() const
+{
+    return effect_;
 }
 }
 
 
 void ParticleEmitter::SetEffectAttr(const ResourceRef& value)
 void ParticleEmitter::SetEffectAttr(const ResourceRef& value)
@@ -397,18 +410,18 @@ VariantVector ParticleEmitter::GetParticleBillboardsAttr() const
         return ret;
         return ret;
     }
     }
 
 
-    ret.Reserve(billboards_.Size() * 6 + 1);
+    ret.Reserve(billboards_.Size() * 7 + 1);
     ret.Push(billboards_.Size());
     ret.Push(billboards_.Size());
 
 
-    for (Vector<SharedPtr<Billboard>>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
     {
     {
-        Billboard *bb = i->Get();
-        ret.Push(bb->position_);
-        ret.Push(bb->size_);
-        ret.Push(Vector4(bb->uv_.min_.x_, bb->uv_.min_.y_, bb->uv_.max_.x_, bb->uv_.max_.y_));
-        ret.Push(bb->color_);
-        ret.Push(bb->rotation_);
-        ret.Push(bb->enabled_);
+        ret.Push(i->position_);
+        ret.Push(i->size_);
+        ret.Push(Vector4(i->uv_.min_.x_, i->uv_.min_.y_, i->uv_.max_.x_, i->uv_.max_.y_));
+        ret.Push(i->color_);
+        ret.Push(i->rotation_);
+        ret.Push(i->direction_);
+        ret.Push(i->enabled_);
     }
     }
 
 
     return ret;
     return ret;
@@ -419,7 +432,7 @@ void ParticleEmitter::OnSceneSet(Scene* scene)
     BillboardSet::OnSceneSet(scene);
     BillboardSet::OnSceneSet(scene);
 
 
     if (scene && IsEnabledEffective())
     if (scene && IsEnabledEffective())
-        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter, HandleScenePostUpdate));
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(ParticleEmitter, HandleScenePostUpdate));
     else if (!scene)
     else if (!scene)
          UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
          UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
 }
 }
@@ -431,11 +444,13 @@ bool ParticleEmitter::EmitNewParticle()
         return false;
         return false;
     assert(index < particles_.Size());
     assert(index < particles_.Size());
     Particle& particle = particles_[index];
     Particle& particle = particles_[index];
-    SharedPtr<Billboard> billboard = billboards_[index];
+    Billboard& billboard = billboards_[index];
 
 
-    Vector3 startPos;
     Vector3 startDir;
     Vector3 startDir;
+    Vector3 startPos;
 
 
+    startDir = effect_->GetRandomDirection();
+    startDir.Normalize();
 
 
     switch (effect_->GetEmitterType())
     switch (effect_->GetEmitterType())
     {
     {
@@ -463,8 +478,18 @@ bool ParticleEmitter::EmitNewParticle()
         break;
         break;
     }
     }
 
 
-    startDir = effect_->GetRandomDirection();
-    startDir.Normalize();
+    particle.size_ = effect_->GetRandomSize();
+    particle.timer_ = 0.0f;
+    particle.timeToLive_ = effect_->GetRandomTimeToLive();
+    particle.scale_ = 1.0f;
+    particle.rotationSpeed_ = effect_->GetRandomRotationSpeed();
+    particle.colorIndex_ = 0;
+    particle.texIndex_ = 0;
+
+    if (faceCameraMode_ == FC_DIRECTION)
+    {
+        startPos += startDir * particle.size_.y_;
+    }
 
 
     if (!relative_)
     if (!relative_)
     {
     {
@@ -473,22 +498,16 @@ bool ParticleEmitter::EmitNewParticle()
     };
     };
 
 
     particle.velocity_ = effect_->GetRandomVelocity() * startDir;
     particle.velocity_ = effect_->GetRandomVelocity() * startDir;
-    particle.size_ = effect_->GetRandomSize();
-    particle.timer_ = 0.0f;
-    particle.timeToLive_ = effect_->GetRandomTimeToLive();
-    particle.scale_ = 1.0f;
-    particle.rotationSpeed_ = effect_->GetRandomRotationSpeed();
-    particle.colorIndex_ = 0;
-    particle.texIndex_ = 0;
 
 
-    billboard->position_ = startPos;
-    billboard->size_ = particles_[index].size_;
+    billboard.position_ = startPos;
+    billboard.size_ = particles_[index].size_;
     const Vector<TextureFrame>& textureFrames_ = effect_->GetTextureFrames();
     const Vector<TextureFrame>& textureFrames_ = effect_->GetTextureFrames();
-    billboard->uv_ = textureFrames_.Size() ? textureFrames_[0].uv_ : Rect::POSITIVE;
-    billboard->rotation_ = effect_->GetRandomRotation();
+    billboard.uv_ = textureFrames_.Size() ? textureFrames_[0].uv_ : Rect::POSITIVE;
+    billboard.rotation_ = effect_->GetRandomRotation();
     const Vector<ColorFrame>& colorFrames_ = effect_->GetColorFrames();
     const Vector<ColorFrame>& colorFrames_ = effect_->GetColorFrames();
-    billboard->color_ = colorFrames_.Size() ? colorFrames_[0].color_ : Color();
-    billboard->enabled_ = true;
+    billboard.color_ = colorFrames_.Size() ? colorFrames_[0].color_ : Color();
+    billboard.enabled_ = true;
+    billboard.direction_ = startDir;
 
 
     return true;
     return true;
 }
 }
@@ -497,7 +516,7 @@ unsigned ParticleEmitter::GetFreeParticle() const
 {
 {
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     {
     {
-        if (!billboards_[i]->enabled_)
+        if (!billboards_[i].enabled_)
             return i;
             return i;
     }
     }
 
 
@@ -518,6 +537,34 @@ void ParticleEmitter::HandleScenePostUpdate(StringHash eventType, VariantMap& ev
         needUpdate_ = true;
         needUpdate_ = true;
         MarkForUpdate();
         MarkForUpdate();
     }
     }
+
+    if (node_ && !emitting_ && sendFinishEvent_)
+    {
+        // Send finished event only once all billboards are gone
+        bool hasEnabledBillboards = false;
+
+        for (unsigned i = 0; i < billboards_.Size(); ++i)
+        {
+            if (billboards_[i].enabled_)
+            {
+                hasEnabledBillboards = true;
+                break;
+            }
+        }
+
+        if (!hasEnabledBillboards)
+        {
+            sendFinishEvent_ = false;
+
+            using namespace ParticleEffectFinished;
+
+            VariantMap& eventData = GetEventDataMap();
+            eventData[P_NODE] = node_;
+            eventData[P_EFFECT] = effect_;
+
+            node_->SendEvent(E_PARTICLEEFFECTFINISHED, eventData);
+        }
+    }
 }
 }
 
 
 void ParticleEmitter::HandleEffectReloadFinished(StringHash eventType, VariantMap& eventData)
 void ParticleEmitter::HandleEffectReloadFinished(StringHash eventType, VariantMap& eventData)

+ 8 - 6
Source/Atomic/Atomic3D/ParticleEmitter.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,9 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic3D/BillboardSet.h"
+#include "../Graphics/BillboardSet.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 class ParticleEffect;
 class ParticleEffect;
@@ -51,9 +51,9 @@ struct Particle
 };
 };
 
 
 /// %Particle emitter component.
 /// %Particle emitter component.
-class ATOMIC_API ParticleEmitter : public BillboardSet
+class URHO3D_API ParticleEmitter : public BillboardSet
 {
 {
-    OBJECT(ParticleEmitter);
+    URHO3D_OBJECT(ParticleEmitter, BillboardSet);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -86,7 +86,7 @@ public:
     void ApplyEffect();
     void ApplyEffect();
 
 
     /// Return particle effect.
     /// Return particle effect.
-    ParticleEffect* GetEffect() const { return effect_; }
+    ParticleEffect* GetEffect() const;
 
 
     /// Return maximum number of particles.
     /// Return maximum number of particles.
     unsigned GetNumParticles() const { return particles_.Size(); }
     unsigned GetNumParticles() const { return particles_.Size(); }
@@ -141,6 +141,8 @@ private:
     bool needUpdate_;
     bool needUpdate_;
     /// Serialize particles flag.
     /// Serialize particles flag.
     bool serializeParticles_;
     bool serializeParticles_;
+    /// Ready to send effect finish event flag.
+    bool sendFinishEvent_;
 };
 };
 
 
 }
 }

+ 4 - 4
Source/Atomic/Atomic3D/Skeleton.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,12 +22,12 @@
 
 
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
-#include "../Atomic3D/Skeleton.h"
+#include "../Graphics/Skeleton.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 Skeleton::Skeleton() :
 Skeleton::Skeleton() :
@@ -119,7 +119,7 @@ void Skeleton::SetRootBoneIndex(unsigned index)
     if (index < bones_.Size())
     if (index < bones_.Size())
         rootBoneIndex_ = index;
         rootBoneIndex_ = index;
     else
     else
-        LOGERROR("Root bone index out of bounds");
+        URHO3D_LOGERROR("Root bone index out of bounds");
 }
 }
 
 
 void Skeleton::ClearBones()
 void Skeleton::ClearBones()

+ 3 - 3
Source/Atomic/Atomic3D/Skeleton.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,7 @@
 #include "../Math/BoundingBox.h"
 #include "../Math/BoundingBox.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 static const unsigned BONECOLLISION_NONE = 0x0;
 static const unsigned BONECOLLISION_NONE = 0x0;
@@ -78,7 +78,7 @@ struct Bone
 };
 };
 
 
 /// Hierarchical collection of bones.
 /// Hierarchical collection of bones.
-class ATOMIC_API Skeleton
+class URHO3D_API Skeleton
 {
 {
 public:
 public:
     /// Construct an empty skeleton.
     /// Construct an empty skeleton.

+ 4 - 4
Source/Atomic/Atomic3D/Skybox.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -25,12 +25,12 @@
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Graphics/Batch.h"
 #include "../Graphics/Batch.h"
 #include "../Graphics/Camera.h"
 #include "../Graphics/Camera.h"
-#include "../Atomic3D/Skybox.h"
+#include "../Graphics/Skybox.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 extern const char* GEOMETRY_CATEGORY;
 extern const char* GEOMETRY_CATEGORY;
@@ -49,7 +49,7 @@ void Skybox::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<Skybox>(GEOMETRY_CATEGORY);
     context->RegisterFactory<Skybox>(GEOMETRY_CATEGORY);
 
 
-    COPY_BASE_ATTRIBUTES(StaticModel);
+    URHO3D_COPY_BASE_ATTRIBUTES(StaticModel);
 }
 }
 
 
 void Skybox::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
 void Skybox::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)

+ 5 - 5
Source/Atomic/Atomic3D/Skybox.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2008-2016 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Atomic3D/StaticModel.h"
+#include "../Graphics/StaticModel.h"
 
 
-namespace Atomic
+namespace Urho3D
 {
 {
 
 
 /// Static model component with fixed position in relation to the camera.
 /// Static model component with fixed position in relation to the camera.
-class ATOMIC_API Skybox : public StaticModel
+class URHO3D_API Skybox : public StaticModel
 {
 {
-    OBJECT(Skybox);
+    URHO3D_OBJECT(Skybox, StaticModel);
 
 
 public:
 public:
     /// Construct.
     /// Construct.

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно