Эх сурвалжийг харах

Removing Atomic3D sources

Josh Engebretson 9 жил өмнө
parent
commit
320fa1c188
36 өөрчлөгдсөн 0 нэмэгдсэн , 14006 устгасан
  1. 0 1419
      Source/Atomic/Atomic3D/AnimatedModel.cpp
  2. 0 262
      Source/Atomic/Atomic3D/AnimatedModel.h
  3. 0 398
      Source/Atomic/Atomic3D/Animation.cpp
  4. 0 194
      Source/Atomic/Atomic3D/Animation.h
  5. 0 898
      Source/Atomic/Atomic3D/AnimationController.cpp
  6. 0 209
      Source/Atomic/Atomic3D/AnimationController.h
  7. 0 591
      Source/Atomic/Atomic3D/AnimationState.cpp
  8. 0 178
      Source/Atomic/Atomic3D/AnimationState.h
  9. 0 40
      Source/Atomic/Atomic3D/Atomic3D.cpp
  10. 0 9
      Source/Atomic/Atomic3D/Atomic3D.h
  11. 0 760
      Source/Atomic/Atomic3D/BillboardSet.cpp
  12. 0 241
      Source/Atomic/Atomic3D/BillboardSet.h
  13. 0 537
      Source/Atomic/Atomic3D/CustomGeometry.cpp
  14. 0 148
      Source/Atomic/Atomic3D/CustomGeometry.h
  15. 0 1170
      Source/Atomic/Atomic3D/DecalSet.cpp
  16. 0 253
      Source/Atomic/Atomic3D/DecalSet.h
  17. 0 106
      Source/Atomic/Atomic3D/LMStaticModel.cpp
  18. 0 46
      Source/Atomic/Atomic3D/LMStaticModel.h
  19. 0 756
      Source/Atomic/Atomic3D/Model.cpp
  20. 0 233
      Source/Atomic/Atomic3D/Model.h
  21. 0 866
      Source/Atomic/Atomic3D/ParticleEffect.cpp
  22. 0 423
      Source/Atomic/Atomic3D/ParticleEffect.h
  23. 0 577
      Source/Atomic/Atomic3D/ParticleEmitter.cpp
  24. 0 148
      Source/Atomic/Atomic3D/ParticleEmitter.h
  25. 0 181
      Source/Atomic/Atomic3D/Skeleton.cpp
  26. 0 132
      Source/Atomic/Atomic3D/Skeleton.h
  27. 0 88
      Source/Atomic/Atomic3D/Skybox.cpp
  28. 0 58
      Source/Atomic/Atomic3D/Skybox.h
  29. 0 458
      Source/Atomic/Atomic3D/StaticModel.cpp
  30. 0 130
      Source/Atomic/Atomic3D/StaticModel.h
  31. 0 400
      Source/Atomic/Atomic3D/StaticModelGroup.cpp
  32. 0 95
      Source/Atomic/Atomic3D/StaticModelGroup.h
  33. 0 1286
      Source/Atomic/Atomic3D/Terrain.cpp
  34. 0 279
      Source/Atomic/Atomic3D/Terrain.h
  35. 0 294
      Source/Atomic/Atomic3D/TerrainPatch.cpp
  36. 0 143
      Source/Atomic/Atomic3D/TerrainPatch.h

+ 0 - 1419
Source/Atomic/Atomic3D/AnimatedModel.cpp

@@ -1,1419 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/AnimatedModel.h"
-#include "../Graphics/Animation.h"
-#include "../Graphics/AnimationState.h"
-#include "../Graphics/Batch.h"
-#include "../Graphics/Camera.h"
-#include "../Graphics/DebugRenderer.h"
-#include "../Graphics/DrawableEvents.h"
-#include "../Graphics/Geometry.h"
-#include "../Graphics/Graphics.h"
-#include "../Graphics/IndexBuffer.h"
-#include "../Graphics/Material.h"
-#include "../Graphics/Octree.h"
-#include "../Graphics/VertexBuffer.h"
-#include "../IO/Log.h"
-#include "../Resource/ResourceCache.h"
-#include "../Resource/ResourceEvents.h"
-#include "../Scene/Scene.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-
-static bool CompareAnimationOrder(const SharedPtr<AnimationState>& lhs, const SharedPtr<AnimationState>& rhs)
-{
-    return lhs->GetLayer() < rhs->GetLayer();
-}
-
-static const unsigned MAX_ANIMATION_STATES = 256;
-
-AnimatedModel::AnimatedModel(Context* context) :
-    StaticModel(context),
-    animationLodFrameNumber_(0),
-    morphElementMask_(0),
-    animationLodBias_(1.0f),
-    animationLodTimer_(-1.0f),
-    animationLodDistance_(0.0f),
-    updateInvisible_(false),
-    animationDirty_(false),
-    animationOrderDirty_(false),
-    morphsDirty_(false),
-    skinningDirty_(true),
-    boneBoundingBoxDirty_(true),
-    isMaster_(true),
-    loading_(false),
-    assignBonesPending_(false),
-    forceAnimationUpdate_(false)
-{
-}
-
-AnimatedModel::~AnimatedModel()
-{
-    // When being destroyed, remove the bone hierarchy if appropriate (last AnimatedModel in the node)
-    Bone* rootBone = skeleton_.GetRootBone();
-    if (rootBone && rootBone->node_)
-    {
-        Node* parent = rootBone->node_->GetParent();
-        if (parent && !parent->GetComponent<AnimatedModel>())
-            RemoveRootBone();
-    }
-}
-
-void AnimatedModel::RegisterObject(Context* context)
-{
-    context->RegisterFactory<AnimatedModel>(GEOMETRY_CATEGORY);
-
-    ATOMIC_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Material", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()),
-        AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Is Occluder", bool, occluder_, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Update When Invisible", GetUpdateInvisible, SetUpdateInvisible, bool, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
-    ATOMIC_COPY_BASE_ATTRIBUTES(Drawable);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Bone Animation Enabled", GetBonesEnabledAttr, SetBonesEnabledAttr, VariantVector,
-        Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Animation States", GetAnimationStatesAttr, SetAnimationStatesAttr, VariantVector,
-        Variant::emptyVariantVector, AM_FILE);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Morphs", GetMorphsAttr, SetMorphsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
-        AM_DEFAULT | AM_NOEDIT);
-}
-
-bool AnimatedModel::Load(Deserializer& source, bool setInstanceDefault)
-{
-    loading_ = true;
-    bool success = Component::Load(source, setInstanceDefault);
-    loading_ = false;
-
-    return success;
-}
-
-bool AnimatedModel::LoadXML(const XMLElement& source, bool setInstanceDefault)
-{
-    loading_ = true;
-    bool success = Component::LoadXML(source, setInstanceDefault);
-    loading_ = false;
-
-    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()
-{
-    if (assignBonesPending_)
-        AssignBoneNodes();
-}
-
-void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
-{
-    // If no bones or no bone-level testing, use the StaticModel test
-    RayQueryLevel level = query.level_;
-    if (level < RAY_TRIANGLE || !skeleton_.GetNumBones())
-    {
-        StaticModel::ProcessRayQuery(query, results);
-        return;
-    }
-
-    // Check ray hit distance to AABB before proceeding with bone-level tests
-    if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_)
-        return;
-
-    const Vector<Bone>& bones = skeleton_.GetBones();
-    Sphere boneSphere;
-
-    for (unsigned i = 0; i < bones.Size(); ++i)
-    {
-        const Bone& bone = bones[i];
-        if (!bone.node_)
-            continue;
-
-        float distance;
-
-        // Use hitbox if available
-        if (bone.collisionMask_ & BONECOLLISION_BOX)
-        {
-            // Do an initial crude test using the bone's AABB
-            const BoundingBox& box = bone.boundingBox_;
-            const Matrix3x4& transform = bone.node_->GetWorldTransform();
-            distance = query.ray_.HitDistance(box.Transformed(transform));
-            if (distance >= query.maxDistance_)
-                continue;
-            if (level != RAY_AABB)
-            {
-                // Follow with an OBB test if required
-                Matrix3x4 inverse = transform.Inverse();
-                Ray localRay = query.ray_.Transformed(inverse);
-                distance = localRay.HitDistance(box);
-                if (distance >= query.maxDistance_)
-                    continue;
-            }
-        }
-        else if (bone.collisionMask_ & BONECOLLISION_SPHERE)
-        {
-            boneSphere.center_ = bone.node_->GetWorldPosition();
-            boneSphere.radius_ = bone.radius_;
-            distance = query.ray_.HitDistance(boneSphere);
-            if (distance >= query.maxDistance_)
-                continue;
-        }
-        else
-            continue;
-
-        // If the code reaches here then we have a hit
-        RayQueryResult result;
-        result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
-        result.normal_ = -query.ray_.direction_;
-        result.distance_ = distance;
-        result.drawable_ = this;
-        result.node_ = node_;
-        result.subObject_ = i;
-        results.Push(result);
-    }
-}
-
-void AnimatedModel::Update(const FrameInfo& frame)
-{
-    // If node was invisible last frame, need to decide animation LOD distance here
-    // If headless, retain the current animation distance (should be 0)
-    if (frame.camera_ && abs((int)frame.frameNumber_ - (int)viewFrameNumber_) > 1)
-    {
-        // First check for no update at all when invisible. In that case reset LOD timer to ensure update
-        // next time the model is in view
-        if (!updateInvisible_)
-        {
-            if (animationDirty_)
-            {
-                animationLodTimer_ = -1.0f;
-                forceAnimationUpdate_ = true;
-            }
-            return;
-        }
-        float distance = frame.camera_->GetDistance(node_->GetWorldPosition());
-        // If distance is greater than draw distance, no need to update at all
-        if (drawDistance_ > 0.0f && distance > drawDistance_)
-            return;
-        float scale = GetWorldBoundingBox().Size().DotProduct(DOT_SCALE);
-        animationLodDistance_ = frame.camera_->GetLodDistance(distance, scale, lodBias_);
-    }
-
-    if (animationDirty_ || animationOrderDirty_)
-        UpdateAnimation(frame);
-    else if (boneBoundingBoxDirty_)
-        UpdateBoneBoundingBox();
-}
-
-void AnimatedModel::UpdateBatches(const FrameInfo& frame)
-{
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-    const BoundingBox& worldBoundingBox = GetWorldBoundingBox();
-    distance_ = frame.camera_->GetDistance(worldBoundingBox.Center());
-
-    // Note: per-geometry distances do not take skinning into account. Especially in case of a ragdoll they may be
-    // much off base if the node's own transform is not updated
-    if (batches_.Size() == 1)
-        batches_[0].distance_ = distance_;
-    else
-    {
-        for (unsigned i = 0; i < batches_.Size(); ++i)
-            batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_);
-    }
-
-    // Use a transformed version of the model's bounding box instead of world bounding box for LOD scale
-    // determination so that animation does not change the scale
-    BoundingBox transformedBoundingBox = boundingBox_.Transformed(worldTransform);
-    float scale = transformedBoundingBox.Size().DotProduct(DOT_SCALE);
-    float newLodDistance = frame.camera_->GetLodDistance(distance_, scale, lodBias_);
-
-    // If model is rendered from several views, use the minimum LOD distance for animation LOD
-    if (frame.frameNumber_ != animationLodFrameNumber_)
-    {
-        animationLodDistance_ = newLodDistance;
-        animationLodFrameNumber_ = frame.frameNumber_;
-    }
-    else
-        animationLodDistance_ = Min(animationLodDistance_, newLodDistance);
-
-    if (newLodDistance != lodDistance_)
-    {
-        lodDistance_ = newLodDistance;
-        CalculateLodLevels();
-    }
-}
-
-void AnimatedModel::UpdateGeometry(const FrameInfo& frame)
-{
-    // Late update in case the model came into view and animation was dirtied in the meanwhile
-    if (forceAnimationUpdate_)
-    {
-        UpdateAnimation(frame);
-        forceAnimationUpdate_ = false;
-    }
-
-    if (morphsDirty_)
-        UpdateMorphs();
-
-    if (skinningDirty_)
-        UpdateSkinning();
-}
-
-UpdateGeometryType AnimatedModel::GetUpdateGeometryType()
-{
-    if (morphsDirty_ || forceAnimationUpdate_)
-        return UPDATE_MAIN_THREAD;
-    else if (skinningDirty_)
-        return UPDATE_WORKER_THREAD;
-    else
-        return UPDATE_NONE;
-}
-
-void AnimatedModel::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
-{
-    if (debug && IsEnabledEffective())
-    {
-        debug->AddBoundingBox(GetWorldBoundingBox(), Color::GREEN, depthTest);
-        debug->AddSkeleton(skeleton_, Color(0.75f, 0.75f, 0.75f), depthTest);
-    }
-}
-
-void AnimatedModel::SetModel(Model* model, bool createBones)
-{
-    if (model == model_)
-        return;
-
-    // Unsubscribe from the reload event of previous model (if any), then subscribe to the new
-    if (model_)
-        UnsubscribeFromEvent(model_, E_RELOADFINISHED);
-
-    model_ = model;
-
-    if (model)
-    {
-        SubscribeToEvent(model, E_RELOADFINISHED, ATOMIC_HANDLER(AnimatedModel, HandleModelReloadFinished));
-
-        // Copy the subgeometry & LOD level structure
-        SetNumGeometries(model->GetNumGeometries());
-        const Vector<Vector<SharedPtr<Geometry> > >& geometries = model->GetGeometries();
-        const PODVector<Vector3>& geometryCenters = model->GetGeometryCenters();
-        for (unsigned i = 0; i < geometries.Size(); ++i)
-        {
-            geometries_[i] = geometries[i];
-            geometryData_[i].center_ = geometryCenters[i];
-        }
-
-        // Copy geometry bone mappings
-        const Vector<PODVector<unsigned> >& geometryBoneMappings = model->GetGeometryBoneMappings();
-        geometryBoneMappings_.Clear();
-        geometryBoneMappings_.Reserve(geometryBoneMappings.Size());
-        for (unsigned i = 0; i < geometryBoneMappings.Size(); ++i)
-            geometryBoneMappings_.Push(geometryBoneMappings[i]);
-
-        // Copy morphs. Note: morph vertex buffers will be created later on-demand
-        morphVertexBuffers_.Clear();
-        morphs_.Clear();
-        const Vector<ModelMorph>& morphs = model->GetMorphs();
-        morphs_.Reserve(morphs.Size());
-        morphElementMask_ = 0;
-        for (unsigned i = 0; i < morphs.Size(); ++i)
-        {
-            ModelMorph newMorph;
-            newMorph.name_ = morphs[i].name_;
-            newMorph.nameHash_ = morphs[i].nameHash_;
-            newMorph.weight_ = 0.0f;
-            newMorph.buffers_ = morphs[i].buffers_;
-            for (HashMap<unsigned, VertexBufferMorph>::ConstIterator j = morphs[i].buffers_.Begin();
-                 j != morphs[i].buffers_.End(); ++j)
-                morphElementMask_ |= j->second_.elementMask_;
-            morphs_.Push(newMorph);
-        }
-
-        // Copy bounding box & skeleton
-        SetBoundingBox(model->GetBoundingBox());
-        // Initial bone bounding box is just the one stored in the model
-        boneBoundingBox_ = boundingBox_;
-        boneBoundingBoxDirty_ = true;
-        SetSkeleton(model->GetSkeleton(), createBones);
-        ResetLodLevels();
-
-        // Enable skinning in batches
-        for (unsigned i = 0; i < batches_.Size(); ++i)
-        {
-            if (skinMatrices_.Size())
-            {
-                batches_[i].geometryType_ = GEOM_SKINNED;
-                // Check if model has per-geometry bone mappings
-                if (geometrySkinMatrices_.Size() && geometrySkinMatrices_[i].Size())
-                {
-                    batches_[i].worldTransform_ = &geometrySkinMatrices_[i][0];
-                    batches_[i].numWorldTransforms_ = geometrySkinMatrices_[i].Size();
-                }
-                // If not, use the global skin matrices
-                else
-                {
-                    batches_[i].worldTransform_ = &skinMatrices_[0];
-                    batches_[i].numWorldTransforms_ = skinMatrices_.Size();
-                }
-            }
-            else
-            {
-                batches_[i].geometryType_ = GEOM_STATIC;
-                batches_[i].worldTransform_ = &node_->GetWorldTransform();
-                batches_[i].numWorldTransforms_ = 1;
-            }
-        }
-    }
-    else
-    {
-        RemoveRootBone(); // Remove existing root bone if any
-        SetNumGeometries(0);
-        geometryBoneMappings_.Clear();
-        morphVertexBuffers_.Clear();
-        morphs_.Clear();
-        morphElementMask_ = 0;
-        SetBoundingBox(BoundingBox());
-        SetSkeleton(Skeleton(), false);
-    }
-
-    MarkNetworkUpdate();
-}
-
-AnimationState* AnimatedModel::AddAnimationState(Animation* animation)
-{
-    if (!isMaster_)
-    {
-        ATOMIC_LOGERROR("Can not add animation state to non-master model");
-        return 0;
-    }
-
-    if (!animation || !skeleton_.GetNumBones())
-        return 0;
-
-    // Check for not adding twice
-    AnimationState* existing = GetAnimationState(animation);
-    if (existing)
-        return existing;
-
-    SharedPtr<AnimationState> newState(new AnimationState(this, animation));
-    animationStates_.Push(newState);
-    MarkAnimationOrderDirty();
-    return newState;
-}
-
-void AnimatedModel::RemoveAnimationState(Animation* animation)
-{
-    if (animation)
-        RemoveAnimationState(animation->GetNameHash());
-    else
-    {
-        for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
-        {
-            AnimationState* state = *i;
-            if (!state->GetAnimation())
-            {
-                animationStates_.Erase(i);
-                MarkAnimationDirty();
-                return;
-            }
-        }
-    }
-}
-
-void AnimatedModel::RemoveAnimationState(const String& animationName)
-{
-    RemoveAnimationState(StringHash(animationName));
-}
-
-void AnimatedModel::RemoveAnimationState(StringHash animationNameHash)
-{
-    for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
-    {
-        AnimationState* state = *i;
-        Animation* animation = state->GetAnimation();
-        if (animation)
-        {
-            // Check both the animation and the resource name
-            if (animation->GetNameHash() == animationNameHash || animation->GetAnimationNameHash() == animationNameHash)
-            {
-                animationStates_.Erase(i);
-                MarkAnimationDirty();
-                return;
-            }
-        }
-    }
-}
-
-void AnimatedModel::RemoveAnimationState(AnimationState* state)
-{
-    for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
-    {
-        if (*i == state)
-        {
-            animationStates_.Erase(i);
-            MarkAnimationDirty();
-            return;
-        }
-    }
-}
-
-void AnimatedModel::RemoveAnimationState(unsigned index)
-{
-    if (index < animationStates_.Size())
-    {
-        animationStates_.Erase(index);
-        MarkAnimationDirty();
-    }
-}
-
-void AnimatedModel::RemoveAllAnimationStates()
-{
-    if (animationStates_.Size())
-    {
-        animationStates_.Clear();
-        MarkAnimationDirty();
-    }
-}
-
-void AnimatedModel::SetAnimationLodBias(float bias)
-{
-    animationLodBias_ = Max(bias, 0.0f);
-    MarkNetworkUpdate();
-}
-
-void AnimatedModel::SetUpdateInvisible(bool enable)
-{
-    updateInvisible_ = enable;
-    MarkNetworkUpdate();
-}
-
-
-void AnimatedModel::SetMorphWeight(unsigned index, float weight)
-{
-    if (index >= morphs_.Size())
-        return;
-
-    // If morph vertex buffers have not been created yet, create now
-    if (weight > 0.0f && morphVertexBuffers_.Empty())
-        CloneGeometries();
-
-    weight = Clamp(weight, 0.0f, 1.0f);
-
-    if (weight != morphs_[index].weight_)
-    {
-        morphs_[index].weight_ = weight;
-
-        // For a master model, set the same morph weight on non-master models
-        if (isMaster_)
-        {
-            PODVector<AnimatedModel*> models;
-            GetComponents<AnimatedModel>(models);
-
-            // Indexing might not be the same, so use the name hash instead
-            for (unsigned i = 1; i < models.Size(); ++i)
-            {
-                if (!models[i]->isMaster_)
-                    models[i]->SetMorphWeight(morphs_[index].nameHash_, weight);
-            }
-        }
-
-        MarkMorphsDirty();
-        MarkNetworkUpdate();
-    }
-}
-
-void AnimatedModel::SetMorphWeight(const String& name, float weight)
-{
-    for (unsigned i = 0; i < morphs_.Size(); ++i)
-    {
-        if (morphs_[i].name_ == name)
-        {
-            SetMorphWeight(i, weight);
-            return;
-        }
-    }
-}
-
-void AnimatedModel::SetMorphWeight(StringHash nameHash, float weight)
-{
-    for (unsigned i = 0; i < morphs_.Size(); ++i)
-    {
-        if (morphs_[i].nameHash_ == nameHash)
-        {
-            SetMorphWeight(i, weight);
-            return;
-        }
-    }
-}
-
-void AnimatedModel::ResetMorphWeights()
-{
-    for (Vector<ModelMorph>::Iterator i = morphs_.Begin(); i != morphs_.End(); ++i)
-        i->weight_ = 0.0f;
-
-    // For a master model, reset weights on non-master models
-    if (isMaster_)
-    {
-        PODVector<AnimatedModel*> models;
-        GetComponents<AnimatedModel>(models);
-
-        for (unsigned i = 1; i < models.Size(); ++i)
-        {
-            if (!models[i]->isMaster_)
-                models[i]->ResetMorphWeights();
-        }
-    }
-
-    MarkMorphsDirty();
-    MarkNetworkUpdate();
-}
-
-float AnimatedModel::GetMorphWeight(unsigned index) const
-{
-    return index < morphs_.Size() ? morphs_[index].weight_ : 0.0f;
-}
-
-float AnimatedModel::GetMorphWeight(const String& name) const
-{
-    for (Vector<ModelMorph>::ConstIterator i = morphs_.Begin(); i != morphs_.End(); ++i)
-    {
-        if (i->name_ == name)
-            return i->weight_;
-    }
-
-    return 0.0f;
-}
-
-float AnimatedModel::GetMorphWeight(StringHash nameHash) const
-{
-    for (Vector<ModelMorph>::ConstIterator i = morphs_.Begin(); i != morphs_.End(); ++i)
-    {
-        if (i->nameHash_ == nameHash)
-            return i->weight_;
-    }
-
-    return 0.0f;
-}
-
-AnimationState* AnimatedModel::GetAnimationState(Animation* animation) const
-{
-    for (Vector<SharedPtr<AnimationState> >::ConstIterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
-    {
-        if ((*i)->GetAnimation() == animation)
-            return *i;
-    }
-
-    return 0;
-}
-
-AnimationState* AnimatedModel::GetAnimationState(const String& animationName) const
-{
-    return GetAnimationState(StringHash(animationName));
-}
-
-AnimationState* AnimatedModel::GetAnimationState(StringHash animationNameHash) const
-{
-    for (Vector<SharedPtr<AnimationState> >::ConstIterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
-    {
-        Animation* animation = (*i)->GetAnimation();
-        if (animation)
-        {
-            // Check both the animation and the resource name
-            if (animation->GetNameHash() == animationNameHash || animation->GetAnimationNameHash() == animationNameHash)
-                return *i;
-        }
-    }
-
-    return 0;
-}
-
-AnimationState* AnimatedModel::GetAnimationState(unsigned index) const
-{
-    return index < animationStates_.Size() ? animationStates_[index].Get() : 0;
-}
-
-void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
-{
-    if (!node_ && createBones)
-    {
-        ATOMIC_LOGERROR("AnimatedModel not attached to a scene node, can not create bone nodes");
-        return;
-    }
-
-    if (isMaster_)
-    {
-        // Check if bone structure has stayed compatible (reloading the model.) In that case retain the old bones and animations
-        if (skeleton_.GetNumBones() == skeleton.GetNumBones())
-        {
-            Vector<Bone>& destBones = skeleton_.GetModifiableBones();
-            const Vector<Bone>& srcBones = skeleton.GetBones();
-            bool compatible = true;
-
-            for (unsigned i = 0; i < destBones.Size(); ++i)
-            {
-                if (destBones[i].node_ && destBones[i].name_ == srcBones[i].name_ && destBones[i].parentIndex_ ==
-                                                                                     srcBones[i].parentIndex_)
-                {
-                    // If compatible, just copy the values and retain the old node and animated status
-                    Node* boneNode = destBones[i].node_;
-                    bool animated = destBones[i].animated_;
-                    destBones[i] = srcBones[i];
-                    destBones[i].node_ = boneNode;
-                    destBones[i].animated_ = animated;
-                }
-                else
-                {
-                    compatible = false;
-                    break;
-                }
-            }
-            if (compatible)
-                return;
-        }
-
-        RemoveAllAnimationStates();
-
-        // Detach the rootbone of the previous model if any
-        if (createBones)
-            RemoveRootBone();
-
-        skeleton_.Define(skeleton);
-
-        // Merge bounding boxes from non-master models
-        FinalizeBoneBoundingBoxes();
-
-        Vector<Bone>& bones = skeleton_.GetModifiableBones();
-        // Create scene nodes for the bones
-        if (createBones)
-        {
-            for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i)
-            {
-                // 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_);
-                // Copy the model component's temporary status
-                boneNode->SetTemporary(IsTemporary());
-                i->node_ = boneNode;
-            }
-
-            for (unsigned i = 0; i < bones.Size(); ++i)
-            {
-                unsigned parentIndex = bones[i].parentIndex_;
-                if (parentIndex != i && parentIndex < bones.Size())
-                    bones[parentIndex].node_->AddChild(bones[i].node_);
-            }
-        }
-
-        using namespace BoneHierarchyCreated;
-
-        VariantMap& eventData = GetEventDataMap();
-        eventData[P_NODE] = node_;
-        node_->SendEvent(E_BONEHIERARCHYCREATED, eventData);
-    }
-    else
-    {
-        // For non-master models: use the bone nodes of the master model
-        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)
-        {
-            Vector<Bone>& bones = skeleton_.GetModifiableBones();
-            for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i)
-            {
-                Node* boneNode = node_->GetChild(i->name_, true);
-                if (boneNode)
-                    boneNode->AddListener(this);
-                i->node_ = boneNode;
-            }
-        }
-    }
-
-    // Reserve space for skinning matrices
-    skinMatrices_.Resize(skeleton_.GetNumBones());
-    SetGeometryBoneMappings();
-
-    assignBonesPending_ = !createBones;
-}
-
-void AnimatedModel::SetModelAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    // When loading a scene, set model without creating the bone nodes (will be assigned later during post-load)
-    SetModel(cache->GetResource<Model>(value.name_), !loading_);
-}
-
-void AnimatedModel::SetBonesEnabledAttr(const VariantVector& value)
-{
-    Vector<Bone>& bones = skeleton_.GetModifiableBones();
-    for (unsigned i = 0; i < bones.Size() && i < value.Size(); ++i)
-        bones[i].animated_ = value[i].GetBool();
-}
-
-void AnimatedModel::SetAnimationStatesAttr(const VariantVector& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    RemoveAllAnimationStates();
-    unsigned index = 0;
-    unsigned numStates = index < value.Size() ? value[index++].GetUInt() : 0;
-    // Prevent negative or overly large value being assigned from the editor
-    if (numStates > M_MAX_INT)
-        numStates = 0;
-    if (numStates > MAX_ANIMATION_STATES)
-        numStates = MAX_ANIMATION_STATES;
-
-    animationStates_.Reserve(numStates);
-    while (numStates--)
-    {
-        if (index + 5 < value.Size())
-        {
-            // Note: null animation is allowed here for editing
-            const ResourceRef& animRef = value[index++].GetResourceRef();
-            SharedPtr<AnimationState> newState(new AnimationState(this, cache->GetResource<Animation>(animRef.name_)));
-            animationStates_.Push(newState);
-
-            newState->SetStartBone(skeleton_.GetBone(value[index++].GetString()));
-            newState->SetLooped(value[index++].GetBool());
-            newState->SetWeight(value[index++].GetFloat());
-            newState->SetTime(value[index++].GetFloat());
-            newState->SetLayer((unsigned char)value[index++].GetInt());
-        }
-        else
-        {
-            // If not enough data, just add an empty animation state
-            SharedPtr<AnimationState> newState(new AnimationState(this, 0));
-            animationStates_.Push(newState);
-        }
-    }
-
-    if (animationStates_.Size())
-    {
-        MarkAnimationDirty();
-        MarkAnimationOrderDirty();
-    }
-}
-
-void AnimatedModel::SetMorphsAttr(const PODVector<unsigned char>& value)
-{
-    for (unsigned index = 0; index < value.Size(); ++index)
-        SetMorphWeight(index, (float)value[index] / 255.0f);
-}
-
-ResourceRef AnimatedModel::GetModelAttr() const
-{
-    return GetResourceRef(model_, Model::GetTypeStatic());
-}
-
-VariantVector AnimatedModel::GetBonesEnabledAttr() const
-{
-    VariantVector ret;
-    const Vector<Bone>& bones = skeleton_.GetBones();
-    ret.Reserve(bones.Size());
-    for (Vector<Bone>::ConstIterator i = bones.Begin(); i != bones.End(); ++i)
-        ret.Push(i->animated_);
-    return ret;
-}
-
-VariantVector AnimatedModel::GetAnimationStatesAttr() const
-{
-    VariantVector ret;
-    ret.Reserve(animationStates_.Size() * 6 + 1);
-    ret.Push(animationStates_.Size());
-    for (Vector<SharedPtr<AnimationState> >::ConstIterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
-    {
-        AnimationState* state = *i;
-        Animation* animation = state->GetAnimation();
-        Bone* startBone = state->GetStartBone();
-        ret.Push(GetResourceRef(animation, Animation::GetTypeStatic()));
-        ret.Push(startBone ? startBone->name_ : String::EMPTY);
-        ret.Push(state->IsLooped());
-        ret.Push(state->GetWeight());
-        ret.Push(state->GetTime());
-        ret.Push((int)state->GetLayer());
-    }
-    return ret;
-}
-
-const PODVector<unsigned char>& AnimatedModel::GetMorphsAttr() const
-{
-    attrBuffer_.Clear();
-    for (Vector<ModelMorph>::ConstIterator i = morphs_.Begin(); i != morphs_.End(); ++i)
-        attrBuffer_.WriteUByte((unsigned char)(i->weight_ * 255.0f));
-
-    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)
-{
-    Drawable::OnNodeSet(node);
-
-    if (node)
-    {
-        // If this AnimatedModel is the first in the node, it is the master which controls animation & morphs
-        isMaster_ = GetComponent<AnimatedModel>() == this;
-    }
-}
-
-void AnimatedModel::OnMarkedDirty(Node* node)
-{
-    Drawable::OnMarkedDirty(node);
-
-    // If the scene node or any of the bone nodes move, mark skinning dirty
-    if (skeleton_.GetNumBones())
-    {
-        skinningDirty_ = true;
-        // Bone bounding box doesn't need to be marked dirty when only the base scene node moves
-        if (node != node_)
-            boneBoundingBoxDirty_ = true;
-    }
-}
-
-void AnimatedModel::OnWorldBoundingBoxUpdate()
-{
-    if (isMaster_)
-    {
-        // Note: do not update bone bounding box here, instead do it in either of the threaded updates
-        worldBoundingBox_ = boneBoundingBox_.Transformed(node_->GetWorldTransform());
-    }
-    else
-    {
-        // Non-master animated models get the bounding box from the master
-        /// \todo If it's a skinned attachment that does not cover the whole body, it will have unnecessarily large bounds
-        AnimatedModel* master = node_->GetComponent<AnimatedModel>();
-        // Check if we've become the new master model in case the original was deleted
-        if (master == this)
-            isMaster_ = true;
-        if (master)
-            worldBoundingBox_ = master->GetWorldBoundingBox();
-    }
-}
-
-void AnimatedModel::AssignBoneNodes()
-{
-    assignBonesPending_ = false;
-
-    if (!node_)
-        return;
-
-    // Find the bone nodes from the node hierarchy and add listeners
-    Vector<Bone>& bones = skeleton_.GetModifiableBones();
-    bool boneFound = false;
-    for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i)
-    {
-        Node* boneNode = node_->GetChild(i->name_, true);
-        if (boneNode)
-        {
-            boneFound = true;
-            boneNode->AddListener(this);
-        }
-        i->node_ = boneNode;
-    }
-
-    // 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
-    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)
-    {
-        AnimationState* state = *i;
-        state->SetStartBone(state->GetStartBone());
-    }
-}
-
-void AnimatedModel::FinalizeBoneBoundingBoxes()
-{
-    Vector<Bone>& bones = skeleton_.GetModifiableBones();
-    PODVector<AnimatedModel*> models;
-    GetComponents<AnimatedModel>(models);
-
-    if (models.Size() > 1)
-    {
-        // Reset first to the model resource's original bone bounding information if available (should be)
-        if (model_)
-        {
-            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_;
-            }
-        }
-
-        // 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)
-        {
-            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_);
-                    }
-                }
-            }
-        }
-    }
-
-    // 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)
-    {
-        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;
-    }
-}
-
-void AnimatedModel::RemoveRootBone()
-{
-    Bone* rootBone = skeleton_.GetRootBone();
-    if (rootBone && rootBone->node_)
-        rootBone->node_->Remove();
-}
-
-void AnimatedModel::MarkAnimationDirty()
-{
-    if (isMaster_)
-    {
-        animationDirty_ = true;
-        MarkForUpdate();
-    }
-}
-
-void AnimatedModel::MarkAnimationOrderDirty()
-{
-    if (isMaster_)
-    {
-        animationOrderDirty_ = true;
-        MarkForUpdate();
-    }
-}
-
-void AnimatedModel::MarkMorphsDirty()
-{
-    morphsDirty_ = true;
-}
-
-void AnimatedModel::CloneGeometries()
-{
-    const Vector<SharedPtr<VertexBuffer> >& originalVertexBuffers = model_->GetVertexBuffers();
-    HashMap<VertexBuffer*, SharedPtr<VertexBuffer> > clonedVertexBuffers;
-    morphVertexBuffers_.Resize(originalVertexBuffers.Size());
-
-    for (unsigned i = 0; i < originalVertexBuffers.Size(); ++i)
-    {
-        VertexBuffer* original = originalVertexBuffers[i];
-        if (model_->GetMorphRangeCount(i))
-        {
-            SharedPtr<VertexBuffer> clone(new VertexBuffer(context_));
-            clone->SetShadowed(true);
-            clone->SetSize(original->GetVertexCount(), morphElementMask_ & original->GetElementMask(), true);
-            void* dest = clone->Lock(0, original->GetVertexCount());
-            if (dest)
-            {
-                CopyMorphVertices(dest, original->GetShadowData(), original->GetVertexCount(), clone, original);
-                clone->Unlock();
-            }
-            clonedVertexBuffers[original] = clone;
-            morphVertexBuffers_[i] = clone;
-        }
-        else
-            morphVertexBuffers_[i].Reset();
-    }
-
-    // Geometries will always be cloned fully. They contain only references to buffer, so they are relatively light
-    for (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        for (unsigned j = 0; j < geometries_[i].Size(); ++j)
-        {
-            SharedPtr<Geometry> original = geometries_[i][j];
-            SharedPtr<Geometry> clone(new Geometry(context_));
-
-            // Add an additional vertex stream into the clone, which supplies only the morphable vertex data, while the static
-            // data comes from the original vertex buffer(s)
-            const Vector<SharedPtr<VertexBuffer> >& originalBuffers = original->GetVertexBuffers();
-            unsigned totalBuf = originalBuffers.Size();
-            for (unsigned k = 0; k < originalBuffers.Size(); ++k)
-            {
-                VertexBuffer* originalBuffer = originalBuffers[k];
-                if (clonedVertexBuffers.Contains(originalBuffer))
-                    ++totalBuf;
-            }
-            clone->SetNumVertexBuffers(totalBuf);
-
-            unsigned l = 0;
-            for (unsigned k = 0; k < originalBuffers.Size(); ++k)
-            {
-                VertexBuffer* originalBuffer = originalBuffers[k];
-
-                if (clonedVertexBuffers.Contains(originalBuffer))
-                {
-                    VertexBuffer* clonedBuffer = clonedVertexBuffers[originalBuffer];
-                    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
-                    clone->SetVertexBuffer(l++, originalBuffer);
-            }
-
-            clone->SetIndexBuffer(original->GetIndexBuffer());
-            clone->SetDrawRange(original->GetPrimitiveType(), original->GetIndexStart(), original->GetIndexCount());
-            clone->SetLodDistance(original->GetLodDistance());
-
-            geometries_[i][j] = clone;
-        }
-    }
-
-    // Make sure the rendering batches use the new cloned geometries
-    ResetLodLevels();
-    MarkMorphsDirty();
-}
-
-void AnimatedModel::CopyMorphVertices(void* destVertexData, void* srcVertexData, unsigned vertexCount, VertexBuffer* destBuffer,
-    VertexBuffer* srcBuffer)
-{
-    unsigned mask = destBuffer->GetElementMask() & srcBuffer->GetElementMask();
-    unsigned normalOffset = srcBuffer->GetElementOffset(SEM_NORMAL);
-    unsigned tangentOffset = srcBuffer->GetElementOffset(SEM_TANGENT);
-    unsigned vertexSize = srcBuffer->GetVertexSize();
-    float* dest = (float*)destVertexData;
-    unsigned char* src = (unsigned char*)srcVertexData;
-
-    while (vertexCount--)
-    {
-        if (mask & MASK_POSITION)
-        {
-            float* posSrc = (float*)src;
-            dest[0] = posSrc[0];
-            dest[1] = posSrc[1];
-            dest[2] = posSrc[2];
-            dest += 3;
-        }
-        if (mask & MASK_NORMAL)
-        {
-            float* normalSrc = (float*)(src + normalOffset);
-            dest[0] = normalSrc[0];
-            dest[1] = normalSrc[1];
-            dest[2] = normalSrc[2];
-            dest += 3;
-        }
-        if (mask & MASK_TANGENT)
-        {
-            float* tangentSrc = (float*)(src + tangentOffset);
-            dest[0] = tangentSrc[0];
-            dest[1] = tangentSrc[1];
-            dest[2] = tangentSrc[2];
-            dest[3] = tangentSrc[3];
-            dest += 4;
-        }
-
-        src += vertexSize;
-    }
-}
-
-void AnimatedModel::SetGeometryBoneMappings()
-{
-    geometrySkinMatrices_.Clear();
-    geometrySkinMatrixPtrs_.Clear();
-
-    if (!geometryBoneMappings_.Size())
-        return;
-
-    // Check if all mappings are empty, then we do not need to use mapped skinning
-    bool allEmpty = true;
-    for (unsigned i = 0; i < geometryBoneMappings_.Size(); ++i)
-        if (geometryBoneMappings_[i].Size())
-            allEmpty = false;
-
-    if (allEmpty)
-        return;
-
-    // Reserve space for per-geometry skinning matrices
-    geometrySkinMatrices_.Resize(geometryBoneMappings_.Size());
-    for (unsigned i = 0; i < geometryBoneMappings_.Size(); ++i)
-        geometrySkinMatrices_[i].Resize(geometryBoneMappings_[i].Size());
-
-    // Build original-to-skinindex matrix pointer mapping for fast copying
-    // Note: at this point layout of geometrySkinMatrices_ cannot be modified or pointers become invalid
-    geometrySkinMatrixPtrs_.Resize(skeleton_.GetNumBones());
-    for (unsigned i = 0; i < geometryBoneMappings_.Size(); ++i)
-    {
-        for (unsigned j = 0; j < geometryBoneMappings_[i].Size(); ++j)
-            geometrySkinMatrixPtrs_[geometryBoneMappings_[i][j]].Push(&geometrySkinMatrices_[i][j]);
-    }
-}
-
-void AnimatedModel::UpdateAnimation(const FrameInfo& frame)
-{
-    // If using animation LOD, accumulate time and see if it is time to update
-    if (animationLodBias_ > 0.0f && animationLodDistance_ > 0.0f)
-    {
-        // Perform the first update always regardless of LOD timer
-        if (animationLodTimer_ >= 0.0f)
-        {
-            animationLodTimer_ += animationLodBias_ * frame.timeStep_ * ANIMATION_LOD_BASESCALE;
-            if (animationLodTimer_ >= animationLodDistance_)
-                animationLodTimer_ = fmodf(animationLodTimer_, animationLodDistance_);
-            else
-                return;
-        }
-        else
-            animationLodTimer_ = 0.0f;
-    }
-
-    // Make sure animations are in ascending priority order
-    if (animationOrderDirty_)
-    {
-        Sort(animationStates_.Begin(), animationStates_.End(), CompareAnimationOrder);
-        animationOrderDirty_ = false;
-    }
-
-    // Reset skeleton, apply all animations, calculate bones' bounding box. Make sure this is only done for the master model
-    // (first AnimatedModel in a node)
-    if (isMaster_)
-    {
-        skeleton_.ResetSilent();
-        for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
-            (*i)->Apply();
-
-        // Skeleton reset and animations apply the node transforms "silently" to avoid repeated marking dirty. Mark dirty now
-        node_->MarkDirty();
-
-        // Calculate new bone bounding box
-        UpdateBoneBoundingBox();
-    }
-
-    animationDirty_ = false;
-}
-
-void AnimatedModel::UpdateSkinning()
-{
-    // Note: the model's world transform will be baked in the skin matrices
-    const Vector<Bone>& bones = skeleton_.GetBones();
-    // Use model's world transform in case a bone is missing
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-
-    // Skinning with global matrices only
-    if (!geometrySkinMatrices_.Size())
-    {
-        for (unsigned i = 0; i < bones.Size(); ++i)
-        {
-            const Bone& bone = bones[i];
-            if (bone.node_)
-                skinMatrices_[i] = bone.node_->GetWorldTransform() * bone.offsetMatrix_;
-            else
-                skinMatrices_[i] = worldTransform;
-        }
-    }
-    // Skinning with per-geometry matrices
-    else
-    {
-        for (unsigned i = 0; i < bones.Size(); ++i)
-        {
-            const Bone& bone = bones[i];
-            if (bone.node_)
-                skinMatrices_[i] = bone.node_->GetWorldTransform() * bone.offsetMatrix_;
-            else
-                skinMatrices_[i] = worldTransform;
-
-            // Copy the skin matrix to per-geometry matrices as needed
-            for (unsigned j = 0; j < geometrySkinMatrixPtrs_[i].Size(); ++j)
-                *geometrySkinMatrixPtrs_[i][j] = skinMatrices_[i];
-        }
-    }
-
-    skinningDirty_ = false;
-}
-
-void AnimatedModel::UpdateMorphs()
-{
-    Graphics* graphics = GetSubsystem<Graphics>();
-    if (!graphics)
-        return;
-
-    if (morphs_.Size())
-    {
-        // Reset the morph data range from all morphable vertex buffers, then apply morphs
-        for (unsigned i = 0; i < morphVertexBuffers_.Size(); ++i)
-        {
-            VertexBuffer* buffer = morphVertexBuffers_[i];
-            if (buffer)
-            {
-                VertexBuffer* originalBuffer = model_->GetVertexBuffers()[i];
-                unsigned morphStart = model_->GetMorphRangeStart(i);
-                unsigned morphCount = model_->GetMorphRangeCount(i);
-
-                void* dest = buffer->Lock(morphStart, morphCount);
-                if (dest)
-                {
-                    // Reset morph range by copying data from the original vertex buffer
-                    CopyMorphVertices(dest, originalBuffer->GetShadowData() + morphStart * originalBuffer->GetVertexSize(),
-                        morphCount, buffer, originalBuffer);
-
-                    for (unsigned j = 0; j < morphs_.Size(); ++j)
-                    {
-                        if (morphs_[j].weight_ > 0.0f)
-                        {
-                            HashMap<unsigned, VertexBufferMorph>::Iterator k = morphs_[j].buffers_.Find(i);
-                            if (k != morphs_[j].buffers_.End())
-                                ApplyMorph(buffer, dest, morphStart, k->second_, morphs_[j].weight_);
-                        }
-                    }
-
-                    buffer->Unlock();
-                }
-            }
-        }
-    }
-
-    morphsDirty_ = false;
-}
-
-void AnimatedModel::ApplyMorph(VertexBuffer* buffer, void* destVertexData, unsigned morphRangeStart, const VertexBufferMorph& morph,
-    float weight)
-{
-    unsigned elementMask = morph.elementMask_ & buffer->GetElementMask();
-    unsigned vertexCount = morph.vertexCount_;
-    unsigned normalOffset = buffer->GetElementOffset(SEM_NORMAL);
-    unsigned tangentOffset = buffer->GetElementOffset(SEM_TANGENT);
-    unsigned vertexSize = buffer->GetVertexSize();
-
-    unsigned char* srcData = morph.morphData_;
-    unsigned char* destData = (unsigned char*)destVertexData;
-
-    while (vertexCount--)
-    {
-        unsigned vertexIndex = *((unsigned*)srcData) - morphRangeStart;
-        srcData += sizeof(unsigned);
-
-        if (elementMask & MASK_POSITION)
-        {
-            float* dest = (float*)(destData + vertexIndex * vertexSize);
-            float* src = (float*)srcData;
-            dest[0] += src[0] * weight;
-            dest[1] += src[1] * weight;
-            dest[2] += src[2] * weight;
-            srcData += 3 * sizeof(float);
-        }
-        if (elementMask & MASK_NORMAL)
-        {
-            float* dest = (float*)(destData + vertexIndex * vertexSize + normalOffset);
-            float* src = (float*)srcData;
-            dest[0] += src[0] * weight;
-            dest[1] += src[1] * weight;
-            dest[2] += src[2] * weight;
-            srcData += 3 * sizeof(float);
-        }
-        if (elementMask & MASK_TANGENT)
-        {
-            float* dest = (float*)(destData + vertexIndex * vertexSize + tangentOffset);
-            float* src = (float*)srcData;
-            dest[0] += src[0] * weight;
-            dest[1] += src[1] * weight;
-            dest[2] += src[2] * weight;
-            srcData += 3 * sizeof(float);
-        }
-    }
-}
-
-void AnimatedModel::HandleModelReloadFinished(StringHash eventType, VariantMap& eventData)
-{
-    Model* currentModel = model_;
-    model_.Reset(); // Set null to allow to be re-set
-    SetModel(currentModel);
-}
-
-}

+ 0 - 262
Source/Atomic/Atomic3D/AnimatedModel.h

@@ -1,262 +0,0 @@
-//
-// 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 "../Graphics/Model.h"
-#include "../Graphics/Skeleton.h"
-#include "../Graphics/StaticModel.h"
-
-namespace Atomic
-{
-
-class Animation;
-class AnimationState;
-
-/// Animated model component.
-class ATOMIC_API AnimatedModel : public StaticModel
-{
-    ATOMIC_OBJECT(AnimatedModel, StaticModel);
-
-    friend class AnimationState;
-
-public:
-    /// Construct.
-    AnimatedModel(Context* context);
-    /// Destruct.
-    virtual ~AnimatedModel();
-    /// Register object factory. Drawable must be registered first.
-    static void RegisterObject(Context* context);
-
-    /// Load from binary data. Return true if successful.
-    virtual bool Load(Deserializer& source, bool setInstanceDefault = false);
-    /// Load from XML data. Return true if successful.
-    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.
-    virtual void ApplyAttributes();
-    /// Process octree raycast. May be called from a worker thread.
-    virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
-    /// Update before octree reinsertion. Is called from a worker thread.
-    virtual void Update(const FrameInfo& frame);
-    /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
-    virtual void UpdateBatches(const FrameInfo& frame);
-    /// Prepare geometry for rendering. Called from a worker thread if possible (no GPU update.)
-    virtual void UpdateGeometry(const FrameInfo& frame);
-    /// Return whether a geometry update is necessary, and if it can happen in a worker thread.
-    virtual UpdateGeometryType GetUpdateGeometryType();
-    /// Visualize the component as debug geometry.
-    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
-
-    /// Set model.
-    void SetModel(Model* model, bool createBones = true);
-    /// Add an animation.
-    AnimationState* AddAnimationState(Animation* animation);
-    /// Remove an animation by animation pointer.
-    void RemoveAnimationState(Animation* animation);
-    /// Remove an animation by animation name.
-    void RemoveAnimationState(const String& animationName);
-    /// Remove an animation by animation name hash.
-    void RemoveAnimationState(StringHash animationNameHash);
-    /// Remove an animation by AnimationState pointer.
-    void RemoveAnimationState(AnimationState* state);
-    /// Remove an animation by index.
-    void RemoveAnimationState(unsigned index);
-    /// Remove all animations.
-    void RemoveAllAnimationStates();
-    /// Set animation LOD bias.
-    void SetAnimationLodBias(float bias);
-    /// Set whether to update animation and the bounding box when not visible. Recommended to enable for physically controlled models like ragdolls.
-    void SetUpdateInvisible(bool enable);
-    /// Set vertex morph weight by index.
-    void SetMorphWeight(unsigned index, float weight);
-    /// Set vertex morph weight by name.
-    void SetMorphWeight(const String& name, float weight);
-    /// Set vertex morph weight by name hash.
-    void SetMorphWeight(StringHash nameHash, float weight);
-    /// Reset all vertex morphs to zero.
-    void ResetMorphWeights();
-
-    /// Return skeleton.
-    Skeleton& GetSkeleton() { return skeleton_; }
-
-    /// Return all animation states.
-    const Vector<SharedPtr<AnimationState> >& GetAnimationStates() const { return animationStates_; }
-
-    /// Return number of animation states.
-    unsigned GetNumAnimationStates() const { return animationStates_.Size(); }
-
-    /// Return animation state by animation pointer.
-    AnimationState* GetAnimationState(Animation* animation) const;
-    /// Return animation state by animation name.
-    AnimationState* GetAnimationState(const String& animationName) const;
-    /// Return animation state by animation name hash.
-    AnimationState* GetAnimationState(const StringHash animationNameHash) const;
-    /// Return animation state by index.
-    AnimationState* GetAnimationState(unsigned index) const;
-
-    /// Return animation LOD bias.
-    float GetAnimationLodBias() const { return animationLodBias_; }
-
-    /// Return whether to update animation when not visible.
-    bool GetUpdateInvisible() const { return updateInvisible_; }
-
-    /// Return all vertex morphs.
-    const Vector<ModelMorph>& GetMorphs() const { return morphs_; }
-
-    /// Return all morph vertex buffers.
-    const Vector<SharedPtr<VertexBuffer> >& GetMorphVertexBuffers() const { return morphVertexBuffers_; }
-
-    /// Return number of vertex morphs.
-    unsigned GetNumMorphs() const { return morphs_.Size(); }
-
-    /// Return vertex morph weight by index.
-    float GetMorphWeight(unsigned index) const;
-    /// Return vertex morph weight by name.
-    float GetMorphWeight(const String& name) const;
-    /// Return vertex morph weight by name hash.
-    float GetMorphWeight(StringHash nameHash) const;
-
-    /// Return whether is the master (first) animated model.
-    bool IsMaster() const { return isMaster_; }
-
-    /// Set model attribute.
-    void SetModelAttr(const ResourceRef& value);
-    /// Set bones' animation enabled attribute.
-    void SetBonesEnabledAttr(const VariantVector& value);
-    /// Set animation states attribute.
-    void SetAnimationStatesAttr(const VariantVector& value);
-    /// Set morphs attribute.
-    void SetMorphsAttr(const PODVector<unsigned char>& value);
-    /// Return model attribute.
-    ResourceRef GetModelAttr() const;
-    /// Return bones' animation enabled attribute.
-    VariantVector GetBonesEnabledAttr() const;
-    /// Return animation states attribute.
-    VariantVector GetAnimationStatesAttr() const;
-    /// Return morphs attribute.
-    const PODVector<unsigned char>& GetMorphsAttr() const;
-
-    /// Return per-geometry bone mappings.
-    const Vector<PODVector<unsigned> >& GetGeometryBoneMappings() const { return geometryBoneMappings_; }
-
-    /// Return per-geometry skin matrices. If empty, uses global skinning
-    const Vector<PODVector<Matrix3x4> >& GetGeometrySkinMatrices() const { return geometrySkinMatrices_; }
-
-    /// 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:
-    /// Handle node being assigned.
-    virtual void OnNodeSet(Node* node);
-    /// Handle node transform being dirtied.
-    virtual void OnMarkedDirty(Node* node);
-    /// Recalculate the world-space bounding box.
-    virtual void OnWorldBoundingBoxUpdate();
-
-private:
-    /// Assign skeleton and animation bone node references as a postprocess. Called by ApplyAttributes.
-    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.
-    void RemoveRootBone();
-    /// Mark animation and skinning to require an update.
-    void MarkAnimationDirty();
-    /// Mark animation and skinning to require a forced update (blending order changed.)
-    void MarkAnimationOrderDirty();
-    /// Mark morphs to require an update.
-    void MarkMorphsDirty();
-    /// Set skeleton.
-    void SetSkeleton(const Skeleton& skeleton, bool createBones);
-    /// Set mapping of subgeometry bone indices.
-    void SetGeometryBoneMappings();
-    /// Clone geometries for vertex morphing.
-    void CloneGeometries();
-    /// Copy morph vertices.
-    void CopyMorphVertices(void* dest, void* src, unsigned vertexCount, VertexBuffer* clone, VertexBuffer* original);
-    /// Recalculate animations. Called from Update().
-    void UpdateAnimation(const FrameInfo& frame);
-    /// Recalculate skinning.
-    void UpdateSkinning();
-    /// Reapply all vertex morphs.
-    void UpdateMorphs();
-    /// Apply a vertex morph.
-    void ApplyMorph
-        (VertexBuffer* buffer, void* destVertexData, unsigned morphRangeStart, const VertexBufferMorph& morph, float weight);
-    /// Handle model reload finished.
-    void HandleModelReloadFinished(StringHash eventType, VariantMap& eventData);
-
-    /// Skeleton.
-    Skeleton skeleton_;
-    /// Morph vertex buffers.
-    Vector<SharedPtr<VertexBuffer> > morphVertexBuffers_;
-    /// Vertex morphs.
-    Vector<ModelMorph> morphs_;
-    /// Animation states.
-    Vector<SharedPtr<AnimationState> > animationStates_;
-    /// Skinning matrices.
-    PODVector<Matrix3x4> skinMatrices_;
-    /// Mapping of subgeometry bone indices, used if more bones than skinning shader can manage.
-    Vector<PODVector<unsigned> > geometryBoneMappings_;
-    /// Subgeometry skinning matrices, used if more bones than skinning shader can manage.
-    Vector<PODVector<Matrix3x4> > geometrySkinMatrices_;
-    /// Subgeometry skinning matrix pointers, if more bones than skinning shader can manage.
-    Vector<PODVector<Matrix3x4*> > geometrySkinMatrixPtrs_;
-    /// Bounding box calculated from bones.
-    BoundingBox boneBoundingBox_;
-    /// Attribute buffer.
-    mutable VectorBuffer attrBuffer_;
-    /// The frame number animation LOD distance was last calculated on.
-    unsigned animationLodFrameNumber_;
-    /// Morph vertex element mask.
-    unsigned morphElementMask_;
-    /// Animation LOD bias.
-    float animationLodBias_;
-    /// Animation LOD timer.
-    float animationLodTimer_;
-    /// Animation LOD distance, the minimum of all LOD view distances last frame.
-    float animationLodDistance_;
-    /// Update animation when invisible flag.
-    bool updateInvisible_;
-    /// Animation dirty flag.
-    bool animationDirty_;
-    /// Animation order dirty flag.
-    bool animationOrderDirty_;
-    /// Vertex morphs dirty flag.
-    bool morphsDirty_;
-    /// Skinning dirty flag.
-    bool skinningDirty_;
-    /// Bone bounding box dirty flag.
-    bool boneBoundingBoxDirty_;
-    /// Master model flag.
-    bool isMaster_;
-    /// Loading flag. During loading bone nodes are not created, as they will be serialized as child nodes.
-    bool loading_;
-    /// Bone nodes assignment pending flag.
-    bool assignBonesPending_;
-    /// Force animation update after becoming visible flag.
-    bool forceAnimationUpdate_;
-};
-
-}

+ 0 - 398
Source/Atomic/Atomic3D/Animation.cpp

@@ -1,398 +0,0 @@
-//
-// 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 "../Container/Sort.h"
-#include "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/Animation.h"
-#include "../IO/Deserializer.h"
-#include "../IO/FileSystem.h"
-#include "../IO/Log.h"
-#include "../IO/Serializer.h"
-#include "../Resource/ResourceCache.h"
-#include "../Resource/XMLFile.h"
-#include "../Resource/JSONFile.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-inline bool CompareTriggers(AnimationTriggerPoint& lhs, AnimationTriggerPoint& rhs)
-{
-    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;
-        Atomic::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)
-        Atomic::Sort(keyFrames_.Begin(), keyFrames_.End(), CompareKeyFrames);
-}
-
-void AnimationTrack::InsertKeyFrame(unsigned index, const AnimationKeyFrame& keyFrame)
-{
-    keyFrames_.Insert(index, keyFrame);
-    Atomic::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
-{
-    if (time < 0.0f)
-        time = 0.0f;
-
-    if (index >= keyFrames_.Size())
-        index = keyFrames_.Size() - 1;
-
-    // Check for being too far ahead
-    while (index && time < keyFrames_[index].time_)
-        --index;
-
-    // Check for being too far behind
-    while (index < keyFrames_.Size() - 1 && time >= keyFrames_[index + 1].time_)
-        ++index;
-}
-
-Animation::Animation(Context* context) :
-    Resource(context),
-    length_(0.f)
-{
-}
-
-Animation::~Animation()
-{
-}
-
-void Animation::RegisterObject(Context* context)
-{
-    context->RegisterFactory<Animation>();
-}
-
-bool Animation::BeginLoad(Deserializer& source)
-{
-    unsigned memoryUse = sizeof(Animation);
-
-    // Check ID
-    if (source.ReadFileID() != "UANI")
-    {
-        ATOMIC_LOGERROR(source.GetName() + " is not a valid animation file");
-        return false;
-    }
-
-    // Read name and length
-    animationName_ = source.ReadString();
-    animationNameHash_ = animationName_;
-    length_ = source.ReadFloat();
-    tracks_.Clear();
-
-    unsigned tracks = source.ReadUInt();
-    memoryUse += tracks * sizeof(AnimationTrack);
-
-    // Read tracks
-    for (unsigned i = 0; i < tracks; ++i)
-    {
-        AnimationTrack* newTrack = CreateTrack(source.ReadString());
-        newTrack->channelMask_ = source.ReadUByte();
-
-        unsigned keyFrames = source.ReadUInt();
-        newTrack->keyFrames_.Resize(keyFrames);
-        memoryUse += keyFrames * sizeof(AnimationKeyFrame);
-
-        // Read keyframes of the track
-        for (unsigned j = 0; j < keyFrames; ++j)
-        {
-            AnimationKeyFrame& newKeyFrame = newTrack->keyFrames_[j];
-            newKeyFrame.time_ = source.ReadFloat();
-            if (newTrack->channelMask_ & CHANNEL_POSITION)
-                newKeyFrame.position_ = source.ReadVector3();
-            if (newTrack->channelMask_ & CHANNEL_ROTATION)
-                newKeyFrame.rotation_ = source.ReadQuaternion();
-            if (newTrack->channelMask_ & CHANNEL_SCALE)
-                newKeyFrame.scale_ = source.ReadVector3();
-        }
-    }
-
-    // Optionally read triggers from an XML file
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    String xmlName = ReplaceExtension(GetName(), ".xml");
-
-    SharedPtr<XMLFile> file(cache->GetTempResource<XMLFile>(xmlName, false));
-    if (file)
-    {
-        XMLElement rootElem = file->GetRoot();
-        XMLElement triggerElem = rootElem.GetChild("trigger");
-        while (triggerElem)
-        {
-            if (triggerElem.HasAttribute("normalizedtime"))
-                AddTrigger(triggerElem.GetFloat("normalizedtime"), true, triggerElem.GetVariant());
-            else if (triggerElem.HasAttribute("time"))
-                AddTrigger(triggerElem.GetFloat("time"), false, triggerElem.GetVariant());
-
-            triggerElem = triggerElem.GetNext("trigger");
-        }
-
-        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);
-    return true;
-}
-
-bool Animation::Save(Serializer& dest) const
-{
-    // Write ID, name and length
-    dest.WriteFileID("UANI");
-    dest.WriteString(animationName_);
-    dest.WriteFloat(length_);
-
-    // Write tracks
-    dest.WriteUInt(tracks_.Size());
-    for (HashMap<StringHash, AnimationTrack>::ConstIterator i = tracks_.Begin(); i != tracks_.End(); ++i)
-    {
-        const AnimationTrack& track = i->second_;
-        dest.WriteString(track.name_);
-        dest.WriteUByte(track.channelMask_);
-        dest.WriteUInt(track.keyFrames_.Size());
-
-        // Write keyframes of the track
-        for (unsigned j = 0; j < track.keyFrames_.Size(); ++j)
-        {
-            const AnimationKeyFrame& keyFrame = track.keyFrames_[j];
-            dest.WriteFloat(keyFrame.time_);
-            if (track.channelMask_ & CHANNEL_POSITION)
-                dest.WriteVector3(keyFrame.position_);
-            if (track.channelMask_ & CHANNEL_ROTATION)
-                dest.WriteQuaternion(keyFrame.rotation_);
-            if (track.channelMask_ & CHANNEL_SCALE)
-                dest.WriteVector3(keyFrame.scale_);
-        }
-    }
-
-    // If triggers have been defined, write an XML file for them
-    if (triggers_.Size())
-    {
-        File* destFile = dynamic_cast<File*>(&dest);
-        if (destFile)
-        {
-            String xmlName = ReplaceExtension(destFile->GetName(), ".xml");
-
-            SharedPtr<XMLFile> xml(new XMLFile(context_));
-            XMLElement rootElem = xml->CreateRoot("animation");
-
-            for (unsigned i = 0; i < triggers_.Size(); ++i)
-            {
-                XMLElement triggerElem = rootElem.CreateChild("trigger");
-                triggerElem.SetFloat("time", triggers_[i].time_);
-                triggerElem.SetVariant(triggers_[i].data_);
-            }
-
-            File xmlFile(context_, xmlName, FILE_WRITE);
-            xml->Save(xmlFile);
-        }
-        else
-            ATOMIC_LOGWARNING("Can not save animation trigger data when not saving into a file");
-    }
-
-    return true;
-}
-
-void Animation::SetAnimationName(const String& name)
-{
-    animationName_ = name;
-    animationNameHash_ = StringHash(name);
-}
-
-void Animation::SetLength(float length)
-{
-    length_ = Max(length, 0.0f);
-}
-
-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)
-{
-    triggers_.Push(trigger);
-    Sort(triggers_.Begin(), triggers_.End(), CompareTriggers);
-}
-
-void Animation::AddTrigger(float time, bool timeIsNormalized, const Variant& data)
-{
-    AnimationTriggerPoint newTrigger;
-    newTrigger.time_ = timeIsNormalized ? time * length_ : time;
-    newTrigger.data_ = data;
-    triggers_.Push(newTrigger);
-
-    Sort(triggers_.Begin(), triggers_.End(), CompareTriggers);
-}
-
-void Animation::RemoveTrigger(unsigned index)
-{
-    if (index < triggers_.Size())
-        triggers_.Erase(index);
-}
-
-void Animation::RemoveAllTriggers()
-{
-    triggers_.Clear();
-}
-
-void Animation::SetNumTriggers(unsigned num)
-{
-    triggers_.Resize(num);
-}
-
-SharedPtr<Animation> Animation::Clone(const String& cloneName) const
-{
-    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;
-}
-
-AnimationTrack* Animation::GetTrack(const String& name)
-{
-    HashMap<StringHash, AnimationTrack>::Iterator i = tracks_.Find(StringHash(name));
-    return i != tracks_.End() ? &i->second_ : (AnimationTrack*)0;
-}
-
-AnimationTrack* Animation::GetTrack(StringHash nameHash)
-{
-    HashMap<StringHash, AnimationTrack>::Iterator i = tracks_.Find(nameHash);
-    return i != tracks_.End() ? &i->second_ : (AnimationTrack*)0;
-}
-
-AnimationTriggerPoint* Animation::GetTrigger(unsigned index)
-{
-    return index < triggers_.Size() ? &triggers_[index] : (AnimationTriggerPoint*)0;
-}
-
-}

+ 0 - 194
Source/Atomic/Atomic3D/Animation.h

@@ -1,194 +0,0 @@
-//
-// 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 "../Container/Ptr.h"
-#include "../Math/Quaternion.h"
-#include "../Math/Vector3.h"
-#include "../Resource/Resource.h"
-
-namespace Atomic
-{
-
-/// Skeletal animation keyframe.
-struct AnimationKeyFrame
-{
-    /// Construct.
-    AnimationKeyFrame() :
-        time_(0.0f),
-        scale_(Vector3::ONE)
-    {
-    }
-
-    /// Keyframe time.
-    float time_;
-    /// Bone position.
-    Vector3 position_;
-    /// Bone rotation.
-    Quaternion rotation_;
-    /// Bone scale.
-    Vector3 scale_;
-};
-
-/// Skeletal animation track, stores keyframes of a single bone.
-struct ATOMIC_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.
-    void GetKeyFrameIndex(float time, unsigned& index) const;
-
-    /// Bone or scene node name.
-    String name_;
-    /// Name hash.
-    StringHash nameHash_;
-    /// Bitmask of included data (position, rotation, scale.)
-    unsigned char channelMask_;
-    /// Keyframes.
-    Vector<AnimationKeyFrame> keyFrames_;
-};
-
-/// %Animation trigger point.
-struct AnimationTriggerPoint
-{
-    /// Construct.
-    AnimationTriggerPoint() :
-        time_(0.0f)
-    {
-    }
-
-    /// Trigger time.
-    float time_;
-    /// Trigger data.
-    Variant data_;
-};
-
-static const unsigned char CHANNEL_POSITION = 0x1;
-static const unsigned char CHANNEL_ROTATION = 0x2;
-static const unsigned char CHANNEL_SCALE = 0x4;
-
-/// Skeletal animation resource.
-class ATOMIC_API Animation : public Resource
-{
-    ATOMIC_OBJECT(Animation, Resource);
-
-public:
-    /// Construct.
-    Animation(Context* context);
-    /// Destruct.
-    virtual ~Animation();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-
-    /// Load resource from stream. May be called from a worker thread. Return true if successful.
-    virtual bool BeginLoad(Deserializer& source);
-    /// Save resource. Return true if successful.
-    virtual bool Save(Serializer& dest) const;
-
-    /// Set animation name.
-    void SetAnimationName(const String& name);
-    /// Set animation length.
-    void SetLength(float length);
-    /// 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.
-    void AddTrigger(float time, bool timeIsNormalized, const Variant& data);
-    /// Remove a trigger point by index.
-    void RemoveTrigger(unsigned index);
-    /// Remove all trigger points.
-    void RemoveAllTriggers();
-    /// Resize trigger point vector.
-    void SetNumTriggers(unsigned num);
-    /// Clone the animation.
-    SharedPtr<Animation> Clone(const String& cloneName = String::EMPTY) const;
-
-    /// Return animation name.
-    const String& GetAnimationName() const { return animationName_; }
-
-    /// Return animation name hash.
-    StringHash GetAnimationNameHash() const { return animationNameHash_; }
-
-    /// Return animation length.
-    float GetLength() const { return length_; }
-
-    /// Return all animation tracks.
-    const HashMap<StringHash, AnimationTrack>& GetTracks() const { return tracks_; }
-
-    /// Return number of animation tracks.
-    unsigned GetNumTracks() const { return tracks_.Size(); }
-
-    /// Return animation track by name.
-    AnimationTrack* GetTrack(const String& name);
-    /// Return animation track by name hash.
-    AnimationTrack* GetTrack(StringHash nameHash);
-
-    /// Return animation trigger points.
-    const Vector<AnimationTriggerPoint>& GetTriggers() const { return triggers_; }
-
-    /// Return number of animation trigger points.
-    unsigned GetNumTriggers() const { return triggers_.Size(); }
-
-    /// Return a trigger point by index.
-    AnimationTriggerPoint* GetTrigger(unsigned index);
-
-private:
-    /// Animation name.
-    String animationName_;
-    /// Animation name hash.
-    StringHash animationNameHash_;
-    /// Animation length.
-    float length_;
-    /// Animation tracks.
-    HashMap<StringHash, AnimationTrack> tracks_;
-    /// Animation trigger points.
-    Vector<AnimationTriggerPoint> triggers_;
-};
-
-}

+ 0 - 898
Source/Atomic/Atomic3D/AnimationController.cpp

@@ -1,898 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/AnimatedModel.h"
-#include "../Graphics/Animation.h"
-#include "../Graphics/AnimationController.h"
-#include "../Graphics/AnimationState.h"
-#include "../IO/Log.h"
-#include "../IO/MemoryBuffer.h"
-#include "../Resource/ResourceCache.h"
-#include "../Scene/Scene.h"
-#include "../Scene/SceneEvents.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-static const unsigned char CTRL_LOOPED = 0x1;
-static const unsigned char CTRL_STARTBONE = 0x2;
-static const unsigned char CTRL_AUTOFADE = 0x4;
-static const unsigned char CTRL_SETTIME = 0x08;
-static const unsigned char CTRL_SETWEIGHT = 0x10;
-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 COMMAND_STAY_TIME = 0.25f;
-static const unsigned MAX_NODE_ANIMATION_STATES = 256;
-
-extern const char* LOGIC_CATEGORY;
-
-AnimationController::AnimationController(Context* context) :
-    Component(context)
-{
-}
-
-AnimationController::~AnimationController()
-{
-}
-
-void AnimationController::RegisterObject(Context* context)
-{
-    context->RegisterFactory<AnimationController>(LOGIC_CATEGORY);
-
-    ATOMIC_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Animations", GetAnimationsAttr, SetAnimationsAttr, VariantVector, Variant::emptyVariantVector,
-        AM_FILE | AM_NOEDIT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Network Animations", GetNetAnimationsAttr, SetNetAnimationsAttr, PODVector<unsigned char>,
-        Variant::emptyBuffer, AM_NET | AM_LATESTDATA | AM_NOEDIT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Node Animation States", GetNodeAnimationStatesAttr, SetNodeAnimationStatesAttr, VariantVector,
-        Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
-}
-
-void AnimationController::OnSetEnabled()
-{
-    Scene* scene = GetScene();
-    if (scene)
-    {
-        if (IsEnabledEffective())
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, ATOMIC_HANDLER(AnimationController, HandleScenePostUpdate));
-        else
-            UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
-    }
-}
-
-void AnimationController::Update(float timeStep)
-{
-    // Loop through animations
-    for (unsigned i = 0; i < animations_.Size();)
-    {
-        AnimationControl& ctrl = animations_[i];
-        AnimationState* state = GetAnimationState(ctrl.hash_);
-        bool remove = false;
-
-        if (!state)
-            remove = true;
-        else
-        {
-            // Advance the animation
-            if (ctrl.speed_ != 0.0f)
-                state->AddTime(ctrl.speed_ * timeStep);
-
-            float targetWeight = ctrl.targetWeight_;
-            float fadeTime = ctrl.fadeTime_;
-
-            // If non-looped animation at the end, activate autofade as applicable
-            if (!state->IsLooped() && state->GetTime() >= state->GetLength() && ctrl.autoFadeTime_ > 0.0f)
-            {
-                targetWeight = 0.0f;
-                fadeTime = ctrl.autoFadeTime_;
-            }
-
-            // Process weight fade
-            float currentWeight = state->GetWeight();
-            if (currentWeight != targetWeight)
-            {
-                if (fadeTime > 0.0f)
-                {
-                    float weightDelta = 1.0f / fadeTime * timeStep;
-                    if (currentWeight < targetWeight)
-                        currentWeight = Min(currentWeight + weightDelta, targetWeight);
-                    else if (currentWeight > targetWeight)
-                        currentWeight = Max(currentWeight - weightDelta, targetWeight);
-                    state->SetWeight(currentWeight);
-                }
-                else
-                    state->SetWeight(targetWeight);
-            }
-
-            // Remove if weight zero and target weight zero
-            if (state->GetWeight() == 0.0f && (targetWeight == 0.0f || fadeTime == 0.0f) && ctrl.removeOnCompletion_)
-                remove = true;
-        }
-
-        // Decrement the command time-to-live values
-        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 (state)
-                RemoveAnimationState(state);
-            animations_.Erase(i);
-            MarkNetworkUpdate();
-        }
-        else
-            ++i;
-    }
-
-    // Node hierarchy animations need to be applied manually
-    for (Vector<SharedPtr<AnimationState> >::Iterator i = nodeAnimationStates_.Begin(); i != nodeAnimationStates_.End(); ++i)
-        (*i)->Apply();
-}
-
-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
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(newAnimation->GetName(), index, state);
-
-    if (!state)
-    {
-        state = AddAnimationState(newAnimation);
-        if (!state)
-            return false;
-    }
-
-    if (index == M_MAX_UNSIGNED)
-    {
-        AnimationControl newControl;
-        newControl.name_ = newAnimation->GetName();
-        newControl.hash_ = newAnimation->GetNameHash();
-        animations_.Push(newControl);
-        index = animations_.Size() - 1;
-    }
-
-    state->SetLayer(layer);
-    state->SetLooped(looped);
-    animations_[index].targetWeight_ = 1.0f;
-    animations_[index].fadeTime_ = fadeInTime;
-
-    MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::PlayExclusive(const String& name, unsigned char layer, bool looped, float fadeTime)
-{
-    FadeOthers(name, 0.0f, fadeTime);
-    return Play(name, layer, looped, fadeTime);
-}
-
-bool AnimationController::Stop(const String& name, float fadeOutTime)
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index != M_MAX_UNSIGNED)
-    {
-        animations_[index].targetWeight_ = 0.0f;
-        animations_[index].fadeTime_ = fadeOutTime;
-        MarkNetworkUpdate();
-    }
-
-    return index != M_MAX_UNSIGNED || state != 0;
-}
-
-void AnimationController::StopLayer(unsigned char layer, float fadeOutTime)
-{
-    bool needUpdate = false;
-    for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i)
-    {
-        AnimationState* state = GetAnimationState(i->hash_);
-        if (state && state->GetLayer() == layer)
-        {
-            i->targetWeight_ = 0.0f;
-            i->fadeTime_ = fadeOutTime;
-            needUpdate = true;
-        }
-    }
-
-    if (needUpdate)
-        MarkNetworkUpdate();
-}
-
-void AnimationController::StopAll(float fadeOutTime)
-{
-    if (animations_.Size())
-    {
-        for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i)
-        {
-            i->targetWeight_ = 0.0f;
-            i->fadeTime_ = fadeOutTime;
-        }
-
-        MarkNetworkUpdate();
-    }
-}
-
-bool AnimationController::Fade(const String& name, float targetWeight, float fadeTime)
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED)
-        return false;
-
-    animations_[index].targetWeight_ = Clamp(targetWeight, 0.0f, 1.0f);
-    animations_[index].fadeTime_ = fadeTime;
-    MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::FadeOthers(const String& name, float targetWeight, float fadeTime)
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED || !state)
-        return false;
-
-    unsigned char layer = state->GetLayer();
-
-    bool needUpdate = false;
-    for (unsigned i = 0; i < animations_.Size(); ++i)
-    {
-        if (i != index)
-        {
-            AnimationControl& control = animations_[i];
-            AnimationState* otherState = GetAnimationState(control.hash_);
-            if (otherState && otherState->GetLayer() == layer)
-            {
-                control.targetWeight_ = Clamp(targetWeight, 0.0f, 1.0f);
-                control.fadeTime_ = fadeTime;
-                needUpdate = true;
-            }
-        }
-    }
-
-    if (needUpdate)
-        MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::SetLayer(const String& name, unsigned char layer)
-{
-    AnimationState* state = GetAnimationState(name);
-    if (!state)
-        return false;
-
-    state->SetLayer(layer);
-    MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::SetStartBone(const String& name, const String& startBoneName)
-{
-    // Start bone can only be set in model mode
-    AnimatedModel* model = GetComponent<AnimatedModel>();
-    if (!model)
-        return false;
-
-    AnimationState* state = model->GetAnimationState(name);
-    if (!state)
-        return false;
-
-    Bone* bone = model->GetSkeleton().GetBone(startBoneName);
-    state->SetStartBone(bone);
-    MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::SetTime(const String& name, float time)
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED || !state)
-        return false;
-
-    time = Clamp(time, 0.0f, state->GetLength());
-    state->SetTime(time);
-    // Prepare "set time" command for network replication
-    animations_[index].setTime_ = (unsigned short)(time / state->GetLength() * 65535.0f);
-    animations_[index].setTimeTtl_ = COMMAND_STAY_TIME;
-    ++animations_[index].setTimeRev_;
-    MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::SetSpeed(const String& name, float speed)
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED)
-        return false;
-
-    animations_[index].speed_ = speed;
-    MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::SetWeight(const String& name, float weight)
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED || !state)
-        return false;
-
-    weight = Clamp(weight, 0.0f, 1.0f);
-    state->SetWeight(weight);
-    // Prepare "set weight" command for network replication
-    animations_[index].setWeight_ = (unsigned char)(weight * 255.0f);
-    animations_[index].setWeightTtl_ = COMMAND_STAY_TIME;
-    ++animations_[index].setWeightRev_;
-    MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::SetRemoveOnCompletion(const String& name, bool removeOnCompletion)
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED || !state)
-        return false;
-
-    animations_[index].removeOnCompletion_ = removeOnCompletion;
-    MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::SetLooped(const String& name, bool enable)
-{
-    AnimationState* state = GetAnimationState(name);
-    if (!state)
-        return false;
-
-    state->SetLooped(enable);
-    MarkNetworkUpdate();
-    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)
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED)
-        return false;
-
-    animations_[index].autoFadeTime_ = Max(fadeOutTime, 0.0f);
-    MarkNetworkUpdate();
-    return true;
-}
-
-bool AnimationController::IsPlaying(const String& name) const
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    return index != M_MAX_UNSIGNED;
-}
-
-bool AnimationController::IsFadingIn(const String& name) const
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED || !state)
-        return false;
-
-    return animations_[index].fadeTime_ && animations_[index].targetWeight_ > state->GetWeight();
-}
-
-bool AnimationController::IsFadingOut(const String& name) const
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED || !state)
-        return false;
-
-    return (animations_[index].fadeTime_ && animations_[index].targetWeight_ < state->GetWeight())
-           || (!state->IsLooped() && state->GetTime() >= state->GetLength() && animations_[index].autoFadeTime_);
-}
-
-bool AnimationController::IsAtEnd(const String& name) const
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    if (index == M_MAX_UNSIGNED || !state)
-        return false;
-    else
-        return state->GetTime() >= state->GetLength();
-}
-
-unsigned char AnimationController::GetLayer(const String& name) const
-{
-    AnimationState* state = GetAnimationState(name);
-    return (unsigned char)(state ? state->GetLayer() : 0);
-}
-
-Bone* AnimationController::GetStartBone(const String& name) const
-{
-    AnimationState* state = GetAnimationState(name);
-    return state ? state->GetStartBone() : 0;
-}
-
-const String& AnimationController::GetStartBoneName(const String& name) const
-{
-    Bone* bone = GetStartBone(name);
-    return bone ? bone->name_ : String::EMPTY;
-}
-
-float AnimationController::GetTime(const String& name) const
-{
-    AnimationState* state = GetAnimationState(name);
-    return state ? state->GetTime() : 0.0f;
-}
-
-float AnimationController::GetWeight(const String& name) const
-{
-    AnimationState* state = GetAnimationState(name);
-    return state ? state->GetWeight() : 0.0f;
-}
-
-bool AnimationController::IsLooped(const String& name) const
-{
-    AnimationState* state = GetAnimationState(name);
-    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
-{
-    AnimationState* state = GetAnimationState(name);
-    return state ? state->GetLength() : 0.0f;
-}
-
-float AnimationController::GetSpeed(const String& name) const
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    return index != M_MAX_UNSIGNED ? animations_[index].speed_ : 0.0f;
-}
-
-float AnimationController::GetFadeTarget(const String& name) const
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    return index != M_MAX_UNSIGNED ? animations_[index].targetWeight_ : 0.0f;
-}
-
-float AnimationController::GetFadeTime(const String& name) const
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    return index != M_MAX_UNSIGNED ? animations_[index].targetWeight_ : 0.0f;
-}
-
-float AnimationController::GetAutoFade(const String& name) const
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    return index != M_MAX_UNSIGNED ? animations_[index].autoFadeTime_ : 0.0f;
-}
-
-bool AnimationController::GetRemoveOnCompletion(const String& name) const
-{
-    unsigned index;
-    AnimationState* state;
-    FindAnimation(name, index, state);
-    return index != M_MAX_UNSIGNED ? animations_[index].removeOnCompletion_ : false;
-}
-
-AnimationState* AnimationController::GetAnimationState(const String& name) const
-{
-    return GetAnimationState(StringHash(name));
-}
-
-AnimationState* AnimationController::GetAnimationState(StringHash nameHash) const
-{
-    // Model mode
-    AnimatedModel* model = GetComponent<AnimatedModel>();
-    if (model)
-        return model->GetAnimationState(nameHash);
-
-    // Node hierarchy mode
-    for (Vector<SharedPtr<AnimationState> >::ConstIterator i = nodeAnimationStates_.Begin(); i != nodeAnimationStates_.End(); ++i)
-    {
-        Animation* animation = (*i)->GetAnimation();
-        if (animation->GetNameHash() == nameHash || animation->GetAnimationNameHash() == nameHash)
-            return *i;
-    }
-
-    return 0;
-}
-
-void AnimationController::SetAnimationsAttr(const VariantVector& value)
-{
-    animations_.Clear();
-    animations_.Reserve(value.Size() / 5);  // Incomplete data is discarded
-    unsigned index = 0;
-    while (index + 4 < value.Size())    // Prevent out-of-bound index access
-    {
-        AnimationControl newControl;
-        newControl.name_ = value[index++].GetString();
-        newControl.hash_ = StringHash(newControl.name_);
-        newControl.speed_ = value[index++].GetFloat();
-        newControl.targetWeight_ = value[index++].GetFloat();
-        newControl.fadeTime_ = value[index++].GetFloat();
-        newControl.autoFadeTime_ = value[index++].GetFloat();
-        animations_.Push(newControl);
-    }
-}
-
-void AnimationController::SetNetAnimationsAttr(const PODVector<unsigned char>& value)
-{
-    MemoryBuffer buf(value);
-
-    AnimatedModel* model = GetComponent<AnimatedModel>();
-
-    // Check which animations we need to remove
-    HashSet<StringHash> processedAnimations;
-
-    unsigned numAnimations = buf.ReadVLE();
-    while (numAnimations--)
-    {
-        String animName = buf.ReadString();
-        StringHash animHash(animName);
-        processedAnimations.Insert(animHash);
-
-        // Check if the animation state exists. If not, add new
-        AnimationState* state = GetAnimationState(animHash);
-        if (!state)
-        {
-            Animation* newAnimation = GetSubsystem<ResourceCache>()->GetResource<Animation>(animName);
-            state = AddAnimationState(newAnimation);
-            if (!state)
-            {
-                ATOMIC_LOGERROR("Animation update applying aborted due to unknown animation");
-                return;
-            }
-        }
-        // Check if the internal control structure exists. If not, add new
-        unsigned index;
-        for (index = 0; index < animations_.Size(); ++index)
-        {
-            if (animations_[index].hash_ == animHash)
-                break;
-        }
-        if (index == animations_.Size())
-        {
-            AnimationControl newControl;
-            newControl.name_ = animName;
-            newControl.hash_ = animHash;
-            animations_.Push(newControl);
-        }
-
-        unsigned char ctrl = buf.ReadUByte();
-        state->SetLayer(buf.ReadUByte());
-        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].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
-        if (ctrl & CTRL_STARTBONE)
-        {
-            StringHash boneHash = buf.ReadStringHash();
-            if (model)
-                state->SetStartBone(model->GetSkeleton().GetBone(boneHash));
-        }
-        else
-            state->SetStartBone(0);
-        if (ctrl & CTRL_AUTOFADE)
-            animations_[index].autoFadeTime_ = (float)buf.ReadUByte() / 64.0f; // 6 bits of decimal precision, max. 4 seconds fade
-        else
-            animations_[index].autoFadeTime_ = 0.0f;
-        
-        animations_[index].removeOnCompletion_ = (ctrl & CTRL_REMOVEONCOMPLETION) != 0;
-        
-        if (ctrl & CTRL_SETTIME)
-        {
-            unsigned char setTimeRev = buf.ReadUByte();
-            unsigned short setTime = buf.ReadUShort();
-            // Apply set time command only if revision differs
-            if (setTimeRev != animations_[index].setTimeRev_)
-            {
-                state->SetTime(((float)setTime / 65535.0f) * state->GetLength());
-                animations_[index].setTimeRev_ = setTimeRev;
-            }
-        }
-        if (ctrl & CTRL_SETWEIGHT)
-        {
-            unsigned char setWeightRev = buf.ReadUByte();
-            unsigned char setWeight = buf.ReadUByte();
-            // Apply set weight command only if revision differs
-            if (setWeightRev != animations_[index].setWeightRev_)
-            {
-                state->SetWeight((float)setWeight / 255.0f);
-                animations_[index].setWeightRev_ = setWeightRev;
-            }
-        }
-    }
-
-    // Set any extra animations to fade out
-    for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i)
-    {
-        if (!processedAnimations.Contains(i->hash_))
-        {
-            i->targetWeight_ = 0.0f;
-            i->fadeTime_ = EXTRA_ANIM_FADEOUT_TIME;
-        }
-    }
-}
-
-void AnimationController::SetNodeAnimationStatesAttr(const VariantVector& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    nodeAnimationStates_.Clear();
-    unsigned index = 0;
-    unsigned numStates = index < value.Size() ? value[index++].GetUInt() : 0;
-    // Prevent negative or overly large value being assigned from the editor
-    if (numStates > M_MAX_INT)
-        numStates = 0;
-    if (numStates > MAX_NODE_ANIMATION_STATES)
-        numStates = MAX_NODE_ANIMATION_STATES;
-
-    nodeAnimationStates_.Reserve(numStates);
-    while (numStates--)
-    {
-        if (index + 2 < value.Size())
-        {
-            // Note: null animation is allowed here for editing
-            const ResourceRef& animRef = value[index++].GetResourceRef();
-            SharedPtr<AnimationState> newState(new AnimationState(GetNode(), cache->GetResource<Animation>(animRef.name_)));
-            nodeAnimationStates_.Push(newState);
-
-            newState->SetLooped(value[index++].GetBool());
-            newState->SetTime(value[index++].GetFloat());
-        }
-        else
-        {
-            // If not enough data, just add an empty animation state
-            SharedPtr<AnimationState> newState(new AnimationState(GetNode(), 0));
-            nodeAnimationStates_.Push(newState);
-        }
-    }
-}
-
-VariantVector AnimationController::GetAnimationsAttr() const
-{
-    VariantVector ret;
-    ret.Reserve(animations_.Size() * 5);
-    for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i)
-    {
-        ret.Push(i->name_);
-        ret.Push(i->speed_);
-        ret.Push(i->targetWeight_);
-        ret.Push(i->fadeTime_);
-        ret.Push(i->autoFadeTime_);
-    }
-    return ret;
-}
-
-const PODVector<unsigned char>& AnimationController::GetNetAnimationsAttr() const
-{
-    attrBuffer_.Clear();
-
-    AnimatedModel* model = GetComponent<AnimatedModel>();
-
-    unsigned validAnimations = 0;
-    for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i)
-    {
-        if (GetAnimationState(i->hash_))
-            ++validAnimations;
-    }
-
-    attrBuffer_.WriteVLE(validAnimations);
-    for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i)
-    {
-        AnimationState* state = GetAnimationState(i->hash_);
-        if (!state)
-            continue;
-
-        unsigned char ctrl = 0;
-        Bone* startBone = state->GetStartBone();
-        if (state->IsLooped())
-            ctrl |= CTRL_LOOPED;
-        if (state->GetBlendMode() == ABM_ADDITIVE)
-            ctrl |= CTRL_ADDITIVE;
-        if (startBone && model && startBone != model->GetSkeleton().GetRootBone())
-            ctrl |= CTRL_STARTBONE;
-        if (i->autoFadeTime_ > 0.0f)
-            ctrl |= CTRL_AUTOFADE;
-        if (i->removeOnCompletion_)
-            ctrl |= CTRL_REMOVEONCOMPLETION;
-        if (i->setTimeTtl_ > 0.0f)
-            ctrl |= CTRL_SETTIME;
-        if (i->setWeightTtl_ > 0.0f)
-            ctrl |= CTRL_SETWEIGHT;
-
-        attrBuffer_.WriteString(i->name_);
-        attrBuffer_.WriteUByte(ctrl);
-        attrBuffer_.WriteUByte(state->GetLayer());
-        attrBuffer_.WriteShort((short)Clamp(i->speed_ * 2048.0f, -32767.0f, 32767.0f));
-        attrBuffer_.WriteUByte((unsigned char)(i->targetWeight_ * 255.0f));
-        attrBuffer_.WriteUByte((unsigned char)Clamp(i->fadeTime_ * 64.0f, 0.0f, 255.0f));
-        if (ctrl & CTRL_STARTBONE)
-            attrBuffer_.WriteStringHash(startBone->nameHash_);
-        if (ctrl & CTRL_AUTOFADE)
-            attrBuffer_.WriteUByte((unsigned char)Clamp(i->autoFadeTime_ * 64.0f, 0.0f, 255.0f));
-        if (ctrl & CTRL_SETTIME)
-        {
-            attrBuffer_.WriteUByte(i->setTimeRev_);
-            attrBuffer_.WriteUShort(i->setTime_);
-        }
-        if (ctrl & CTRL_SETWEIGHT)
-        {
-            attrBuffer_.WriteUByte(i->setWeightRev_);
-            attrBuffer_.WriteUByte(i->setWeight_);
-        }
-    }
-
-    return attrBuffer_.GetBuffer();
-}
-
-VariantVector AnimationController::GetNodeAnimationStatesAttr() const
-{
-    VariantVector ret;
-    ret.Reserve(nodeAnimationStates_.Size() * 3 + 1);
-    ret.Push(nodeAnimationStates_.Size());
-    for (Vector<SharedPtr<AnimationState> >::ConstIterator i = nodeAnimationStates_.Begin(); i != nodeAnimationStates_.End(); ++i)
-    {
-        AnimationState* state = *i;
-        Animation* animation = state->GetAnimation();
-        ret.Push(GetResourceRef(animation, Animation::GetTypeStatic()));
-        ret.Push(state->IsLooped());
-        ret.Push(state->GetTime());
-    }
-    return ret;
-}
-
-void AnimationController::OnSceneSet(Scene* scene)
-{
-    if (scene && IsEnabledEffective())
-        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, ATOMIC_HANDLER(AnimationController, HandleScenePostUpdate));
-    else if (!scene)
-        UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
-}
-
-AnimationState* AnimationController::AddAnimationState(Animation* animation)
-{
-    if (!animation)
-        return 0;
-
-    // Model mode
-    AnimatedModel* model = GetComponent<AnimatedModel>();
-    if (model)
-        return model->AddAnimationState(animation);
-
-    // Node hierarchy mode
-    SharedPtr<AnimationState> newState(new AnimationState(node_, animation));
-    nodeAnimationStates_.Push(newState);
-    return newState;
-}
-
-void AnimationController::RemoveAnimationState(AnimationState* state)
-{
-    if (!state)
-        return;
-
-    // Model mode
-    AnimatedModel* model = GetComponent<AnimatedModel>();
-    if (model)
-    {
-        model->RemoveAnimationState(state);
-        return;
-    }
-
-    // Node hierarchy mode
-    for (Vector<SharedPtr<AnimationState> >::Iterator i = nodeAnimationStates_.Begin(); i != nodeAnimationStates_.End(); ++i)
-    {
-        if ((*i) == state)
-        {
-            nodeAnimationStates_.Erase(i);
-            return;
-        }
-    }
-}
-
-void AnimationController::FindAnimation(const String& name, unsigned& index, AnimationState*& state) const
-{
-    StringHash nameHash(name);
-
-    // Find the AnimationState
-    state = GetAnimationState(nameHash);
-    if (state)
-    {
-        // Either a resource name or animation name may be specified. We store resource names, so correct the hash if necessary
-        nameHash = state->GetAnimation()->GetNameHash();
-    }
-
-    // Find the internal control structure
-    index = M_MAX_UNSIGNED;
-    for (unsigned i = 0; i < animations_.Size(); ++i)
-    {
-        if (animations_[i].hash_ == nameHash)
-        {
-            index = i;
-            break;
-        }
-    }
-}
-
-void AnimationController::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
-{
-    using namespace ScenePostUpdate;
-
-    Update(eventData[P_TIMESTEP].GetFloat());
-}
-
-}

+ 0 - 209
Source/Atomic/Atomic3D/AnimationController.h

@@ -1,209 +0,0 @@
-//
-// 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 "../IO/VectorBuffer.h"
-#include "../Scene/Component.h"
-#include "../Graphics/AnimationState.h"
-
-namespace Atomic
-{
-
-class AnimatedModel;
-class Animation;
-struct Bone;
-
-/// Control data for an animation.
-struct AnimationControl
-{
-    /// Construct with defaults.
-    AnimationControl() :
-        speed_(1.0f),
-        targetWeight_(0.0f),
-        fadeTime_(0.0f),
-        autoFadeTime_(0.0f),
-        setTimeTtl_(0.0f),
-        setWeightTtl_(0.0f),
-        setTime_(0),
-        setWeight_(0),
-        setTimeRev_(0),
-        setWeightRev_(0),
-        removeOnCompletion_(true)
-    {
-    }
-
-    /// Animation resource name.
-    String name_;
-    /// Animation resource name hash.
-    StringHash hash_;
-    /// Animation speed.
-    float speed_;
-    /// Animation target weight.
-    float targetWeight_;
-    /// Animation weight fade time, 0 if no fade.
-    float fadeTime_;
-    /// Animation autofade on stop -time, 0 if disabled.
-    float autoFadeTime_;
-    /// Set time command time-to-live.
-    float setTimeTtl_;
-    /// Set weight command time-to-live.
-    float setWeightTtl_;
-    /// Set time command.
-    unsigned short setTime_;
-    /// Set weight command.
-    unsigned char setWeight_;
-    /// Set time command revision.
-    unsigned char setTimeRev_;
-    /// Set weight command revision.
-    unsigned char setWeightRev_;
-    /// Sets whether this should automatically be removed when it finishes playing.
-    bool removeOnCompletion_;
-};
-
-/// %Component that drives an AnimatedModel's animations.
-class ATOMIC_API AnimationController : public Component
-{
-    ATOMIC_OBJECT(AnimationController, Component);
-
-public:
-    /// Construct.
-    AnimationController(Context* context);
-    /// Destruct.
-    virtual ~AnimationController();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-
-    /// Handle enabled/disabled state change.
-    virtual void OnSetEnabled();
-
-    /// Update the animations. Is called from HandleScenePostUpdate().
-    void Update(float timeStep);
-    /// Play an animation and set full target weight. Name must be the full resource name. Return true on success.
-    bool Play(const String& name, unsigned char layer, bool looped, float fadeInTime = 0.0f);
-    /// Play an animation, set full target weight and fade out all other animations on the same layer. Name must be the full resource name. Return true on success.
-    bool PlayExclusive(const String& name, unsigned char layer, bool looped, float fadeTime = 0.0f);
-    /// Stop an animation. Zero fadetime is instant. Return true on success.
-    bool Stop(const String& name, float fadeOutTime = 0.0f);
-    /// Stop all animations on a specific layer. Zero fadetime is instant.
-    void StopLayer(unsigned char layer, float fadeOutTime = 0.0f);
-    /// Stop all animations. Zero fadetime is instant.
-    void StopAll(float fadeTime = 0.0f);
-    /// Fade animation to target weight. Return true on success.
-    bool Fade(const String& name, float targetWeight, float fadeTime);
-    /// Fade other animations on the same layer to target weight. Return true on success.
-    bool FadeOthers(const String& name, float targetWeight, float fadeTime);
-
-    /// Set animation blending layer priority. Return true on success.
-    bool SetLayer(const String& name, unsigned char layer);
-    /// Set animation start bone. Return true on success.
-    bool SetStartBone(const String& name, const String& startBoneName);
-    /// Set animation time position. Return true on success.
-    bool SetTime(const String& name, float time);
-    /// Set animation weight. Return true on success.
-    bool SetWeight(const String& name, float weight);
-    /// Set animation looping. Return true on success.
-    bool SetLooped(const String& name, bool enable);
-    /// Set animation speed. Return true on success.
-    bool SetSpeed(const String& name, float speed);
-    /// Set animation autofade at end (non-looped animations only.) Zero time disables. Return true on success.
-    bool SetAutoFade(const String& name, float fadeOutTime);
-    /// Set whether an animation auto-removes on completion.
-    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.
-    bool IsPlaying(const String& name) const;
-    /// Return whether an animation is fading in.
-    bool IsFadingIn(const String& name) const;
-    /// Return whether an animation is fading out.
-    bool IsFadingOut(const String& name) const;
-    /// Return whether an animation is at its end. Will return false if the animation is not active at all.
-    bool IsAtEnd(const String& name) const;
-    /// Return animation blending layer.
-    unsigned char GetLayer(const String& name) const;
-    /// Return animation start bone, or null if no such animation.
-    Bone* GetStartBone(const String& name) const;
-    /// Return animation start bone name, or empty string if no such animation.
-    const String& GetStartBoneName(const String& name) const;
-    /// Return animation time position.
-    float GetTime(const String& name) const;
-    /// Return animation weight.
-    float GetWeight(const String& name) const;
-    /// Return animation looping.
-    bool IsLooped(const String& name) const;
-    /// Return animation blending mode.
-    AnimationBlendMode GetBlendMode(const String& name) const;
-    /// Return animation length.
-    float GetLength(const String& name) const;
-    /// Return animation speed.
-    float GetSpeed(const String& name) const;
-    /// Return animation fade target weight.
-    float GetFadeTarget(const String& name) const;
-    /// Return animation fade time.
-    float GetFadeTime(const String& name) const;
-    /// Return animation autofade time.
-    float GetAutoFade(const String& name) const;
-    /// Return whether animation auto-removes on completion, or false if no such animation.
-    bool GetRemoveOnCompletion(const String& name) const;
-    /// Find an animation state by animation name.
-    AnimationState* GetAnimationState(const String& name) const;
-    /// Find an animation state by animation name hash
-    AnimationState* GetAnimationState(StringHash nameHash) const;
-
-    /// Set animation control structures attribute.
-    void SetAnimationsAttr(const VariantVector& value);
-    /// Set animations attribute for network replication.
-    void SetNetAnimationsAttr(const PODVector<unsigned char>& value);
-    /// Set node animation states attribute.
-    void SetNodeAnimationStatesAttr(const VariantVector& value);
-    /// Return animation control structures attribute.
-    VariantVector GetAnimationsAttr() const;
-    /// Return animations attribute for network replication.
-    const PODVector<unsigned char>& GetNetAnimationsAttr() const;
-    /// Return node animation states attribute.
-    VariantVector GetNodeAnimationStatesAttr() const;
-
-protected:
-    /// Handle scene being assigned.
-    virtual void OnSceneSet(Scene* scene);
-
-private:
-    /// Add an animation state either to AnimatedModel or as a node animation.
-    AnimationState* AddAnimationState(Animation* animation);
-    /// Remove an animation state.
-    void RemoveAnimationState(AnimationState* state);
-    /// Find the internal index and animation state of an animation.
-    void FindAnimation(const String& name, unsigned& index, AnimationState*& state) const;
-    /// Handle scene post-update event.
-    void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
-
-    /// Animation control structures.
-    Vector<AnimationControl> animations_;
-    /// Node hierarchy mode animation states.
-    Vector<SharedPtr<AnimationState> > nodeAnimationStates_;
-    /// Attribute buffer for network replication.
-    mutable VectorBuffer attrBuffer_;
-};
-
-}

+ 0 - 591
Source/Atomic/Atomic3D/AnimationState.cpp

@@ -1,591 +0,0 @@
-//
-// 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/AnimatedModel.h"
-#include "../Graphics/Animation.h"
-#include "../Graphics/AnimationState.h"
-#include "../Graphics/DrawableEvents.h"
-#include "../IO/Log.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-AnimationStateTrack::AnimationStateTrack() :
-    track_(0),
-    bone_(0),
-    weight_(1.0f),
-    keyFrame_(0)
-{
-}
-
-AnimationStateTrack::~AnimationStateTrack()
-{
-}
-
-AnimationState::AnimationState(AnimatedModel* model, Animation* animation) :
-    model_(model),
-    animation_(animation),
-    startBone_(0),
-    looped_(false),
-    weight_(0.0f),
-    time_(0.0f),
-    layer_(0),
-    blendingMode_(ABM_LERP)
-{
-    // Set default start bone (use all tracks.)
-    SetStartBone(0);
-}
-
-AnimationState::AnimationState(Node* node, Animation* animation) :
-    node_(node),
-    animation_(animation),
-    startBone_(0),
-    looped_(false),
-    weight_(1.0f),
-    time_(0.0f),
-    layer_(0),
-    blendingMode_(ABM_LERP)
-{
-    if (animation_)
-    {
-        // Setup animation track to scene node mapping
-        if (node_)
-        {
-            const HashMap<StringHash, AnimationTrack>& tracks = animation_->GetTracks();
-            stateTracks_.Clear();
-
-            for (HashMap<StringHash, AnimationTrack>::ConstIterator i = tracks.Begin(); i != tracks.End(); ++i)
-            {
-                const StringHash& nameHash = i->second_.nameHash_;
-                AnimationStateTrack stateTrack;
-                stateTrack.track_ = &i->second_;
-
-                if (node_->GetNameHash() == nameHash || tracks.Size() == 1)
-                    stateTrack.node_ = node_;
-                else
-                {
-                    Node* targetNode = node_->GetChild(nameHash, true);
-                    if (targetNode)
-                        stateTrack.node_ = targetNode;
-                    else
-                        ATOMIC_LOGWARNING("Node " + i->second_.name_ + " not found for node animation " + animation_->GetName());
-                }
-
-                if (stateTrack.node_)
-                    stateTracks_.Push(stateTrack);
-            }
-        }
-    }
-}
-
-
-AnimationState::~AnimationState()
-{
-}
-
-void AnimationState::SetStartBone(Bone* startBone)
-{
-    if (!model_ || !animation_)
-        return;
-
-    Skeleton& skeleton = model_->GetSkeleton();
-    if (!startBone)
-    {
-        Bone* rootBone = skeleton.GetRootBone();
-        if (!rootBone)
-            return;
-        startBone = rootBone;
-    }
-
-    // Do not reassign if the start bone did not actually change, and we already have valid bone nodes
-    if (startBone == startBone_ && !stateTracks_.Empty())
-        return;
-
-    startBone_ = startBone;
-
-    const HashMap<StringHash, AnimationTrack>& tracks = animation_->GetTracks();
-    stateTracks_.Clear();
-
-    if (!startBone->node_)
-        return;
-
-    for (HashMap<StringHash, AnimationTrack>::ConstIterator i = tracks.Begin(); i != tracks.End(); ++i)
-    {
-        AnimationStateTrack stateTrack;
-        stateTrack.track_ = &i->second_;
-
-        // Include those tracks that are either the start bone itself, or its children
-        Bone* trackBone = 0;
-        const StringHash& nameHash = i->second_.nameHash_;
-
-        if (nameHash == startBone->nameHash_)
-            trackBone = startBone;
-        else
-        {
-            Node* trackBoneNode = startBone->node_->GetChild(nameHash, true);
-            if (trackBoneNode)
-                trackBone = skeleton.GetBone(nameHash);
-        }
-
-        if (trackBone && trackBone->node_)
-        {
-            stateTrack.bone_ = trackBone;
-            stateTrack.node_ = trackBone->node_;
-            stateTracks_.Push(stateTrack);
-        }
-    }
-
-    model_->MarkAnimationDirty();
-}
-
-void AnimationState::SetLooped(bool looped)
-{
-    looped_ = looped;
-}
-
-void AnimationState::SetWeight(float weight)
-{
-    // Weight can only be set in model mode. In node animation it is hardcoded to full
-    if (model_)
-    {
-        weight = Clamp(weight, 0.0f, 1.0f);
-        if (weight != weight_)
-        {
-            weight_ = weight;
-            model_->MarkAnimationDirty();
-        }
-    }
-}
-
-void AnimationState::SetBlendMode(AnimationBlendMode mode)
-{
-    if (model_)
-    {
-        if (blendingMode_ != mode)
-        {
-            blendingMode_ = mode;
-            model_->MarkAnimationDirty();
-        }
-    }
-}
-
-void AnimationState::SetTime(float time)
-{
-    if (!animation_)
-        return;
-
-    time = Clamp(time, 0.0f, animation_->GetLength());
-    if (time != time_)
-    {
-        time_ = time;
-        if (model_)
-            model_->MarkAnimationDirty();
-    }
-}
-
-void AnimationState::SetBoneWeight(unsigned index, float weight, bool recursive)
-{
-    if (index >= stateTracks_.Size())
-        return;
-
-    weight = Clamp(weight, 0.0f, 1.0f);
-
-    if (weight != stateTracks_[index].weight_)
-    {
-        stateTracks_[index].weight_ = weight;
-        if (model_)
-            model_->MarkAnimationDirty();
-    }
-
-    if (recursive)
-    {
-        Node* boneNode = stateTracks_[index].node_;
-        if (boneNode)
-        {
-            const Vector<SharedPtr<Node> >& children = boneNode->GetChildren();
-            for (unsigned i = 0; i < children.Size(); ++i)
-            {
-                unsigned childTrackIndex = GetTrackIndex(children[i]);
-                if (childTrackIndex != M_MAX_UNSIGNED)
-                    SetBoneWeight(childTrackIndex, weight, true);
-            }
-        }
-    }
-}
-
-void AnimationState::SetBoneWeight(const String& name, float weight, bool recursive)
-{
-    SetBoneWeight(GetTrackIndex(name), weight, recursive);
-}
-
-void AnimationState::SetBoneWeight(StringHash nameHash, float weight, bool recursive)
-{
-    SetBoneWeight(GetTrackIndex(nameHash), weight, recursive);
-}
-
-void AnimationState::AddWeight(float delta)
-{
-    if (delta == 0.0f)
-        return;
-
-    SetWeight(GetWeight() + delta);
-}
-
-void AnimationState::AddTime(float delta)
-{
-    if (!animation_ || (!model_ && !node_))
-        return;
-
-    float length = animation_->GetLength();
-    if (delta == 0.0f || length == 0.0f)
-        return;
-
-    bool sendFinishEvent = false;
-
-    float oldTime = GetTime();
-    float time = oldTime + delta;
-    if (looped_)
-    {
-        while (time >= length)
-        {
-            time -= length;
-            sendFinishEvent = true;
-        }
-        while (time < 0.0f)
-        {
-            time += length;
-            sendFinishEvent = true;
-        }
-    }
-
-    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
-    if (animation_->GetNumTriggers())
-    {
-        bool wrap = false;
-
-        if (delta > 0.0f)
-        {
-            if (oldTime > time)
-            {
-                oldTime -= length;
-                wrap = true;
-            }
-        }
-        if (delta < 0.0f)
-        {
-            if (time > oldTime)
-            {
-                time -= length;
-                wrap = true;
-            }
-        }
-        if (oldTime > time)
-            Swap(oldTime, time);
-
-        const Vector<AnimationTriggerPoint>& triggers = animation_->GetTriggers();
-        for (Vector<AnimationTriggerPoint>::ConstIterator i = triggers.Begin(); i != triggers.End(); ++i)
-        {
-            float frameTime = i->time_;
-            if (looped_ && wrap)
-                frameTime = fmodf(frameTime, length);
-
-            if (oldTime <= frameTime && time > frameTime)
-            {
-                using namespace AnimationTrigger;
-
-                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_TIME] = i->time_;
-                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);
-                if (senderNode.Expired() || self.Expired())
-                    return;
-            }
-        }
-    }
-}
-
-void AnimationState::SetLayer(unsigned char layer)
-{
-    if (layer != layer_)
-    {
-        layer_ = layer;
-        if (model_)
-            model_->MarkAnimationOrderDirty();
-    }
-}
-
-AnimatedModel* AnimationState::GetModel() const
-{
-    return model_;
-}
-
-Node* AnimationState::GetNode() const
-{
-    return node_;
-}
-
-Bone* AnimationState::GetStartBone() const
-{
-    return model_ ? startBone_ : 0;
-}
-
-float AnimationState::GetBoneWeight(unsigned index) const
-{
-    return index < stateTracks_.Size() ? stateTracks_[index].weight_ : 0.0f;
-}
-
-float AnimationState::GetBoneWeight(const String& name) const
-{
-    return GetBoneWeight(GetTrackIndex(name));
-}
-
-float AnimationState::GetBoneWeight(StringHash nameHash) const
-{
-    return GetBoneWeight(GetTrackIndex(nameHash));
-}
-
-unsigned AnimationState::GetTrackIndex(const String& name) const
-{
-    for (unsigned i = 0; i < stateTracks_.Size(); ++i)
-    {
-        Node* node = stateTracks_[i].node_;
-        if (node && node->GetName() == name)
-            return i;
-    }
-
-    return M_MAX_UNSIGNED;
-}
-
-unsigned AnimationState::GetTrackIndex(Node* node) const
-{
-    for (unsigned i = 0; i < stateTracks_.Size(); ++i)
-    {
-        if (stateTracks_[i].node_ == node)
-            return i;
-    }
-
-    return M_MAX_UNSIGNED;
-}
-
-unsigned AnimationState::GetTrackIndex(StringHash nameHash) const
-{
-    for (unsigned i = 0; i < stateTracks_.Size(); ++i)
-    {
-        Node* node = stateTracks_[i].node_;
-        if (node && node->GetNameHash() == nameHash)
-            return i;
-    }
-
-    return M_MAX_UNSIGNED;
-}
-
-float AnimationState::GetLength() const
-{
-    return animation_ ? animation_->GetLength() : 0.0f;
-}
-
-void AnimationState::Apply()
-{
-    if (!animation_ || !IsEnabled())
-        return;
-
-    if (model_)
-        ApplyToModel();
-    else
-        ApplyToNodes();
-}
-
-void AnimationState::ApplyToModel()
-{
-    for (Vector<AnimationStateTrack>::Iterator i = stateTracks_.Begin(); i != stateTracks_.End(); ++i)
-    {
-        AnimationStateTrack& stateTrack = *i;
-        float finalWeight = weight_ * stateTrack.weight_;
-
-        // Do not apply if zero effective weight or the bone has animation disabled
-        if (Equals(finalWeight, 0.0f) || !stateTrack.bone_->animated_)
-            continue;
-            
-        ApplyTrack(stateTrack, finalWeight, true);
-    }
-}
-
-void AnimationState::ApplyToNodes()
-{
-    // 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)
-        ApplyTrack(*i, 1.0f, false);
-}
-
-void AnimationState::ApplyTrack(AnimationStateTrack& stateTrack, float weight, bool silent)
-{
-    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_;
-
-    Vector3 newPosition;
-    Quaternion newRotation;
-    Vector3 newScale;
-
-    if (interpolate)
-    {
-        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;
-
-        if (channelMask & CHANNEL_POSITION)
-            newPosition = keyFrame->position_.Lerp(nextKeyFrame->position_, t);
-        if (channelMask & CHANNEL_ROTATION)
-            newRotation = keyFrame->rotation_.Slerp(nextKeyFrame->rotation_, t);
-        if (channelMask & CHANNEL_SCALE)
-            newScale = keyFrame->scale_.Lerp(nextKeyFrame->scale_, t);
-    }
-    else
-    {
-        if (channelMask & CHANNEL_POSITION)
-            newPosition = keyFrame->position_;
-        if (channelMask & CHANNEL_ROTATION)
-            newRotation = keyFrame->rotation_;
-        if (channelMask & CHANNEL_SCALE)
-            newScale = keyFrame->scale_;
-    }
-    
-    if (blendingMode_ == ABM_ADDITIVE) // not ABM_LERP
-    {
-        if (channelMask & CHANNEL_POSITION)
-        {
-            Vector3 delta = newPosition - stateTrack.bone_->initialPosition_;
-            newPosition = node->GetPosition() + delta * weight;
-        }
-        if (channelMask & CHANNEL_ROTATION)
-        {
-            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)
-        {
-            Vector3 delta = newScale - stateTrack.bone_->initialScale_;
-            newScale = node->GetScale() + delta * weight;
-        }
-    }
-    else
-    {
-        if (!Equals(weight, 1.0f)) // not full weight
-        {
-            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);
-        }
-    }
-    
-    if (silent)
-    {
-        if (channelMask & CHANNEL_POSITION)
-            node->SetPositionSilent(newPosition);
-        if (channelMask & CHANNEL_ROTATION)
-            node->SetRotationSilent(newRotation);
-        if (channelMask & CHANNEL_SCALE)
-            node->SetScaleSilent(newScale);
-    }
-    else
-    {
-        if (channelMask & CHANNEL_POSITION)
-            node->SetPosition(newPosition);
-        if (channelMask & CHANNEL_ROTATION)
-            node->SetRotation(newRotation);
-        if (channelMask & CHANNEL_SCALE)
-            node->SetScale(newScale);
-    }
-}
-
-}

+ 0 - 178
Source/Atomic/Atomic3D/AnimationState.h

@@ -1,178 +0,0 @@
-//
-// 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 "../Container/HashMap.h"
-#include "../Container/Ptr.h"
-
-namespace Atomic
-{
-
-class Animation;
-class AnimatedModel;
-class Deserializer;
-class Serializer;
-class Skeleton;
-struct AnimationTrack;
-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.
-struct AnimationStateTrack
-{
-    /// Construct with defaults.
-    AnimationStateTrack();
-    /// Destruct
-    ~AnimationStateTrack();
-
-    /// Animation track.
-    const AnimationTrack* track_;
-    /// Bone pointer.
-    Bone* bone_;
-    /// Scene node pointer.
-    WeakPtr<Node> node_;
-    /// Blending weight.
-    float weight_;
-    /// Last key frame.
-    unsigned keyFrame_;
-};
-
-/// %Animation instance.
-class ATOMIC_API AnimationState : public RefCounted
-{
-public:
-    /// Construct with animated model and animation pointers.
-    AnimationState(AnimatedModel* model, Animation* animation);
-    /// Construct with root scene node and animation pointers.
-    AnimationState(Node* node, Animation* animation);
-    /// Destruct.
-    ~AnimationState();
-
-    /// Set start bone. Not supported in node animation mode. Resets any assigned per-bone weights.
-    void SetStartBone(Bone* bone);
-    /// Set looping enabled/disabled.
-    void SetLooped(bool looped);
-    /// Set blending weight.
-    void SetWeight(float weight);
-    /// Set blending mode.
-    void SetBlendMode(AnimationBlendMode mode);
-    /// Set time position. Does not fire animation triggers.
-    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.
-    void SetBoneWeight(unsigned index, float weight, bool recursive = false);
-    /// Set per-bone blending weight by name.
-    void SetBoneWeight(const String& name, float weight, bool recursive = false);
-    /// Set per-bone blending weight by name hash.
-    void SetBoneWeight(StringHash nameHash, float weight, bool recursive = false);
-    /// Modify blending weight.
-    void AddWeight(float delta);
-    /// Modify time position. %Animation triggers will be fired.
-    void AddTime(float delta);
-    /// Set blending layer.
-    void SetLayer(unsigned char layer);
-
-    /// Return animation.
-    Animation* GetAnimation() const { return animation_; }
-
-    /// Return animated model this state belongs to (model mode.)
-    AnimatedModel* GetModel() const;
-    /// Return root scene node this state controls (node hierarchy mode.)
-    Node* GetNode() const;
-    /// Return start bone.
-    Bone* GetStartBone() const;
-    /// Return per-bone blending weight by track index.
-    float GetBoneWeight(unsigned index) const;
-    /// Return per-bone blending weight by name.
-    float GetBoneWeight(const String& name) const;
-    /// Return per-bone blending weight by name.
-    float GetBoneWeight(StringHash nameHash) const;
-    /// Return track index with matching bone node, or M_MAX_UNSIGNED if not found.
-    unsigned GetTrackIndex(Node* node) const;
-    /// Return track index by bone name, or M_MAX_UNSIGNED if not found.
-    unsigned GetTrackIndex(const String& name) const;
-    /// Return track index by bone name hash, or M_MAX_UNSIGNED if not found.
-    unsigned GetTrackIndex(StringHash nameHash) const;
-
-    /// Return whether weight is nonzero.
-    bool IsEnabled() const { return weight_ > 0.0f; }
-
-    /// Return whether looped.
-    bool IsLooped() const { return looped_; }
-
-    /// Return blending weight.
-    float GetWeight() const { return weight_; }
-
-    /// Return blending mode.
-    AnimationBlendMode GetBlendMode() const { return blendingMode_; }
-
-    /// Return time position.
-    float GetTime() const { return time_; }
-
-    /// Return animation length.
-    float GetLength() const;
-
-    /// Return blending layer.
-    unsigned char GetLayer() const { return layer_; }
-
-    /// Apply the animation at the current time position.
-    void Apply();
-
-private:
-    /// Apply animation to a skeleton. Transform changes are applied silently, so the model needs to dirty its root model afterward.
-    void ApplyToModel();
-    /// Apply animation to a scene node hierarchy.
-    void ApplyToNodes();
-    /// Apply track.
-    void ApplyTrack(AnimationStateTrack& stateTrack, float weight, bool silent);
-
-    /// Animated model (model mode.)
-    WeakPtr<AnimatedModel> model_;
-    /// Root scene node (node hierarchy mode.)
-    WeakPtr<Node> node_;
-    /// Animation.
-    SharedPtr<Animation> animation_;
-    /// Start bone.
-    Bone* startBone_;
-    /// Per-track data.
-    Vector<AnimationStateTrack> stateTracks_;
-    /// Looped flag.
-    bool looped_;
-    /// Blending weight.
-    float weight_;
-    /// Time position.
-    float time_;
-    /// Blending layer.
-    unsigned char layer_;
-    /// Blending mode.
-    AnimationBlendMode blendingMode_;
-};
-
-}

+ 0 - 40
Source/Atomic/Atomic3D/Atomic3D.cpp

@@ -1,40 +0,0 @@
-
-#include "StaticModel.h"
-#include "StaticModelGroup.h"
-#include "Skybox.h"
-#include "Animation.h"
-#include "Model.h"
-#include "AnimatedModel.h"
-#include "AnimationController.h"
-#include "BillboardSet.h"
-#include "ParticleEffect.h"
-#include "ParticleEmitter.h"
-#include "CustomGeometry.h"
-#include "DecalSet.h"
-#include "Terrain.h"
-#include "TerrainPatch.h"
-
-
-namespace Atomic
-{
-
-
-void RegisterAtomic3DLibrary(Context* context)
-{
-    Animation::RegisterObject(context);
-    Model::RegisterObject(context);
-    StaticModel::RegisterObject(context);
-    StaticModelGroup::RegisterObject(context);
-    Skybox::RegisterObject(context);
-    AnimatedModel::RegisterObject(context);
-    AnimationController::RegisterObject(context);
-    BillboardSet::RegisterObject(context);
-    ParticleEffect::RegisterObject(context);
-    ParticleEmitter::RegisterObject(context);
-    CustomGeometry::RegisterObject(context);
-    DecalSet::RegisterObject(context);
-    Terrain::RegisterObject(context);
-    TerrainPatch::RegisterObject(context);
-}
-
-}

+ 0 - 9
Source/Atomic/Atomic3D/Atomic3D.h

@@ -1,9 +0,0 @@
-
-#pragma once
-
-namespace Atomic
-{
-
-void RegisterAtomic3DLibrary(Context* context);
-
-}

+ 0 - 760
Source/Atomic/Atomic3D/BillboardSet.cpp

@@ -1,760 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/Batch.h"
-#include "../Graphics/BillboardSet.h"
-#include "../Graphics/Camera.h"
-#include "../Graphics/Geometry.h"
-#include "../Graphics/Graphics.h"
-#include "../Graphics/IndexBuffer.h"
-#include "../Graphics/OctreeQuery.h"
-#include "../Graphics/VertexBuffer.h"
-#include "../IO/MemoryBuffer.h"
-#include "../Resource/ResourceCache.h"
-#include "../Scene/Node.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-
-static const float INV_SQRT_TWO = 1.0f / sqrtf(2.0f);
-
-const char* faceCameraModeNames[] =
-{
-    "None",
-    "Rotate XYZ",
-    "Rotate Y",
-    "LookAt XYZ",
-    "LookAt Y",
-    "Direction",
-    0
-};
-
-inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
-{
-    return lhs->sortDistance_ > rhs->sortDistance_;
-}
-
-BillboardSet::BillboardSet(Context* context) :
-    Drawable(context, DRAWABLE_GEOMETRY),
-    animationLodBias_(1.0f),
-    animationLodTimer_(0.0f),
-    relative_(true),
-    scaled_(true),
-    sorted_(false),
-    fixedScreenSize_(false),
-    faceCameraMode_(FC_ROTATE_XYZ),
-    geometry_(new Geometry(context)),
-    vertexBuffer_(new VertexBuffer(context_)),
-    indexBuffer_(new IndexBuffer(context_)),
-    bufferSizeDirty_(true),
-    bufferDirty_(true),
-    forceUpdate_(false),
-    geometryTypeUpdate_(false),
-    sortThisFrame_(false),
-    hasOrthoCamera_(false),
-    sortFrameNumber_(0),
-    previousOffset_(Vector3::ZERO)
-{
-    geometry_->SetVertexBuffer(0, vertexBuffer_);
-    geometry_->SetIndexBuffer(indexBuffer_);
-
-    batches_.Resize(1);
-    batches_[0].geometry_ = geometry_;
-    batches_[0].geometryType_ = GEOM_BILLBOARD;
-    batches_[0].worldTransform_ = &transforms_[0];
-}
-
-BillboardSet::~BillboardSet()
-{
-}
-
-void BillboardSet::RegisterObject(Context* context)
-{
-    context->RegisterFactory<BillboardSet>(GEOMETRY_CATEGORY);
-
-    ATOMIC_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()),
-        AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Relative Position", IsRelative, SetRelative, bool, true, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Relative Scale", IsScaled, SetScaled, bool, true, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Sort By Distance", IsSorted, SetSorted, bool, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Fixed Screen Size", IsFixedScreenSize, SetFixedScreenSize, bool, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
-    ATOMIC_ENUM_ACCESSOR_ATTRIBUTE("Face Camera Mode", GetFaceCameraMode, SetFaceCameraMode, FaceCameraMode, faceCameraModeNames, FC_ROTATE_XYZ, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
-    ATOMIC_COPY_BASE_ATTRIBUTES(Drawable);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Billboards", GetBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector,
-        AM_FILE);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Network Billboards", GetNetBillboardsAttr, SetNetBillboardsAttr, PODVector<unsigned char>,
-        Variant::emptyBuffer, AM_NET | AM_NOEDIT);
-}
-
-void BillboardSet::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
-{
-    // If no billboard-level testing, use the Drawable test
-    if (query.level_ < RAY_TRIANGLE)
-    {
-        Drawable::ProcessRayQuery(query, results);
-        return;
-    }
-
-    // Check ray hit distance to AABB before proceeding with billboard-level tests
-    if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_)
-        return;
-
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-    Matrix3x4 billboardTransform = relative_ ? worldTransform : Matrix3x4::IDENTITY;
-    Vector3 billboardScale = scaled_ ? worldTransform.Scale() : Vector3::ONE;
-
-    for (unsigned i = 0; i < billboards_.Size(); ++i)
-    {
-        if (!billboards_[i].enabled_)
-            continue;
-
-        // Approximate the billboards as spheres for raycasting
-        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);
-
-        float distance = query.ray_.HitDistance(billboardSphere);
-        if (distance < query.maxDistance_)
-        {
-            // If the code reaches here then we have a hit
-            RayQueryResult result;
-            result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
-            result.normal_ = -query.ray_.direction_;
-            result.distance_ = distance;
-            result.drawable_ = this;
-            result.node_ = node_;
-            result.subObject_ = i;
-            results.Push(result);
-        }
-    }
-}
-
-void BillboardSet::UpdateBatches(const FrameInfo& frame)
-{
-    // If beginning a new frame, assume no sorting first
-    if (frame.frameNumber_ != sortFrameNumber_)
-    {
-        sortThisFrame_ = false;
-        sortFrameNumber_ = frame.frameNumber_;
-    }
-
-    Vector3 worldPos = node_->GetWorldPosition();
-    Vector3 offset = (worldPos - frame.camera_->GetNode()->GetWorldPosition());
-    // Sort if position relative to camera has changed
-    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());
-
-    // Calculate scaled distance for animation LOD
-    float scale = GetWorldBoundingBox().Size().DotProduct(DOT_SCALE);
-    // If there are no billboards, the size becomes zero, and LOD'ed updates no longer happen. Disable LOD in that case
-    if (scale > M_EPSILON)
-        lodDistance_ = frame.camera_->GetLodDistance(distance_, scale, lodBias_);
-    else
-        lodDistance_ = 0.0f;
-
-    batches_[0].distance_ = distance_;
-    batches_[0].numWorldTransforms_ = 2;
-    // Billboard positioning
-    transforms_[0] = relative_ ? node_->GetWorldTransform() : Matrix3x4::IDENTITY;
-    // Billboard rotation
-    transforms_[1] = Matrix3x4(Vector3::ZERO, faceCameraMode_ != FC_NONE ? frame.camera_->GetFaceCameraRotation(
-        node_->GetWorldPosition(), node_->GetWorldRotation(), faceCameraMode_) : node_->GetWorldRotation(), Vector3::ONE);
-}
-
-void BillboardSet::UpdateGeometry(const FrameInfo& 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 (faceCameraMode_ != FC_NONE)
-    {
-        transforms_[1] = Matrix3x4(Vector3::ZERO, frame.camera_->GetFaceCameraRotation(node_->GetWorldPosition(),
-            node_->GetWorldRotation(), faceCameraMode_), Vector3::ONE);
-    }
-
-    if (bufferSizeDirty_ || indexBuffer_->IsDataLost())
-        UpdateBufferSize();
-
-    if (bufferDirty_ || sortThisFrame_ || vertexBuffer_->IsDataLost())
-        UpdateVertexBuffer(frame);
-}
-
-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 (bufferDirty_ || bufferSizeDirty_ || vertexBuffer_->IsDataLost() || indexBuffer_->IsDataLost() || sortThisFrame_ ||
-        faceCameraMode_ != FC_NONE || fixedScreenSize_)
-        return UPDATE_MAIN_THREAD;
-    else
-        return UPDATE_NONE;
-}
-
-void BillboardSet::SetMaterial(Material* material)
-{
-    batches_[0].material_ = material;
-    MarkNetworkUpdate();
-}
-
-void BillboardSet::SetNumBillboards(unsigned num)
-{
-    // Prevent negative value being assigned from the editor
-    if (num > M_MAX_INT)
-        num = 0;
-    if (num > MAX_BILLBOARDS)
-        num = MAX_BILLBOARDS;
-
-    unsigned oldNum = billboards_.Size();
-    if (num == oldNum)
-        return;
-
-    billboards_.Resize(num);
-
-    // Set default values to new billboards
-    for (unsigned i = oldNum; i < num; ++i)
-    {
-        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;
-    Commit();
-}
-
-void BillboardSet::SetRelative(bool enable)
-{
-    relative_ = enable;
-    Commit();
-}
-
-void BillboardSet::SetScaled(bool enable)
-{
-    scaled_ = enable;
-    Commit();
-}
-
-void BillboardSet::SetSorted(bool enable)
-{
-    sorted_ = enable;
-    Commit();
-}
-
-void BillboardSet::SetFixedScreenSize(bool enable)
-{
-    fixedScreenSize_ = enable;
-    Commit();
-}
-
-void BillboardSet::SetFaceCameraMode(FaceCameraMode mode)
-{
-    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)
-{
-    animationLodBias_ = Max(bias, 0.0f);
-    MarkNetworkUpdate();
-}
-
-void BillboardSet::Commit()
-{
-    MarkPositionsDirty();
-    MarkNetworkUpdate();
-}
-
-Material* BillboardSet::GetMaterial() const
-{
-    return batches_[0].material_;
-}
-
-Billboard* BillboardSet::GetBillboard(unsigned index)
-{
-    return index < billboards_.Size() ? &billboards_[index] : (Billboard*)0;
-}
-
-void BillboardSet::SetMaterialAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    SetMaterial(cache->GetResource<Material>(value.name_));
-}
-
-void BillboardSet::SetBillboardsAttr(const VariantVector& value)
-{
-    unsigned index = 0;
-    unsigned numBillboards = index < value.Size() ? value[index++].GetUInt() : 0;
-    SetNumBillboards(numBillboards);
-
-    // Dealing with old billboard format
-    if (value.Size() == billboards_.Size() * 6 + 1)
-    {
-        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();
-}
-
-void BillboardSet::SetNetBillboardsAttr(const PODVector<unsigned char>& value)
-{
-    MemoryBuffer buf(value);
-    unsigned numBillboards = buf.ReadVLE();
-    SetNumBillboards(numBillboards);
-
-    for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
-    {
-        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();
-}
-
-ResourceRef BillboardSet::GetMaterialAttr() const
-{
-    return GetResourceRef(batches_[0].material_, Material::GetTypeStatic());
-}
-
-VariantVector BillboardSet::GetBillboardsAttr() const
-{
-    VariantVector ret;
-    ret.Reserve(billboards_.Size() * 7 + 1);
-    ret.Push(billboards_.Size());
-
-    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
-    {
-        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;
-}
-
-const PODVector<unsigned char>& BillboardSet::GetNetBillboardsAttr() const
-{
-    attrBuffer_.Clear();
-    attrBuffer_.WriteVLE(billboards_.Size());
-
-    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
-    {
-        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();
-}
-
-void BillboardSet::OnWorldBoundingBoxUpdate()
-{
-    unsigned enabledBillboards = 0;
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-    Matrix3x4 billboardTransform = relative_ ? worldTransform : Matrix3x4::IDENTITY;
-    Vector3 billboardScale = scaled_ ? worldTransform.Scale() : Vector3::ONE;
-    BoundingBox worldBox;
-
-    for (unsigned i = 0; i < billboards_.Size(); ++i)
-    {
-        if (!billboards_[i].enabled_)
-            continue;
-
-        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;
-        worldBox.Merge(BoundingBox(center - edge, center + edge));
-
-        ++enabledBillboards;
-    }
-
-    // Always merge the node's own position to ensure particle emitter updates continue when the relative mode is switched
-    worldBox.Merge(node_->GetWorldPosition());
-
-    worldBoundingBox_ = worldBox;
-}
-
-void BillboardSet::UpdateBufferSize()
-{
-    unsigned numBillboards = billboards_.Size();
-
-    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)
-        indexBuffer_->SetSize(numBillboards * 6, false);
-
-    bufferSizeDirty_ = false;
-    bufferDirty_ = true;
-    forceUpdate_ = true;
-
-    if (!numBillboards)
-        return;
-
-    // Indices do not change for a given billboard capacity
-    unsigned short* dest = (unsigned short*)indexBuffer_->Lock(0, numBillboards * 6, true);
-    if (!dest)
-        return;
-
-    unsigned vertexIndex = 0;
-    while (numBillboards--)
-    {
-        dest[0] = (unsigned short)vertexIndex;
-        dest[1] = (unsigned short)(vertexIndex + 1);
-        dest[2] = (unsigned short)(vertexIndex + 2);
-        dest[3] = (unsigned short)(vertexIndex + 2);
-        dest[4] = (unsigned short)(vertexIndex + 3);
-        dest[5] = (unsigned short)vertexIndex;
-
-        dest += 6;
-        vertexIndex += 4;
-    }
-
-    indexBuffer_->Unlock();
-    indexBuffer_->ClearDataLost();
-}
-
-void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
-{
-    // If using animation LOD, accumulate time and see if it is time to update
-    if (animationLodBias_ > 0.0f && lodDistance_ > 0.0f)
-    {
-        animationLodTimer_ += animationLodBias_ * frame.timeStep_ * ANIMATION_LOD_BASESCALE;
-        if (animationLodTimer_ >= lodDistance_)
-            animationLodTimer_ = fmodf(animationLodTimer_, lodDistance_);
-        else
-        {
-            // No LOD if immediate update forced
-            if (!forceUpdate_)
-                return;
-        }
-    }
-
-    unsigned numBillboards = billboards_.Size();
-    unsigned enabledBillboards = 0;
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-    Matrix3x4 billboardTransform = relative_ ? worldTransform : Matrix3x4::IDENTITY;
-    Vector3 billboardScale = scaled_ ? worldTransform.Scale() : Vector3::ONE;
-
-    // First check number of enabled billboards
-    for (unsigned i = 0; i < numBillboards; ++i)
-    {
-        if (billboards_[i].enabled_)
-            ++enabledBillboards;
-    }
-
-    sortedBillboards_.Resize(enabledBillboards);
-    unsigned index = 0;
-
-    // Then set initial sort order and distances
-    for (unsigned i = 0; i < numBillboards; ++i)
-    {
-        Billboard& billboard = billboards_[i];
-        if (billboard.enabled_)
-        {
-            sortedBillboards_[index++] = &billboard;
-            if (sorted_)
-                billboard.sortDistance_ = frame.camera_->GetDistanceSquared(billboardTransform * billboards_[i].position_);
-        }
-    }
-
-    batches_[0].geometry_->SetDrawRange(TRIANGLE_LIST, 0, enabledBillboards * 6, false);
-
-    bufferDirty_ = false;
-    forceUpdate_ = false;
-    if (!enabledBillboards)
-        return;
-
-    if (sorted_)
-    {
-        Sort(sortedBillboards_.Begin(), sortedBillboards_.End(), CompareBillboards);
-        Vector3 worldPos = node_->GetWorldPosition();
-        // Store the "last sorted position" now
-        previousOffset_ = (worldPos - frame.camera_->GetNode()->GetWorldPosition());
-    }
-
-    float* dest = (float*)vertexBuffer_->Lock(0, enabledBillboards * 4, true);
-    if (!dest)
-        return;
-
-    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
-    {
-        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_->ClearDataLost();
-}
-
-void BillboardSet::MarkPositionsDirty()
-{
-    Drawable::OnMarkedDirty(node_);
-    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;
-}
-
-}

+ 0 - 241
Source/Atomic/Atomic3D/BillboardSet.h

@@ -1,241 +0,0 @@
-//
-// 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 "../Graphics/Drawable.h"
-#include "../IO/VectorBuffer.h"
-#include "../Math/Color.h"
-#include "../Math/Matrix3x4.h"
-#include "../Math/Rect.h"
-
-namespace Atomic
-{
-
-class IndexBuffer;
-class VertexBuffer;
-
-/// One billboard in the billboard set.
-class ATOMIC_API Billboard : public RefCounted
-{
-    friend class BillboardSet;
-    friend class ParticleEmitter;
-    ATOMIC_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; }
-
-    const Vector3& GetDirection() { return direction_; }
-    void SetRotation(const Vector3& direction) { direction_ = direction; }
-
-    bool IsEnabled() { return enabled_; }
-    void SetEnabled(bool enabled) { enabled_ = enabled; }
-
-    float GetSortDistance() { return sortDistance_; }
-    void SetSortDistance(float sortDistance) { sortDistance_ = sortDistance; }
-
-private:
-    /// Position.
-    Vector3 position_;
-    /// Two-dimensional size. If BillboardSet has fixed screen size enabled, this is measured in pixels instead of world units.
-    Vector2 size_;
-    /// UV coordinates.
-    Rect uv_;
-    /// Color.
-    Color color_;
-    /// Rotation.
-    float rotation_;
-    /// Direction (For direction based billboard only).
-    Vector3 direction_;
-    /// Enabled flag.
-    bool enabled_;
-    /// Sort distance. Used internally.
-    float sortDistance_;
-    /// Scale factor for fixed screen size mode. Used internally.
-    float screenScaleFactor_;
-};
-
-
-static const unsigned MAX_BILLBOARDS = 65536 / 4;
-
-/// %Billboard component.
-class ATOMIC_API BillboardSet : public Drawable
-{
-    ATOMIC_OBJECT(BillboardSet, Drawable);
-
-public:
-    /// Construct.
-    BillboardSet(Context* context);
-    /// Destruct.
-    virtual ~BillboardSet();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-
-    /// Process octree raycast. May be called from a worker thread.
-    virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
-    /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
-    virtual void UpdateBatches(const FrameInfo& frame);
-    /// Prepare geometry for rendering. Called from a worker thread if possible (no GPU update.)
-    virtual void UpdateGeometry(const FrameInfo& frame);
-    /// Return whether a geometry update is necessary, and if it can happen in a worker thread.
-    virtual UpdateGeometryType GetUpdateGeometryType();
-
-    /// Set material.
-    void SetMaterial(Material* material);
-    /// Set number of billboards.
-    void SetNumBillboards(unsigned num);
-    /// Set whether billboards are relative to the scene node. Default true.
-    void SetRelative(bool enable);
-    /// Set whether scene node scale affects billboards' size. Default true.
-    void SetScaled(bool enable);
-    /// Set whether billboards are sorted by distance. Default false.
-    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.)
-    void SetFaceCameraMode(FaceCameraMode mode);
-    /// Set animation LOD bias.
-    void SetAnimationLodBias(float bias);
-    /// Mark for bounding box and vertex buffer update. Call after modifying the billboards.
-    void Commit();
-
-    /// Return material.
-    Material* GetMaterial() const;
-
-    /// Return number of billboards.
-    unsigned GetNumBillboards() const { return billboards_.Size(); }
-
-    /// Return all billboards.
-    PODVector<Billboard>& GetBillboards() { return billboards_; }
-
-    /// Return billboard by index.
-    Billboard* GetBillboard(unsigned index);
-
-    /// Return whether billboards are relative to the scene node.
-    bool IsRelative() const { return relative_; }
-
-    /// Return whether scene node scale affects billboards' size.
-    bool IsScaled() const { return scaled_; }
-
-    /// Return whether billboards are 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.
-    FaceCameraMode GetFaceCameraMode() const { return faceCameraMode_; }
-
-    /// Return animation LOD bias.
-    float GetAnimationLodBias() const { return animationLodBias_; }
-
-    /// Set material attribute.
-    void SetMaterialAttr(const ResourceRef& value);
-    /// Set billboards attribute.
-    void SetBillboardsAttr(const VariantVector& value);
-    /// Set billboards attribute for network replication.
-    void SetNetBillboardsAttr(const PODVector<unsigned char>& value);
-    /// Return material attribute.
-    ResourceRef GetMaterialAttr() const;
-    /// Return billboards attribute.
-    VariantVector GetBillboardsAttr() const;
-    /// Return billboards attribute for network replication.
-    const PODVector<unsigned char>& GetNetBillboardsAttr() const;
-
-protected:
-    /// Recalculate the world-space bounding box.
-    virtual void OnWorldBoundingBoxUpdate();
-    /// Mark billboard vertex buffer to need an update.
-    void MarkPositionsDirty();
-
-    /// Billboards.
-    PODVector<Billboard> billboards_;
-    /// Animation LOD bias.
-    float animationLodBias_;
-    /// Animation LOD timer.
-    float animationLodTimer_;
-    /// Billboards relative flag.
-    bool relative_;
-    /// Scale affects billboard scale flag.
-    bool scaled_;
-    /// Billboards sorted flag.
-    bool sorted_;
-    /// Billboards fixed screen size flag.
-    bool fixedScreenSize_;
-    /// Billboard rotation mode in relation to the camera.
-    FaceCameraMode faceCameraMode_;
-
-private:
-    /// Resize billboard vertex and index buffers.
-    void UpdateBufferSize();
-    /// Rewrite billboard vertex buffer.
-    void UpdateVertexBuffer(const FrameInfo& frame);
-    /// Calculate billboard scale factors in fixed screen size mode.
-    void CalculateFixedScreenSize(const FrameInfo& frame);
-
-    /// Geometry.
-    SharedPtr<Geometry> geometry_;
-    /// Vertex buffer.
-    SharedPtr<VertexBuffer> vertexBuffer_;
-    /// Index buffer.
-    SharedPtr<IndexBuffer> indexBuffer_;
-    /// Transform matrices for position and billboard orientation.
-    Matrix3x4 transforms_[2];
-    /// Buffers need resize flag.
-    bool bufferSizeDirty_;
-    /// Vertex buffer needs rewrite flag.
-    bool bufferDirty_;
-    /// Force update flag (ignore animation LOD momentarily.)
-    bool forceUpdate_;
-    /// Update billboard geometry type
-    bool geometryTypeUpdate_;
-    /// Sorting flag. Triggers a vertex buffer rewrite for each view this billboard set is rendered from.
-    bool sortThisFrame_;
-    /// Whether was last rendered from an ortho camera.
-    bool hasOrthoCamera_;
-    /// Frame number on which was last sorted.
-    unsigned sortFrameNumber_;
-    /// Previous offset to camera for determining whether sorting is necessary.
-    Vector3 previousOffset_;
-    /// Billboard pointers for sorting.
-    Vector<Billboard*> sortedBillboards_;
-    /// Attribute buffer for network replication.
-    mutable VectorBuffer attrBuffer_;
-};
-
-}

+ 0 - 537
Source/Atomic/Atomic3D/CustomGeometry.cpp

@@ -1,537 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/Batch.h"
-#include "../Graphics/Camera.h"
-#include "../Graphics/CustomGeometry.h"
-#include "../Graphics/Geometry.h"
-#include "../Graphics/Material.h"
-#include "../Graphics/OcclusionBuffer.h"
-#include "../Graphics/OctreeQuery.h"
-#include "../Graphics/VertexBuffer.h"
-#include "../IO/Log.h"
-#include "../IO/MemoryBuffer.h"
-#include "../Resource/ResourceCache.h"
-#include "../Scene/Node.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-
-CustomGeometry::CustomGeometry(Context* context) :
-    Drawable(context, DRAWABLE_GEOMETRY),
-    vertexBuffer_(new VertexBuffer(context)),
-    elementMask_(MASK_POSITION),
-    geometryIndex_(0),
-    materialsAttr_(Material::GetTypeStatic()),
-    dynamic_(false)
-{
-    vertexBuffer_->SetShadowed(true);
-    SetNumGeometries(1);
-}
-
-CustomGeometry::~CustomGeometry()
-{
-}
-
-void CustomGeometry::RegisterObject(Context* context)
-{
-    context->RegisterFactory<CustomGeometry>(GEOMETRY_CATEGORY);
-
-    ATOMIC_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Dynamic Vertex Buffer", bool, dynamic_, false, AM_DEFAULT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Geometry Data", GetGeometryDataAttr, SetGeometryDataAttr, PODVector<unsigned char>,
-        Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Materials", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()),
-        AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Is Occluder", bool, occluder_, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
-    ATOMIC_COPY_BASE_ATTRIBUTES(Drawable);
-}
-
-void CustomGeometry::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
-{
-    RayQueryLevel level = query.level_;
-
-    switch (level)
-    {
-    case RAY_AABB:
-        Drawable::ProcessRayQuery(query, results);
-        break;
-
-    case RAY_OBB:
-    case RAY_TRIANGLE:
-        {
-            Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-            Ray localRay = query.ray_.Transformed(inverse);
-            float distance = localRay.HitDistance(boundingBox_);
-            Vector3 normal = -query.ray_.direction_;
-
-            if (level == RAY_TRIANGLE && distance < query.maxDistance_)
-            {
-                distance = M_INFINITY;
-
-                for (unsigned i = 0; i < batches_.Size(); ++i)
-                {
-                    Geometry* geometry = batches_[i].geometry_;
-                    if (geometry)
-                    {
-                        Vector3 geometryNormal;
-                        float geometryDistance = geometry->GetHitDistance(localRay, &geometryNormal);
-                        if (geometryDistance < query.maxDistance_ && geometryDistance < distance)
-                        {
-                            distance = geometryDistance;
-                            normal = (node_->GetWorldTransform() * Vector4(geometryNormal, 0.0f)).Normalized();
-                        }
-                    }
-                }
-            }
-
-            if (distance < query.maxDistance_)
-            {
-                RayQueryResult result;
-                result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
-                result.normal_ = normal;
-                result.distance_ = distance;
-                result.drawable_ = this;
-                result.node_ = node_;
-                result.subObject_ = M_MAX_UNSIGNED;
-                results.Push(result);
-            }
-        }
-        break;
-
-    case RAY_TRIANGLE_UV:
-        ATOMIC_LOGWARNING("RAY_TRIANGLE_UV query level is not supported for CustomGeometry component");
-        break;
-    }
-}
-
-Geometry* CustomGeometry::GetLodGeometry(unsigned batchIndex, unsigned level)
-{
-    return batchIndex < geometries_.Size() ? geometries_[batchIndex] : (Geometry*)0;
-}
-
-unsigned CustomGeometry::GetNumOccluderTriangles()
-{
-    unsigned triangles = 0;
-
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-    {
-        Geometry* geometry = GetLodGeometry(i, 0);
-        if (!geometry)
-            continue;
-
-        // Check that the material is suitable for occlusion (default material always is)
-        Material* mat = batches_[i].material_;
-        if (mat && !mat->GetOcclusion())
-            continue;
-
-        triangles += geometry->GetVertexCount() / 3;
-    }
-
-    return triangles;
-}
-
-bool CustomGeometry::DrawOcclusion(OcclusionBuffer* buffer)
-{
-    bool success = true;
-
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-    {
-        Geometry* geometry = GetLodGeometry(i, 0);
-        if (!geometry)
-            continue;
-
-        // Check that the material is suitable for occlusion (default material always is) and set culling mode
-        Material* material = batches_[i].material_;
-        if (material)
-        {
-            if (!material->GetOcclusion())
-                continue;
-            buffer->SetCullMode(material->GetCullMode());
-        }
-        else
-            buffer->SetCullMode(CULL_CCW);
-
-        const unsigned char* vertexData;
-        unsigned vertexSize;
-        const unsigned char* indexData;
-        unsigned indexSize;
-        const PODVector<VertexElement>* elements;
-
-        geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elements);
-        // Check for valid geometry data
-        if (!vertexData || !elements || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0)
-            continue;
-
-        // Draw and check for running out of triangles
-        success = buffer->AddTriangles(node_->GetWorldTransform(), vertexData, vertexSize, geometry->GetVertexStart(),
-            geometry->GetVertexCount());
-
-        if (!success)
-            break;
-    }
-
-    return success;
-}
-
-void CustomGeometry::Clear()
-{
-    elementMask_ = MASK_POSITION;
-    batches_.Clear();
-    geometries_.Clear();
-    primitiveTypes_.Clear();
-    vertices_.Clear();
-}
-
-void CustomGeometry::SetNumGeometries(unsigned num)
-{
-    batches_.Resize(num);
-    geometries_.Resize(num);
-    primitiveTypes_.Resize(num);
-    vertices_.Resize(num);
-
-    for (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        if (!geometries_[i])
-            geometries_[i] = new Geometry(context_);
-
-        batches_[i].geometry_ = geometries_[i];
-    }
-}
-
-void CustomGeometry::SetDynamic(bool enable)
-{
-    dynamic_ = enable;
-
-    MarkNetworkUpdate();
-}
-
-void CustomGeometry::BeginGeometry(unsigned index, PrimitiveType type)
-{
-    if (index > geometries_.Size())
-    {
-        ATOMIC_LOGERROR("Geometry index out of bounds");
-        return;
-    }
-
-    geometryIndex_ = index;
-    primitiveTypes_[index] = type;
-    vertices_[index].Clear();
-
-    // If beginning the first geometry, reset the element mask
-    if (!index)
-        elementMask_ = MASK_POSITION;
-}
-
-void CustomGeometry::DefineVertex(const Vector3& position)
-{
-    if (vertices_.Size() < geometryIndex_)
-        return;
-
-    vertices_[geometryIndex_].Resize(vertices_[geometryIndex_].Size() + 1);
-    vertices_[geometryIndex_].Back().position_ = position;
-}
-
-void CustomGeometry::DefineNormal(const Vector3& normal)
-{
-    if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
-        return;
-
-    vertices_[geometryIndex_].Back().normal_ = normal;
-    elementMask_ |= MASK_NORMAL;
-}
-
-void CustomGeometry::DefineColor(const Color& color)
-{
-    if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
-        return;
-
-    vertices_[geometryIndex_].Back().color_ = color.ToUInt();
-    elementMask_ |= MASK_COLOR;
-}
-
-void CustomGeometry::DefineTexCoord(const Vector2& texCoord)
-{
-    if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
-        return;
-
-    vertices_[geometryIndex_].Back().texCoord_ = texCoord;
-    elementMask_ |= MASK_TEXCOORD1;
-}
-
-void CustomGeometry::DefineTangent(const Vector4& tangent)
-{
-    if (vertices_.Size() < geometryIndex_ || vertices_[geometryIndex_].Empty())
-        return;
-
-    vertices_[geometryIndex_].Back().tangent_ = tangent;
-    elementMask_ |= MASK_TANGENT;
-}
-
-void CustomGeometry::DefineGeometry(unsigned index, PrimitiveType type, unsigned numVertices, bool hasNormals, bool hasColors,
-    bool hasTexCoords, bool hasTangents)
-{
-    if (index > geometries_.Size())
-    {
-        ATOMIC_LOGERROR("Geometry index out of bounds");
-        return;
-    }
-
-    geometryIndex_ = index;
-    primitiveTypes_[index] = type;
-    vertices_[index].Resize(numVertices);
-
-    // If defining the first geometry, reset the element mask
-    if (!index)
-        elementMask_ = MASK_POSITION;
-    if (hasNormals)
-        elementMask_ |= MASK_NORMAL;
-    if (hasColors)
-        elementMask_ |= MASK_COLOR;
-    if (hasTexCoords)
-        elementMask_ |= MASK_TEXCOORD1;
-    if (hasTangents)
-        elementMask_ |= MASK_TANGENT;
-}
-
-void CustomGeometry::Commit()
-{
-    ATOMIC_PROFILE(CommitCustomGeometry);
-
-    unsigned totalVertices = 0;
-    boundingBox_.Clear();
-
-    for (unsigned i = 0; i < vertices_.Size(); ++i)
-    {
-        totalVertices += vertices_[i].Size();
-
-        for (unsigned j = 0; j < vertices_[i].Size(); ++j)
-            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
-    if (vertexBuffer_->GetVertexCount() != totalVertices || vertexBuffer_->GetElementMask() != elementMask_ ||
-        vertexBuffer_->IsDynamic() != dynamic_)
-        vertexBuffer_->SetSize(totalVertices, elementMask_, dynamic_);
-
-    if (totalVertices)
-    {
-        unsigned char* dest = (unsigned char*)vertexBuffer_->Lock(0, totalVertices, true);
-        if (dest)
-        {
-            unsigned vertexStart = 0;
-
-            for (unsigned i = 0; i < vertices_.Size(); ++i)
-            {
-                unsigned vertexCount = 0;
-
-                for (unsigned j = 0; j < vertices_[i].Size(); ++j)
-                {
-                    *((Vector3*)dest) = vertices_[i][j].position_;
-                    dest += sizeof(Vector3);
-
-                    if (elementMask_ & MASK_NORMAL)
-                    {
-                        *((Vector3*)dest) = vertices_[i][j].normal_;
-                        dest += sizeof(Vector3);
-                    }
-                    if (elementMask_ & MASK_COLOR)
-                    {
-                        *((unsigned*)dest) = vertices_[i][j].color_;
-                        dest += sizeof(unsigned);
-                    }
-                    if (elementMask_ & MASK_TEXCOORD1)
-                    {
-                        *((Vector2*)dest) = vertices_[i][j].texCoord_;
-                        dest += sizeof(Vector2);
-                    }
-                    if (elementMask_ & MASK_TANGENT)
-                    {
-                        *((Vector4*)dest) = vertices_[i][j].tangent_;
-                        dest += sizeof(Vector4);
-                    }
-
-                    ++vertexCount;
-                }
-
-                geometries_[i]->SetVertexBuffer(0, vertexBuffer_);
-                geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, vertexStart, vertexCount);
-                vertexStart += vertexCount;
-            }
-
-            vertexBuffer_->Unlock();
-        }
-        else
-            ATOMIC_LOGERROR("Failed to lock custom geometry vertex buffer");
-    }
-    else
-    {
-        for (unsigned i = 0; i < geometries_.Size(); ++i)
-        {
-            geometries_[i]->SetVertexBuffer(0, vertexBuffer_);
-            geometries_[i]->SetDrawRange(primitiveTypes_[i], 0, 0, 0, 0);
-        }
-    }
-
-    vertexBuffer_->ClearDataLost();
-}
-
-void CustomGeometry::SetMaterial(Material* material)
-{
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-        batches_[i].material_ = material;
-
-    MarkNetworkUpdate();
-}
-
-bool CustomGeometry::SetMaterial(unsigned index, Material* material)
-{
-    if (index >= batches_.Size())
-    {
-        ATOMIC_LOGERROR("Material index out of bounds");
-        return false;
-    }
-
-    batches_[index].material_ = material;
-    MarkNetworkUpdate();
-    return true;
-}
-
-unsigned CustomGeometry::GetNumVertices(unsigned index) const
-{
-    return index < vertices_.Size() ? vertices_[index].Size() : 0;
-}
-
-Material* CustomGeometry::GetMaterial(unsigned index) const
-{
-    return index < batches_.Size() ? batches_[index].material_ : (Material*)0;
-}
-
-CustomGeometryVertex* CustomGeometry::GetVertex(unsigned geometryIndex, unsigned vertexNum)
-{
-    return (geometryIndex < vertices_.Size() && vertexNum < vertices_[geometryIndex].Size()) ?
-           &vertices_[geometryIndex][vertexNum] : (CustomGeometryVertex*)0;
-}
-
-void CustomGeometry::SetGeometryDataAttr(const PODVector<unsigned char>& value)
-{
-    if (value.Empty())
-        return;
-
-    MemoryBuffer buffer(value);
-
-    SetNumGeometries(buffer.ReadVLE());
-    elementMask_ = buffer.ReadUInt();
-
-    for (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        unsigned numVertices = buffer.ReadVLE();
-        vertices_[i].Resize(numVertices);
-        primitiveTypes_[i] = (PrimitiveType)buffer.ReadUByte();
-
-        for (unsigned j = 0; j < numVertices; ++j)
-        {
-            if (elementMask_ & MASK_POSITION)
-                vertices_[i][j].position_ = buffer.ReadVector3();
-            if (elementMask_ & MASK_NORMAL)
-                vertices_[i][j].normal_ = buffer.ReadVector3();
-            if (elementMask_ & MASK_COLOR)
-                vertices_[i][j].color_ = buffer.ReadUInt();
-            if (elementMask_ & MASK_TEXCOORD1)
-                vertices_[i][j].texCoord_ = buffer.ReadVector2();
-            if (elementMask_ & MASK_TANGENT)
-                vertices_[i][j].tangent_ = buffer.ReadVector4();
-        }
-    }
-
-    Commit();
-}
-
-void CustomGeometry::SetMaterialsAttr(const ResourceRefList& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    for (unsigned i = 0; i < value.names_.Size(); ++i)
-        SetMaterial(i, cache->GetResource<Material>(value.names_[i]));
-}
-
-PODVector<unsigned char> CustomGeometry::GetGeometryDataAttr() const
-{
-    VectorBuffer ret;
-
-    ret.WriteVLE(geometries_.Size());
-    ret.WriteUInt(elementMask_);
-
-    for (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        unsigned numVertices = vertices_[i].Size();
-        ret.WriteVLE(numVertices);
-        ret.WriteUByte(primitiveTypes_[i]);
-
-        for (unsigned j = 0; j < numVertices; ++j)
-        {
-            if (elementMask_ & MASK_POSITION)
-                ret.WriteVector3(vertices_[i][j].position_);
-            if (elementMask_ & MASK_NORMAL)
-                ret.WriteVector3(vertices_[i][j].normal_);
-            if (elementMask_ & MASK_COLOR)
-                ret.WriteUInt(vertices_[i][j].color_);
-            if (elementMask_ & MASK_TEXCOORD1)
-                ret.WriteVector2(vertices_[i][j].texCoord_);
-            if (elementMask_ & MASK_TANGENT)
-                ret.WriteVector4(vertices_[i][j].tangent_);
-        }
-    }
-
-    return ret.GetBuffer();
-}
-
-const ResourceRefList& CustomGeometry::GetMaterialsAttr() const
-{
-    materialsAttr_.names_.Resize(batches_.Size());
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-        materialsAttr_.names_[i] = GetResourceName(batches_[i].material_);
-
-    return materialsAttr_;
-}
-
-void CustomGeometry::OnWorldBoundingBoxUpdate()
-{
-    worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
-}
-
-}

+ 0 - 148
Source/Atomic/Atomic3D/CustomGeometry.h

@@ -1,148 +0,0 @@
-//
-// 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 "../Graphics/Drawable.h"
-
-namespace Atomic
-{
-
-/// Custom geometry vertex.
-struct CustomGeometryVertex
-{
-    /// Position.
-    Vector3 position_;
-    /// Normal.
-    Vector3 normal_;
-    /// Color.
-    unsigned color_;
-    /// Texture coordinates.
-    Vector2 texCoord_;
-    /// Tangent.
-    Vector4 tangent_;
-};
-
-class VertexBuffer;
-
-/// Custom geometry component.
-class ATOMIC_API CustomGeometry : public Drawable
-{
-    ATOMIC_OBJECT(CustomGeometry, Drawable);
-
-public:
-    /// Construct.
-    CustomGeometry(Context* context);
-    /// Destruct.
-    virtual ~CustomGeometry();
-    /// Register object factory. Drawable must be registered first.
-    static void RegisterObject(Context* context);
-
-    /// Process octree raycast. May be called from a worker thread.
-    virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
-    /// Return the geometry for a specific LOD level.
-    virtual Geometry* GetLodGeometry(unsigned batchIndex, unsigned level);
-    /// Return number of occlusion geometry triangles.
-    virtual unsigned GetNumOccluderTriangles();
-    /// Draw to occlusion buffer. Return true if did not run out of triangles.
-    virtual bool DrawOcclusion(OcclusionBuffer* buffer);
-
-    /// Clear all geometries.
-    void Clear();
-    /// Set number of geometries.
-    void SetNumGeometries(unsigned num);
-    /// Set vertex buffer dynamic mode. A dynamic buffer should be faster to update frequently. Effective at the next Commit() call.
-    void SetDynamic(bool enable);
-    /// Begin defining a geometry. Clears existing vertices in that index.
-    void BeginGeometry(unsigned index, PrimitiveType type);
-    /// Define a vertex position. This begins a new vertex.
-    void DefineVertex(const Vector3& position);
-    /// Define a vertex normal.
-    void DefineNormal(const Vector3& normal);
-    /// Define a vertex color.
-    void DefineColor(const Color& color);
-    /// Define a vertex UV coordinate.
-    void DefineTexCoord(const Vector2& texCoord);
-    /// Define a vertex tangent.
-    void DefineTangent(const Vector4& tangent);
-    /// Set the primitive type, number of vertices and elements in a geometry, after which the vertices can be edited with GetVertex(). An alternative to BeginGeometry() / DefineVertex().
-    void DefineGeometry
-        (unsigned index, PrimitiveType type, unsigned numVertices, bool hasNormals, bool hasColors, bool hasTexCoords,
-            bool hasTangents);
-    /// Update vertex buffer and calculate the bounding box. Call after finishing defining geometry.
-    void Commit();
-    /// Set material on all geometries.
-    void SetMaterial(Material* material);
-    /// Set material on one geometry. Return true if successful.
-    bool SetMaterial(unsigned index, Material* material);
-
-    /// Return number of geometries.
-    unsigned GetNumGeometries() const { return geometries_.Size(); }
-
-    /// Return number of vertices in a geometry.
-    unsigned GetNumVertices(unsigned index) const;
-
-    /// Return whether vertex buffer dynamic mode is enabled.
-    bool IsDynamic() const { return dynamic_; }
-
-    /// Return material by geometry index.
-    Material* GetMaterial(unsigned index = 0) const;
-
-    /// Return all vertices. These can be edited; calling Commit() updates the vertex buffer.
-    Vector<PODVector<CustomGeometryVertex> >& GetVertices() { return vertices_; }
-
-    /// Return a vertex in a geometry for editing, or null if out of bounds. After the edits are finished, calling Commit() updates  the vertex buffer.
-    CustomGeometryVertex* GetVertex(unsigned geometryIndex, unsigned vertexNum);
-
-    /// Set geometry data attribute.
-    void SetGeometryDataAttr(const PODVector<unsigned char>& value);
-    /// Set materials attribute.
-    void SetMaterialsAttr(const ResourceRefList& value);
-    /// Return geometry data attribute.
-    PODVector<unsigned char> GetGeometryDataAttr() const;
-    /// Return materials attribute.
-    const ResourceRefList& GetMaterialsAttr() const;
-
-protected:
-    /// Recalculate the world-space bounding box.
-    virtual void OnWorldBoundingBoxUpdate();
-
-private:
-    /// Primitive type per geometry.
-    PODVector<PrimitiveType> primitiveTypes_;
-    /// Source vertices per geometry.
-    Vector<PODVector<CustomGeometryVertex> > vertices_;
-    /// All geometries.
-    Vector<SharedPtr<Geometry> > geometries_;
-    /// Vertex buffer.
-    SharedPtr<VertexBuffer> vertexBuffer_;
-    /// Element mask used so far.
-    unsigned elementMask_;
-    /// Current geometry being updated.
-    unsigned geometryIndex_;
-    /// Material list attribute.
-    mutable ResourceRefList materialsAttr_;
-    /// Vertex buffer dynamic flag.
-    bool dynamic_;
-};
-
-}

+ 0 - 1170
Source/Atomic/Atomic3D/DecalSet.cpp

@@ -1,1170 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/AnimatedModel.h"
-#include "../Graphics/Batch.h"
-#include "../Graphics/Camera.h"
-#include "../Graphics/DecalSet.h"
-#include "../Graphics/Geometry.h"
-#include "../Graphics/Graphics.h"
-#include "../Graphics/IndexBuffer.h"
-#include "../Graphics/Material.h"
-#include "../Graphics/Tangent.h"
-#include "../Graphics/VertexBuffer.h"
-#include "../IO/Log.h"
-#include "../IO/MemoryBuffer.h"
-#include "../Resource/ResourceCache.h"
-#include "../Scene/Scene.h"
-#include "../Scene/SceneEvents.h"
-
-#include "../DebugNew.h"
-
-#ifdef _MSC_VER
-#pragma warning(disable:6293)
-#endif
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-
-static const unsigned MIN_VERTICES = 4;
-static const unsigned MIN_INDICES = 6;
-static const unsigned MAX_VERTICES = 65536;
-static const unsigned DEFAULT_MAX_VERTICES = 512;
-static const unsigned DEFAULT_MAX_INDICES = 1024;
-static const unsigned STATIC_ELEMENT_MASK = MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT;
-static const unsigned SKINNED_ELEMENT_MASK = MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT | MASK_BLENDWEIGHTS |
-                                             MASK_BLENDINDICES;
-
-static DecalVertex ClipEdge(const DecalVertex& v0, const DecalVertex& v1, float d0, float d1, bool skinned)
-{
-    DecalVertex ret;
-    float t = d0 / (d0 - d1);
-
-    ret.position_ = v0.position_ + t * (v1.position_ - v0.position_);
-    ret.normal_ = v0.normal_ + t * (v1.normal_ - v0.normal_);
-    if (skinned)
-    {
-        if (*((unsigned*)v0.blendIndices_) != *((unsigned*)v1.blendIndices_))
-        {
-            // Blend weights and indices: if indices are different, choose the vertex nearer to the split plane
-            const DecalVertex& src = Abs(d0) < Abs(d1) ? v0 : v1;
-            for (unsigned i = 0; i < 4; ++i)
-            {
-                ret.blendWeights_[i] = src.blendWeights_[i];
-                ret.blendIndices_[i] = src.blendIndices_[i];
-            }
-        }
-        else
-        {
-            // If indices are same, can interpolate the weights
-            for (unsigned i = 0; i < 4; ++i)
-            {
-                ret.blendWeights_[i] = v0.blendWeights_[i] + t * (v1.blendWeights_[i] - v0.blendWeights_[i]);
-                ret.blendIndices_[i] = v0.blendIndices_[i];
-            }
-        }
-    }
-
-    return ret;
-}
-
-static void ClipPolygon(PODVector<DecalVertex>& dest, const PODVector<DecalVertex>& src, const Plane& plane, bool skinned)
-{
-    unsigned last = 0;
-    float lastDistance = 0.0f;
-    dest.Clear();
-
-    if (src.Empty())
-        return;
-
-    for (unsigned i = 0; i < src.Size(); ++i)
-    {
-        float distance = plane.Distance(src[i].position_);
-        if (distance >= 0.0f)
-        {
-            if (lastDistance < 0.0f)
-                dest.Push(ClipEdge(src[last], src[i], lastDistance, distance, skinned));
-
-            dest.Push(src[i]);
-        }
-        else
-        {
-            if (lastDistance >= 0.0f && i != 0)
-                dest.Push(ClipEdge(src[last], src[i], lastDistance, distance, skinned));
-        }
-
-        last = i;
-        lastDistance = distance;
-    }
-
-    // Recheck the distances of the last and first vertices and add the final clipped vertex if applicable
-    float distance = plane.Distance(src[0].position_);
-    if ((lastDistance < 0.0f && distance >= 0.0f) || (lastDistance >= 0.0f && distance < 0.0f))
-        dest.Push(ClipEdge(src[last], src[0], lastDistance, distance, skinned));
-}
-
-void Decal::AddVertex(const DecalVertex& vertex)
-{
-    for (unsigned i = 0; i < vertices_.Size(); ++i)
-    {
-        if (vertex.position_.Equals(vertices_[i].position_) && vertex.normal_.Equals(vertices_[i].normal_))
-        {
-            indices_.Push((unsigned short)i);
-            return;
-        }
-    }
-
-    unsigned short newIndex = (unsigned short)vertices_.Size();
-    vertices_.Push(vertex);
-    indices_.Push(newIndex);
-}
-
-void Decal::CalculateBoundingBox()
-{
-    boundingBox_.Clear();
-    for (unsigned i = 0; i < vertices_.Size(); ++i)
-        boundingBox_.Merge(vertices_[i].position_);
-}
-
-DecalSet::DecalSet(Context* context) :
-    Drawable(context, DRAWABLE_GEOMETRY),
-    geometry_(new Geometry(context)),
-    vertexBuffer_(new VertexBuffer(context_)),
-    indexBuffer_(new IndexBuffer(context_)),
-    numVertices_(0),
-    numIndices_(0),
-    maxVertices_(DEFAULT_MAX_VERTICES),
-    maxIndices_(DEFAULT_MAX_INDICES),
-    skinned_(false),
-    bufferSizeDirty_(true),
-    bufferDirty_(true),
-    boundingBoxDirty_(true),
-    skinningDirty_(false),
-    assignBonesPending_(false),
-    subscribed_(false)
-{
-    geometry_->SetIndexBuffer(indexBuffer_);
-
-    batches_.Resize(1);
-    batches_[0].geometry_ = geometry_;
-    batches_[0].geometryType_ = GEOM_STATIC_NOINSTANCING;
-}
-
-DecalSet::~DecalSet()
-{
-}
-
-void DecalSet::RegisterObject(Context* context)
-{
-    context->RegisterFactory<DecalSet>(GEOMETRY_CATEGORY);
-
-    ATOMIC_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()),
-        AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Max Vertices", GetMaxVertices, SetMaxVertices, unsigned, DEFAULT_MAX_VERTICES, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Max Indices", GetMaxIndices, SetMaxIndices, unsigned, DEFAULT_MAX_INDICES, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_COPY_BASE_ATTRIBUTES(Drawable);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Decals", GetDecalsAttr, SetDecalsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
-        AM_FILE | AM_NOEDIT);
-}
-
-void DecalSet::ApplyAttributes()
-{
-    if (assignBonesPending_)
-        AssignBoneNodes();
-}
-
-void DecalSet::OnSetEnabled()
-{
-    Drawable::OnSetEnabled();
-
-    UpdateEventSubscription(true);
-}
-
-void DecalSet::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
-{
-    // Do not return raycast hits
-}
-
-void DecalSet::UpdateBatches(const FrameInfo& frame)
-{
-    const BoundingBox& worldBoundingBox = GetWorldBoundingBox();
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-    distance_ = frame.camera_->GetDistance(worldBoundingBox.Center());
-
-    float scale = worldBoundingBox.Size().DotProduct(DOT_SCALE);
-    lodDistance_ = frame.camera_->GetLodDistance(distance_, scale, lodBias_);
-
-    batches_[0].distance_ = distance_;
-    if (!skinned_)
-        batches_[0].worldTransform_ = &worldTransform;
-}
-
-void DecalSet::UpdateGeometry(const FrameInfo& frame)
-{
-    if (bufferSizeDirty_)
-        UpdateBufferSize();
-
-    if (bufferDirty_ || vertexBuffer_->IsDataLost() || indexBuffer_->IsDataLost())
-        UpdateBuffers();
-
-    if (skinningDirty_)
-        UpdateSkinning();
-}
-
-UpdateGeometryType DecalSet::GetUpdateGeometryType()
-{
-    if (bufferDirty_ || bufferSizeDirty_ || vertexBuffer_->IsDataLost() || indexBuffer_->IsDataLost())
-        return UPDATE_MAIN_THREAD;
-    else if (skinningDirty_)
-        return UPDATE_WORKER_THREAD;
-    else
-        return UPDATE_NONE;
-}
-
-void DecalSet::SetMaterial(Material* material)
-{
-    batches_[0].material_ = material;
-    MarkNetworkUpdate();
-}
-
-void DecalSet::SetMaxVertices(unsigned num)
-{
-    // Never expand to 32 bit indices
-    num = (unsigned)Clamp(num, MIN_VERTICES, MAX_VERTICES);
-
-    if (num != maxVertices_)
-    {
-        bufferSizeDirty_ = true;
-        maxVertices_ = num;
-
-        while (decals_.Size() && numVertices_ > maxVertices_)
-            RemoveDecals(1);
-
-        MarkNetworkUpdate();
-    }
-}
-
-void DecalSet::SetMaxIndices(unsigned num)
-{
-    if (num < MIN_INDICES)
-        num = MIN_INDICES;
-
-    if (num != maxIndices_)
-    {
-        bufferSizeDirty_ = true;
-        maxIndices_ = num;
-
-        while (decals_.Size() && numIndices_ > maxIndices_)
-            RemoveDecals(1);
-
-        MarkNetworkUpdate();
-    }
-}
-
-bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Quaternion& worldRotation, float size,
-    float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV, float timeToLive, float normalCutoff,
-    unsigned subGeometry)
-{
-    ATOMIC_PROFILE(AddDecal);
-
-    // Do not add decals in headless mode
-    if (!node_ || !GetSubsystem<Graphics>())
-        return false;
-
-    if (!target || !target->GetNode())
-    {
-        ATOMIC_LOGERROR("Null target drawable for decal");
-        return false;
-    }
-
-    // Check for animated target and switch into skinned/static mode if necessary
-    AnimatedModel* animatedModel = dynamic_cast<AnimatedModel*>(target);
-    if ((animatedModel && !skinned_) || (!animatedModel && skinned_))
-    {
-        RemoveAllDecals();
-        skinned_ = animatedModel != 0;
-        bufferSizeDirty_ = true;
-    }
-
-    // Center the decal frustum on the world position
-    Vector3 adjustedWorldPosition = worldPosition - 0.5f * depth * (worldRotation * Vector3::FORWARD);
-    /// \todo target transform is not right if adding a decal to StaticModelGroup
-    Matrix3x4 targetTransform = target->GetNode()->GetWorldTransform().Inverse();
-
-    // For an animated model, adjust the decal position back to the bind pose
-    // To do this, need to find the bone the decal is colliding with
-    if (animatedModel)
-    {
-        Skeleton& skeleton = animatedModel->GetSkeleton();
-        unsigned numBones = skeleton.GetNumBones();
-        Bone* bestBone = 0;
-        float bestSize = 0.0f;
-
-        for (unsigned i = 0; i < numBones; ++i)
-        {
-            Bone* bone = skeleton.GetBone(i);
-            if (!bone->node_ || !bone->collisionMask_)
-                continue;
-
-            // Represent the decal as a sphere, try to find the biggest colliding bone
-            Sphere decalSphere
-                (bone->node_->GetWorldTransform().Inverse() * worldPosition, 0.5f * size / bone->node_->GetWorldScale().Length());
-
-            if (bone->collisionMask_ & BONECOLLISION_BOX)
-            {
-                float size = bone->boundingBox_.HalfSize().Length();
-                if (bone->boundingBox_.IsInside(decalSphere) && size > bestSize)
-                {
-                    bestBone = bone;
-                    bestSize = size;
-                }
-            }
-            else if (bone->collisionMask_ & BONECOLLISION_SPHERE)
-            {
-                Sphere boneSphere(Vector3::ZERO, bone->radius_);
-                float size = bone->radius_;
-                if (boneSphere.IsInside(decalSphere) && size > bestSize)
-                {
-                    bestBone = bone;
-                    bestSize = size;
-                }
-            }
-        }
-
-        if (bestBone)
-            targetTransform = (bestBone->node_->GetWorldTransform() * bestBone->offsetMatrix_).Inverse();
-    }
-
-    // Build the decal frustum
-    Frustum decalFrustum;
-    Matrix3x4 frustumTransform = targetTransform * Matrix3x4(adjustedWorldPosition, worldRotation, 1.0f);
-    decalFrustum.DefineOrtho(size, aspectRatio, 1.0, 0.0f, depth, frustumTransform);
-
-    Vector3 decalNormal = (targetTransform * Vector4(worldRotation * Vector3::BACK, 0.0f)).Normalized();
-
-    decals_.Resize(decals_.Size() + 1);
-    Decal& newDecal = decals_.Back();
-    newDecal.timeToLive_ = timeToLive;
-
-    Vector<PODVector<DecalVertex> > faces;
-    PODVector<DecalVertex> tempFace;
-
-    unsigned numBatches = target->GetBatches().Size();
-
-    // Use either a specified subgeometry in the target, or all
-    if (subGeometry < numBatches)
-        GetFaces(faces, target, subGeometry, decalFrustum, decalNormal, normalCutoff);
-    else
-    {
-        for (unsigned i = 0; i < numBatches; ++i)
-            GetFaces(faces, target, i, decalFrustum, decalNormal, normalCutoff);
-    }
-
-    // Clip the acquired faces against all frustum planes
-    for (unsigned i = 0; i < NUM_FRUSTUM_PLANES; ++i)
-    {
-        for (unsigned j = 0; j < faces.Size(); ++j)
-        {
-            PODVector<DecalVertex>& face = faces[j];
-            if (face.Empty())
-                continue;
-
-            ClipPolygon(tempFace, face, decalFrustum.planes_[i], skinned_);
-            face = tempFace;
-        }
-    }
-
-    // Now triangulate the resulting faces into decal vertices
-    for (unsigned i = 0; i < faces.Size(); ++i)
-    {
-        PODVector<DecalVertex>& face = faces[i];
-        if (face.Size() < 3)
-            continue;
-
-        for (unsigned j = 2; j < face.Size(); ++j)
-        {
-            newDecal.AddVertex(face[0]);
-            newDecal.AddVertex(face[j - 1]);
-            newDecal.AddVertex(face[j]);
-        }
-    }
-
-    // Check if resulted in no triangles
-    if (newDecal.vertices_.Empty())
-    {
-        decals_.Pop();
-        return true;
-    }
-
-    if (newDecal.vertices_.Size() > maxVertices_)
-    {
-        ATOMIC_LOGWARNING("Can not add decal, vertex count " + String(newDecal.vertices_.Size()) + " exceeds maximum " +
-                   String(maxVertices_));
-        decals_.Pop();
-        return false;
-    }
-    if (newDecal.indices_.Size() > maxIndices_)
-    {
-        ATOMIC_LOGWARNING("Can not add decal, index count " + String(newDecal.indices_.Size()) + " exceeds maximum " +
-                   String(maxIndices_));
-        decals_.Pop();
-        return false;
-    }
-
-    // Calculate UVs
-    Matrix4 projection(Matrix4::ZERO);
-    projection.m11_ = (1.0f / (size * 0.5f));
-    projection.m00_ = projection.m11_ / aspectRatio;
-    projection.m22_ = 1.0f / depth;
-    projection.m33_ = 1.0f;
-
-    CalculateUVs(newDecal, frustumTransform.Inverse(), projection, topLeftUV, bottomRightUV);
-
-    // Transform vertices to this node's local space and generate tangents
-    Matrix3x4 decalTransform = node_->GetWorldTransform().Inverse() * target->GetNode()->GetWorldTransform();
-    TransformVertices(newDecal, skinned_ ? Matrix3x4::IDENTITY : decalTransform);
-    GenerateTangents(&newDecal.vertices_[0], sizeof(DecalVertex), &newDecal.indices_[0], sizeof(unsigned short), 0,
-        newDecal.indices_.Size(), offsetof(DecalVertex, normal_), offsetof(DecalVertex, texCoord_), offsetof(DecalVertex,
-        tangent_));
-
-    newDecal.CalculateBoundingBox();
-    numVertices_ += newDecal.vertices_.Size();
-    numIndices_ += newDecal.indices_.Size();
-
-    // Remove oldest decals if total vertices exceeded
-    while (decals_.Size() && (numVertices_ > maxVertices_ || numIndices_ > maxIndices_))
-        RemoveDecals(1);
-
-    ATOMIC_LOGDEBUG("Added decal with " + String(newDecal.vertices_.Size()) + " vertices");
-
-    // If new decal is time limited, subscribe to scene post-update
-    if (newDecal.timeToLive_ > 0.0f && !subscribed_)
-        UpdateEventSubscription(false);
-
-    MarkDecalsDirty();
-    return true;
-}
-
-void DecalSet::RemoveDecals(unsigned num)
-{
-    while (num-- && decals_.Size())
-        RemoveDecal(decals_.Begin());
-}
-
-void DecalSet::RemoveAllDecals()
-{
-    if (!decals_.Empty())
-    {
-        decals_.Clear();
-        numVertices_ = 0;
-        numIndices_ = 0;
-        MarkDecalsDirty();
-    }
-
-    // Remove all bones and skinning matrices and stop listening to the bone nodes
-    for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
-    {
-        if (i->node_)
-            i->node_->RemoveListener(this);
-    }
-
-    bones_.Clear();
-    skinMatrices_.Clear();
-    UpdateBatch();
-}
-
-Material* DecalSet::GetMaterial() const
-{
-    return batches_[0].material_;
-}
-
-void DecalSet::SetMaterialAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    SetMaterial(cache->GetResource<Material>(value.name_));
-}
-
-void DecalSet::SetDecalsAttr(const PODVector<unsigned char>& value)
-{
-    RemoveAllDecals();
-
-    if (value.Empty())
-        return;
-
-    MemoryBuffer buffer(value);
-
-    skinned_ = buffer.ReadBool();
-    unsigned numDecals = buffer.ReadVLE();
-
-    while (numDecals--)
-    {
-        decals_.Resize(decals_.Size() + 1);
-        Decal& newDecal = decals_.Back();
-
-        newDecal.timer_ = buffer.ReadFloat();
-        newDecal.timeToLive_ = buffer.ReadFloat();
-        newDecal.vertices_.Resize(buffer.ReadVLE());
-        newDecal.indices_.Resize(buffer.ReadVLE());
-
-        for (PODVector<DecalVertex>::Iterator i = newDecal.vertices_.Begin(); i != newDecal.vertices_.End(); ++i)
-        {
-            i->position_ = buffer.ReadVector3();
-            i->normal_ = buffer.ReadVector3();
-            i->texCoord_ = buffer.ReadVector2();
-            i->tangent_ = buffer.ReadVector4();
-            if (skinned_)
-            {
-                for (unsigned j = 0; j < 4; ++j)
-                    i->blendWeights_[j] = buffer.ReadFloat();
-                for (unsigned j = 0; j < 4; ++j)
-                    i->blendIndices_[j] = buffer.ReadUByte();
-            }
-        }
-
-        for (PODVector<unsigned short>::Iterator i = newDecal.indices_.Begin(); i != newDecal.indices_.End(); ++i)
-            *i = buffer.ReadUShort();
-
-        newDecal.CalculateBoundingBox();
-        numVertices_ += newDecal.vertices_.Size();
-        numIndices_ += newDecal.indices_.Size();
-    }
-
-    if (skinned_)
-    {
-        unsigned numBones = buffer.ReadVLE();
-        skinMatrices_.Resize(numBones);
-        bones_.Resize(numBones);
-
-        for (unsigned i = 0; i < numBones; ++i)
-        {
-            Bone& newBone = bones_[i];
-
-            newBone.name_ = buffer.ReadString();
-            newBone.collisionMask_ = buffer.ReadUByte();
-            if (newBone.collisionMask_ & BONECOLLISION_SPHERE)
-                newBone.radius_ = buffer.ReadFloat();
-            if (newBone.collisionMask_ & BONECOLLISION_BOX)
-                newBone.boundingBox_ = buffer.ReadBoundingBox();
-            buffer.Read(&newBone.offsetMatrix_.m00_, sizeof(Matrix3x4));
-        }
-
-        assignBonesPending_ = true;
-        skinningDirty_ = true;
-    }
-
-    UpdateEventSubscription(true);
-    UpdateBatch();
-    MarkDecalsDirty();
-    bufferSizeDirty_ = true;
-}
-
-ResourceRef DecalSet::GetMaterialAttr() const
-{
-    return GetResourceRef(batches_[0].material_, Material::GetTypeStatic());
-}
-
-PODVector<unsigned char> DecalSet::GetDecalsAttr() const
-{
-    VectorBuffer ret;
-
-    ret.WriteBool(skinned_);
-    ret.WriteVLE(decals_.Size());
-
-    for (List<Decal>::ConstIterator i = decals_.Begin(); i != decals_.End(); ++i)
-    {
-        ret.WriteFloat(i->timer_);
-        ret.WriteFloat(i->timeToLive_);
-        ret.WriteVLE(i->vertices_.Size());
-        ret.WriteVLE(i->indices_.Size());
-
-        for (PODVector<DecalVertex>::ConstIterator j = i->vertices_.Begin(); j != i->vertices_.End(); ++j)
-        {
-            ret.WriteVector3(j->position_);
-            ret.WriteVector3(j->normal_);
-            ret.WriteVector2(j->texCoord_);
-            ret.WriteVector4(j->tangent_);
-            if (skinned_)
-            {
-                for (unsigned k = 0; k < 4; ++k)
-                    ret.WriteFloat(j->blendWeights_[k]);
-                for (unsigned k = 0; k < 4; ++k)
-                    ret.WriteUByte(j->blendIndices_[k]);
-            }
-        }
-
-        for (PODVector<unsigned short>::ConstIterator j = i->indices_.Begin(); j != i->indices_.End(); ++j)
-            ret.WriteUShort(*j);
-    }
-
-    if (skinned_)
-    {
-        ret.WriteVLE(bones_.Size());
-
-        for (Vector<Bone>::ConstIterator i = bones_.Begin(); i != bones_.End(); ++i)
-        {
-            ret.WriteString(i->name_);
-            ret.WriteUByte(i->collisionMask_);
-            if (i->collisionMask_ & BONECOLLISION_SPHERE)
-                ret.WriteFloat(i->radius_);
-            if (i->collisionMask_ & BONECOLLISION_BOX)
-                ret.WriteBoundingBox(i->boundingBox_);
-            ret.Write(i->offsetMatrix_.Data(), sizeof(Matrix3x4));
-        }
-    }
-
-    return ret.GetBuffer();
-}
-
-void DecalSet::OnMarkedDirty(Node* node)
-{
-    Drawable::OnMarkedDirty(node);
-
-    if (skinned_)
-    {
-        // If the scene node or any of the bone nodes move, mark skinning dirty
-        skinningDirty_ = true;
-    }
-}
-
-void DecalSet::OnWorldBoundingBoxUpdate()
-{
-    if (!skinned_)
-    {
-        if (boundingBoxDirty_)
-            CalculateBoundingBox();
-
-        worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
-    }
-    else
-    {
-        // When using skinning, update world bounding box based on the bones
-        BoundingBox worldBox;
-
-        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)
-                worldBox.Merge(i->boundingBox_.Transformed(boneNode->GetWorldTransform()));
-            else if (i->collisionMask_ & BONECOLLISION_SPHERE)
-                worldBox.Merge(Sphere(boneNode->GetWorldPosition(), i->radius_ * 0.5f));
-        }
-
-        worldBoundingBox_ = worldBox;
-    }
-}
-
-void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Drawable* target, unsigned batchIndex, const Frustum& frustum,
-    const Vector3& decalNormal, float normalCutoff)
-{
-    // Try to use the most accurate LOD level if possible
-    Geometry* geometry = target->GetLodGeometry(batchIndex, 0);
-    if (!geometry || geometry->GetPrimitiveType() != TRIANGLE_LIST)
-        return;
-
-    const unsigned char* positionData = 0;
-    const unsigned char* normalData = 0;
-    const unsigned char* skinningData = 0;
-    const unsigned char* indexData = 0;
-    unsigned positionStride = 0;
-    unsigned normalStride = 0;
-    unsigned skinningStride = 0;
-    unsigned indexStride = 0;
-
-    IndexBuffer* ib = geometry->GetIndexBuffer();
-    if (ib)
-    {
-        indexData = ib->GetShadowData();
-        indexStride = ib->GetIndexSize();
-    }
-
-    // For morphed models positions, normals and skinning may be in different buffers
-    for (unsigned i = 0; i < geometry->GetNumVertexBuffers(); ++i)
-    {
-        VertexBuffer* vb = geometry->GetVertexBuffer(i);
-        if (!vb)
-            continue;
-
-        unsigned elementMask = vb->GetElementMask();
-        unsigned char* data = vb->GetShadowData();
-        if (!data)
-            continue;
-
-        if (elementMask & MASK_POSITION)
-        {
-            positionData = data;
-            positionStride = vb->GetVertexSize();
-        }
-        if (elementMask & MASK_NORMAL)
-        {
-            normalData = data + vb->GetElementOffset(SEM_NORMAL);
-            normalStride = vb->GetVertexSize();
-        }
-        if (elementMask & MASK_BLENDWEIGHTS)
-        {
-            skinningData = data + vb->GetElementOffset(SEM_BLENDWEIGHTS);
-            skinningStride = vb->GetVertexSize();
-        }
-    }
-
-    // Positions and indices are needed
-    if (!positionData)
-    {
-        // As a fallback, try to get the geometry's raw vertex/index data
-        const PODVector<VertexElement>* elements;
-        geometry->GetRawData(positionData, positionStride, indexData, indexStride, elements);
-        if (!positionData)
-        {
-            ATOMIC_LOGWARNING("Can not add decal, target drawable has no CPU-side geometry data");
-            return;
-        }
-    }
-
-    if (indexData)
-    {
-        unsigned indexStart = geometry->GetIndexStart();
-        unsigned indexCount = geometry->GetIndexCount();
-
-        // 16-bit indices
-        if (indexStride == sizeof(unsigned short))
-        {
-            const unsigned short* indices = ((const unsigned short*)indexData) + indexStart;
-            const unsigned short* indicesEnd = indices + indexCount;
-
-            while (indices < indicesEnd)
-            {
-                GetFace(faces, target, batchIndex, indices[0], indices[1], indices[2], positionData, normalData, skinningData,
-                    positionStride, normalStride, skinningStride, frustum, decalNormal, normalCutoff);
-                indices += 3;
-            }
-        }
-        else
-        // 32-bit indices
-        {
-            const unsigned* indices = ((const unsigned*)indexData) + indexStart;
-            const unsigned* indicesEnd = indices + indexCount;
-
-            while (indices < indicesEnd)
-            {
-                GetFace(faces, target, batchIndex, indices[0], indices[1], indices[2], positionData, normalData, skinningData,
-                    positionStride, normalStride, skinningStride, frustum, decalNormal, normalCutoff);
-                indices += 3;
-            }
-        }
-    }
-    else
-    {
-        // Non-indexed geometry
-        unsigned indices = geometry->GetVertexStart();
-        unsigned indicesEnd = indices + geometry->GetVertexCount();
-
-        while (indices + 2 < indicesEnd)
-        {
-            GetFace(faces, target, batchIndex, indices, indices + 1, indices + 2, positionData, normalData, skinningData,
-                positionStride, normalStride, skinningStride, frustum, decalNormal, normalCutoff);
-            indices += 3;
-        }
-    }
-}
-
-void DecalSet::GetFace(Vector<PODVector<DecalVertex> >& faces, Drawable* target, unsigned batchIndex, unsigned i0, unsigned i1,
-    unsigned i2, const unsigned char* positionData, const unsigned char* normalData, const unsigned char* skinningData,
-    unsigned positionStride, unsigned normalStride, unsigned skinningStride, const Frustum& frustum, const Vector3& decalNormal,
-    float normalCutoff)
-{
-    bool hasNormals = normalData != 0;
-    bool hasSkinning = skinned_ && skinningData != 0;
-
-    const Vector3& v0 = *((const Vector3*)(&positionData[i0 * positionStride]));
-    const Vector3& v1 = *((const Vector3*)(&positionData[i1 * positionStride]));
-    const Vector3& v2 = *((const Vector3*)(&positionData[i2 * positionStride]));
-
-    // Calculate unsmoothed face normals if no normal data
-    Vector3 faceNormal = Vector3::ZERO;
-    if (!hasNormals)
-    {
-        Vector3 dist1 = v1 - v0;
-        Vector3 dist2 = v2 - v0;
-        faceNormal = (dist1.CrossProduct(dist2)).Normalized();
-    }
-
-    const Vector3& n0 = hasNormals ? *((const Vector3*)(&normalData[i0 * normalStride])) : faceNormal;
-    const Vector3& n1 = hasNormals ? *((const Vector3*)(&normalData[i1 * normalStride])) : faceNormal;
-    const Vector3& n2 = hasNormals ? *((const Vector3*)(&normalData[i2 * normalStride])) : faceNormal;
-
-    const unsigned char* s0 = hasSkinning ? &skinningData[i0 * skinningStride] : (const unsigned char*)0;
-    const unsigned char* s1 = hasSkinning ? &skinningData[i1 * skinningStride] : (const unsigned char*)0;
-    const unsigned char* s2 = hasSkinning ? &skinningData[i2 * skinningStride] : (const unsigned char*)0;
-
-    // Check if face is too much away from the decal normal
-    if (decalNormal.DotProduct((n0 + n1 + n2) / 3.0f) < normalCutoff)
-        return;
-
-    // Check if face is culled completely by any of the planes
-    for (unsigned i = PLANE_FAR; i < NUM_FRUSTUM_PLANES; --i)
-    {
-        const Plane& plane = frustum.planes_[i];
-        if (plane.Distance(v0) < 0.0f && plane.Distance(v1) < 0.0f && plane.Distance(v2) < 0.0f)
-            return;
-    }
-
-    faces.Resize(faces.Size() + 1);
-    PODVector<DecalVertex>& face = faces.Back();
-    if (!hasSkinning)
-    {
-        face.Reserve(3);
-        face.Push(DecalVertex(v0, n0));
-        face.Push(DecalVertex(v1, n1));
-        face.Push(DecalVertex(v2, n2));
-    }
-    else
-    {
-        const float* bw0 = (const float*)s0;
-        const float* bw1 = (const float*)s1;
-        const float* bw2 = (const float*)s2;
-        const unsigned char* bi0 = s0 + sizeof(float) * 4;
-        const unsigned char* bi1 = s1 + sizeof(float) * 4;
-        const unsigned char* bi2 = s2 + sizeof(float) * 4;
-        unsigned char nbi0[4];
-        unsigned char nbi1[4];
-        unsigned char nbi2[4];
-
-        // Make sure all bones are found and that there is room in the skinning matrices
-        if (!GetBones(target, batchIndex, bw0, bi0, nbi0) || !GetBones(target, batchIndex, bw1, bi1, nbi1) ||
-            !GetBones(target, batchIndex, bw2, bi2, nbi2))
-            return;
-
-        face.Reserve(3);
-        face.Push(DecalVertex(v0, n0, bw0, nbi0));
-        face.Push(DecalVertex(v1, n1, bw1, nbi1));
-        face.Push(DecalVertex(v2, n2, bw2, nbi2));
-    }
-}
-
-bool DecalSet::GetBones(Drawable* target, unsigned batchIndex, const float* blendWeights, const unsigned char* blendIndices,
-    unsigned char* newBlendIndices)
-{
-    AnimatedModel* animatedModel = dynamic_cast<AnimatedModel*>(target);
-    if (!animatedModel)
-        return false;
-
-    // Check whether target is using global or per-geometry skinning
-    const Vector<PODVector<Matrix3x4> >& geometrySkinMatrices = animatedModel->GetGeometrySkinMatrices();
-    const Vector<PODVector<unsigned> >& geometryBoneMappings = animatedModel->GetGeometryBoneMappings();
-
-    for (unsigned i = 0; i < 4; ++i)
-    {
-        if (blendWeights[i] > 0.0f)
-        {
-            Bone* bone = 0;
-            if (geometrySkinMatrices.Empty())
-                bone = animatedModel->GetSkeleton().GetBone(blendIndices[i]);
-            else if (blendIndices[i] < geometryBoneMappings[batchIndex].Size())
-                bone = animatedModel->GetSkeleton().GetBone(geometryBoneMappings[batchIndex][blendIndices[i]]);
-
-            if (!bone)
-            {
-                ATOMIC_LOGWARNING("Out of range bone index for skinned decal");
-                return false;
-            }
-
-            bool found = false;
-            unsigned index;
-
-            for (index = 0; index < bones_.Size(); ++index)
-            {
-                if (bones_[index].node_ == bone->node_)
-                {
-                    // Check also that the offset matrix matches, in case we for example have a separate attachment AnimatedModel
-                    // with a different bind pose
-                    if (bones_[index].offsetMatrix_.Equals(bone->offsetMatrix_))
-                    {
-                        found = true;
-                        break;
-                    }
-                }
-            }
-
-            if (!found)
-            {
-                if (bones_.Size() >= Graphics::GetMaxBones())
-                {
-                    ATOMIC_LOGWARNING("Maximum skinned decal bone count reached");
-                    return false;
-                }
-                else
-                {
-                    // Copy the bone from the model to the decal
-                    index = bones_.Size();
-                    bones_.Resize(bones_.Size() + 1);
-                    bones_[index] = *bone;
-                    skinMatrices_.Resize(skinMatrices_.Size() + 1);
-                    skinningDirty_ = true;
-
-                    // Start listening to bone transform changes to update skinning
-                    bone->node_->AddListener(this);
-                }
-            }
-
-            newBlendIndices[i] = (unsigned char)index;
-        }
-        else
-            newBlendIndices[i] = 0;
-    }
-
-    // Update amount of shader data in the decal batch
-    UpdateBatch();
-    return true;
-}
-
-void DecalSet::CalculateUVs(Decal& decal, const Matrix3x4& view, const Matrix4& projection, const Vector2& topLeftUV,
-    const Vector2& bottomRightUV)
-{
-    Matrix4 viewProj = projection * view;
-
-    for (PODVector<DecalVertex>::Iterator i = decal.vertices_.Begin(); i != decal.vertices_.End(); ++i)
-    {
-        Vector3 projected = viewProj * i->position_;
-        i->texCoord_ = Vector2(
-            Lerp(topLeftUV.x_, bottomRightUV.x_, projected.x_ * 0.5f + 0.5f),
-            Lerp(bottomRightUV.y_, topLeftUV.y_, projected.y_ * 0.5f + 0.5f)
-        );
-    }
-}
-
-void DecalSet::TransformVertices(Decal& decal, const Matrix3x4& transform)
-{
-    for (PODVector<DecalVertex>::Iterator i = decal.vertices_.Begin(); i != decal.vertices_.End(); ++i)
-    {
-        i->position_ = transform * i->position_;
-        i->normal_ = (transform * i->normal_).Normalized();
-    }
-}
-
-List<Decal>::Iterator DecalSet::RemoveDecal(List<Decal>::Iterator i)
-{
-    numVertices_ -= i->vertices_.Size();
-    numIndices_ -= i->indices_.Size();
-    MarkDecalsDirty();
-    return decals_.Erase(i);
-}
-
-void DecalSet::MarkDecalsDirty()
-{
-    if (!boundingBoxDirty_)
-    {
-        boundingBoxDirty_ = true;
-        OnMarkedDirty(node_);
-    }
-    bufferDirty_ = true;
-}
-
-void DecalSet::CalculateBoundingBox()
-{
-    boundingBox_.Clear();
-    for (List<Decal>::ConstIterator i = decals_.Begin(); i != decals_.End(); ++i)
-        boundingBox_.Merge(i->boundingBox_);
-
-    boundingBoxDirty_ = false;
-}
-
-void DecalSet::UpdateBufferSize()
-{
-    vertexBuffer_->SetSize(maxVertices_, skinned_ ? SKINNED_ELEMENT_MASK : STATIC_ELEMENT_MASK);
-    indexBuffer_->SetSize(maxIndices_, false);
-    geometry_->SetVertexBuffer(0, vertexBuffer_);
-
-    bufferDirty_ = true;
-    bufferSizeDirty_ = false;
-}
-
-void DecalSet::UpdateBuffers()
-{
-    geometry_->SetDrawRange(TRIANGLE_LIST, 0, numIndices_, 0, numVertices_);
-
-    float* vertices = (float*)vertexBuffer_->Lock(0, numVertices_);
-    unsigned short* indices = (unsigned short*)indexBuffer_->Lock(0, numIndices_);
-
-    if (vertices && indices)
-    {
-        unsigned short indexStart = 0;
-
-        for (List<Decal>::ConstIterator i = decals_.Begin(); i != decals_.End(); ++i)
-        {
-            for (unsigned j = 0; j < i->vertices_.Size(); ++j)
-            {
-                const DecalVertex& vertex = i->vertices_[j];
-                *vertices++ = vertex.position_.x_;
-                *vertices++ = vertex.position_.y_;
-                *vertices++ = vertex.position_.z_;
-                *vertices++ = vertex.normal_.x_;
-                *vertices++ = vertex.normal_.y_;
-                *vertices++ = vertex.normal_.z_;
-                *vertices++ = vertex.texCoord_.x_;
-                *vertices++ = vertex.texCoord_.y_;
-                *vertices++ = vertex.tangent_.x_;
-                *vertices++ = vertex.tangent_.y_;
-                *vertices++ = vertex.tangent_.z_;
-                *vertices++ = vertex.tangent_.w_;
-                if (skinned_)
-                {
-                    *vertices++ = vertex.blendWeights_[0];
-                    *vertices++ = vertex.blendWeights_[1];
-                    *vertices++ = vertex.blendWeights_[2];
-                    *vertices++ = vertex.blendWeights_[3];
-                    *vertices++ = *((float*)vertex.blendIndices_);
-                }
-            }
-
-            for (unsigned j = 0; j < i->indices_.Size(); ++j)
-                *indices++ = i->indices_[j] + indexStart;
-
-            indexStart += i->vertices_.Size();
-        }
-    }
-
-    vertexBuffer_->Unlock();
-    vertexBuffer_->ClearDataLost();
-    indexBuffer_->Unlock();
-    indexBuffer_->ClearDataLost();
-    bufferDirty_ = false;
-}
-
-void DecalSet::UpdateSkinning()
-{
-    // Use model's world transform in case a bone is missing
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-
-    for (unsigned i = 0; i < bones_.Size(); ++i)
-    {
-        const Bone& bone = bones_[i];
-        if (bone.node_)
-            skinMatrices_[i] = bone.node_->GetWorldTransform() * bone.offsetMatrix_;
-        else
-            skinMatrices_[i] = worldTransform;
-    }
-
-    skinningDirty_ = false;
-}
-
-void DecalSet::UpdateBatch()
-{
-    if (skinMatrices_.Size())
-    {
-        batches_[0].geometryType_ = GEOM_SKINNED;
-        batches_[0].worldTransform_ = &skinMatrices_[0];
-        batches_[0].numWorldTransforms_ = skinMatrices_.Size();
-    }
-    else
-    {
-        batches_[0].geometryType_ = GEOM_STATIC;
-        batches_[0].worldTransform_ = &node_->GetWorldTransform();
-        batches_[0].numWorldTransforms_ = 1;
-    }
-}
-
-void DecalSet::AssignBoneNodes()
-{
-    assignBonesPending_ = false;
-
-    if (!node_)
-        return;
-
-    // Find the bone nodes from the node hierarchy and add listeners
-    for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
-    {
-        Node* boneNode = node_->GetChild(i->name_, true);
-        if (boneNode)
-            boneNode->AddListener(this);
-        i->node_ = boneNode;
-    }
-}
-
-void DecalSet::UpdateEventSubscription(bool checkAllDecals)
-{
-    Scene* scene = GetScene();
-    if (!scene)
-        return;
-
-    bool enabled = IsEnabledEffective();
-
-    if (enabled && checkAllDecals)
-    {
-        bool hasTimeLimitedDecals = false;
-
-        for (List<Decal>::ConstIterator i = decals_.Begin(); i != decals_.End(); ++i)
-        {
-            if (i->timeToLive_ > 0.0f)
-            {
-                hasTimeLimitedDecals = true;
-                break;
-            }
-        }
-
-        // If no time limited decals, no need to subscribe to scene update
-        enabled = hasTimeLimitedDecals;
-    }
-
-    if (enabled && !subscribed_)
-    {
-        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, ATOMIC_HANDLER(DecalSet, HandleScenePostUpdate));
-        subscribed_ = true;
-    }
-    else if (!enabled && subscribed_)
-    {
-        UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
-        subscribed_ = false;
-    }
-}
-
-void DecalSet::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
-{
-    using namespace ScenePostUpdate;
-
-    float timeStep = eventData[P_TIMESTEP].GetFloat();
-
-    for (List<Decal>::Iterator i = decals_.Begin(); i != decals_.End();)
-    {
-        i->timer_ += timeStep;
-
-        // Remove the decal if time to live expired
-        if (i->timeToLive_ > 0.0f && i->timer_ > i->timeToLive_)
-            i = RemoveDecal(i);
-        else
-            ++i;
-    }
-}
-
-}

+ 0 - 253
Source/Atomic/Atomic3D/DecalSet.h

@@ -1,253 +0,0 @@
-//
-// 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 "../Container/List.h"
-#include "../Graphics/Drawable.h"
-#include "../Graphics/Skeleton.h"
-#include "../Math/Frustum.h"
-
-namespace Atomic
-{
-
-class IndexBuffer;
-class VertexBuffer;
-
-/// %Decal vertex.
-struct DecalVertex
-{
-    /// Construct with defaults.
-    DecalVertex()
-    {
-    }
-
-    /// Construct with position and normal.
-    DecalVertex(const Vector3& position, const Vector3& normal) :
-        position_(position),
-        normal_(normal)
-    {
-    }
-
-    /// Construct with position, normal and skinning information.
-    DecalVertex(const Vector3& position, const Vector3& normal, const float* blendWeights, const unsigned char* blendIndices) :
-        position_(position),
-        normal_(normal)
-    {
-        for (unsigned i = 0; i < 4; ++i)
-        {
-            blendWeights_[i] = blendWeights[i];
-            blendIndices_[i] = blendIndices[i];
-        }
-    }
-
-    /// Position.
-    Vector3 position_;
-    /// Normal.
-    Vector3 normal_;
-    /// Texture coordinates.
-    Vector2 texCoord_;
-    /// Tangent.
-    Vector4 tangent_;
-    /// Blend weights.
-    float blendWeights_[4];
-    /// Blend indices.
-    unsigned char blendIndices_[4];
-};
-
-/// One decal in a decal set.
-struct Decal
-{
-    /// Construct with defaults.
-    Decal() :
-        timer_(0.0f),
-        timeToLive_(0.0f)
-    {
-    }
-
-    /// Add a vertex.
-    void AddVertex(const DecalVertex& vertex);
-    /// Calculate local-space bounding box.
-    void CalculateBoundingBox();
-
-    /// Decal age timer.
-    float timer_;
-    /// Maximum time to live in seconds (0 = infinite)
-    float timeToLive_;
-    /// Local-space bounding box.
-    BoundingBox boundingBox_;
-    /// Decal vertices.
-    PODVector<DecalVertex> vertices_;
-    /// Decal indices.
-    PODVector<unsigned short> indices_;
-};
-
-/// %Decal renderer component.
-class ATOMIC_API DecalSet : public Drawable
-{
-    ATOMIC_OBJECT(DecalSet, Drawable);
-
-public:
-    /// Construct.
-    DecalSet(Context* context);
-    /// Destruct.
-    virtual ~DecalSet();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-
-    /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
-    virtual void ApplyAttributes();
-    /// Handle enabled/disabled state change.
-    virtual void OnSetEnabled();
-    /// Process octree raycast. May be called from a worker thread.
-    virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
-    /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
-    virtual void UpdateBatches(const FrameInfo& frame);
-    /// Prepare geometry for rendering. Called from a worker thread if possible (no GPU update.)
-    virtual void UpdateGeometry(const FrameInfo& frame);
-    /// Return whether a geometry update is necessary, and if it can happen in a worker thread.
-    virtual UpdateGeometryType GetUpdateGeometryType();
-
-    /// Set material. The material should use a small negative depth bias to avoid Z-fighting.
-    void SetMaterial(Material* material);
-    /// Set maximum number of decal vertices.
-    void SetMaxVertices(unsigned num);
-    /// Set maximum number of decal vertex indices.
-    void SetMaxIndices(unsigned num);
-    /// Add a decal at world coordinates, using a target drawable's geometry for reference. If the decal needs to move with the target, the decal component should be created to the target's node. Return true if successful.
-    bool AddDecal(Drawable* target, const Vector3& worldPosition, const Quaternion& worldRotation, float size, float aspectRatio,
-        float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV, float timeToLive = 0.0f, float normalCutoff = 0.1f,
-        unsigned subGeometry = M_MAX_UNSIGNED);
-    /// Remove n oldest decals.
-    void RemoveDecals(unsigned num);
-    /// Remove all decals.
-    void RemoveAllDecals();
-
-    /// Return material.
-    Material* GetMaterial() const;
-
-    /// Return number of decals.
-    unsigned GetNumDecals() const { return decals_.Size(); }
-
-    /// Retur number of vertices in the decals.
-    unsigned GetNumVertices() const { return numVertices_; }
-
-    /// Retur number of vertex indices in the decals.
-    unsigned GetNumIndices() const { return numIndices_; }
-
-    /// Return maximum number of decal vertices.
-    unsigned GetMaxVertices() const { return maxVertices_; }
-
-    /// Return maximum number of decal vertex indices.
-    unsigned GetMaxIndices() const { return maxIndices_; }
-
-    /// Set material attribute.
-    void SetMaterialAttr(const ResourceRef& value);
-    /// Set decals attribute.
-    void SetDecalsAttr(const PODVector<unsigned char>& value);
-    /// Return material attribute.
-    ResourceRef GetMaterialAttr() const;
-    /// Return decals attribute.
-    PODVector<unsigned char> GetDecalsAttr() const;
-
-protected:
-    /// Recalculate the world-space bounding box.
-    virtual void OnWorldBoundingBoxUpdate();
-    /// Handle node transform being dirtied.
-    virtual void OnMarkedDirty(Node* node);
-
-private:
-    /// Get triangle faces from the target geometry.
-    void GetFaces(Vector<PODVector<DecalVertex> >& faces, Drawable* target, unsigned batchIndex, const Frustum& frustum,
-        const Vector3& decalNormal, float normalCutoff);
-    /// Get triangle face from the target geometry.
-    void GetFace
-        (Vector<PODVector<DecalVertex> >& faces, Drawable* target, unsigned batchIndex, unsigned i0, unsigned i1, unsigned i2,
-            const unsigned char* positionData, const unsigned char* normalData, const unsigned char* skinningData,
-            unsigned positionStride, unsigned normalStride, unsigned skinningStride, const Frustum& frustum,
-            const Vector3& decalNormal, float normalCutoff);
-    /// Get bones referenced by skinning data and remap the skinning indices. Return true if successful.
-    bool GetBones(Drawable* target, unsigned batchIndex, const float* blendWeights, const unsigned char* blendIndices,
-        unsigned char* newBlendIndices);
-    /// Calculate UV coordinates for the decal.
-    void CalculateUVs
-        (Decal& decal, const Matrix3x4& view, const Matrix4& projection, const Vector2& topLeftUV, const Vector2& bottomRightUV);
-    /// Transform decal's vertices from the target geometry to the decal set local space.
-    void TransformVertices(Decal& decal, const Matrix3x4& transform);
-    /// Remove a decal by iterator and return iterator to the next decal.
-    List<Decal>::Iterator RemoveDecal(List<Decal>::Iterator i);
-    /// Mark decals and the bounding box dirty.
-    void MarkDecalsDirty();
-    /// Recalculate the local-space bounding box.
-    void CalculateBoundingBox();
-    /// Resize decal vertex and index buffers.
-    void UpdateBufferSize();
-    /// Rewrite decal vertex and index buffers.
-    void UpdateBuffers();
-    /// Recalculate skinning.
-    void UpdateSkinning();
-    /// Update the batch (geometry type, shader data.)
-    void UpdateBatch();
-    /// Find bones after loading.
-    void AssignBoneNodes();
-    /// Subscribe/unsubscribe from scene post-update as necessary.
-    void UpdateEventSubscription(bool checkAllDecals);
-    /// Handle scene post-update event.
-    void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
-
-    /// Geometry.
-    SharedPtr<Geometry> geometry_;
-    /// Vertex buffer.
-    SharedPtr<VertexBuffer> vertexBuffer_;
-    /// Index buffer.
-    SharedPtr<IndexBuffer> indexBuffer_;
-    /// Decals.
-    List<Decal> decals_;
-    /// Bones used for skinned decals.
-    Vector<Bone> bones_;
-    /// Skinning matrices.
-    PODVector<Matrix3x4> skinMatrices_;
-    /// Vertices in the current decals.
-    unsigned numVertices_;
-    /// Indices in the current decals.
-    unsigned numIndices_;
-    /// Maximum vertices.
-    unsigned maxVertices_;
-    /// Maximum indices.
-    unsigned maxIndices_;
-    /// Skinned mode flag.
-    bool skinned_;
-    /// Vertex buffer needs resize flag.
-    bool bufferSizeDirty_;
-    /// Vertex buffer needs rewrite flag.
-    bool bufferDirty_;
-    /// Bounding box needs update flag.
-    bool boundingBoxDirty_;
-    /// Skinning dirty flag.
-    bool skinningDirty_;
-    /// Bone nodes assignment pending flag.
-    bool assignBonesPending_;
-    /// Subscribed to scene post update event flag.
-    bool subscribed_;
-};
-
-}

+ 0 - 106
Source/Atomic/Atomic3D/LMStaticModel.cpp

@@ -1,106 +0,0 @@
-// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
-// Please see LICENSE.md in repository root for license information
-// https://github.com/AtomicGameEngine/AtomicGameEngine
-
-#include "Precompiled.h"
-#include "../Core/Context.h"
-#include "../Resource/ResourceCache.h"
-#include "../Graphics/Technique.h"
-#include "../Atomic3D/LMStaticModel.h"
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-
-
-LMStaticModel::LMStaticModel(Context* context) :
-    StaticModel(context),
-    lightmapTilingOffset_(1.0f, 1.0f, 0.0f, 0.0f)
-{
-
-}
-
-LMStaticModel::~LMStaticModel()
-{
-
-}
-
-void LMStaticModel::RegisterObject(Context* context)
-{
-    context->RegisterFactory<LMStaticModel>(GEOMETRY_CATEGORY);
-
-    COPY_BASE_ATTRIBUTES(StaticModel);
-    ATTRIBUTE("Lightmap Tiling Offset", Vector4, lightmapTilingOffset_ , Vector4(1.0f, 1.0f, 0.0f, 0.0f), AM_DEFAULT);
-    MIXED_ACCESSOR_ATTRIBUTE("LightmapTexture", GetLightmapTextureAttr, SetLightmapTextureAttr, ResourceRef, ResourceRef(Texture2D::GetTypeStatic()), AM_DEFAULT);
-
-}
-
-void LMStaticModel::SetLightmapTextureAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-
-    if (value.name_.Length())
-    {
-        lightmapTexture_ = cache->GetResource<Texture2D>(value.name_);
-    }
-    else
-    {
-        lightmapTexture_ = NULL;
-    }
-
-    // TODO: This is making a separate material for each model
-    // we should be able to factor this into batching
-    if (lightmapTexture_.NotNull() && lightmapMaterial_.Null())
-    {
-        Material* base = GetMaterial(0);
-        if (base)
-        {
-            ResourceCache* cache = GetSubsystem<ResourceCache>();
-            lightmapMaterial_ = base->Clone();
-            Technique* technique = cache->GetResource<Technique>("Techniques/DiffLightMap.xml");
-            lightmapMaterial_->SetTechnique(0, technique);
-            lightmapMaterial_->SetTexture(TU_EMISSIVE, lightmapTexture_);
-        }
-    }
-
-
-}
-
-ResourceRef LMStaticModel::GetLightmapTextureAttr() const
-{
-    return GetResourceRef(lightmapTexture_, Texture2D::GetTypeStatic());
-}
-
-void LMStaticModel::UpdateBatches(const FrameInfo& frame)
-{
-    /*
-    StaticModel::UpdateBatches(frame);
-
-    if (batches_.Size() > 1)
-    {
-        for (unsigned i = 0; i < batches_.Size(); ++i)
-        {
-            batches_[i].lightmapTilingOffset_ = &lightmapTilingOffset_;
-            batches_[i].geometryType_ = GEOM_STATIC_NOINSTANCING;
-
-            if (!lightmapMaterial_.Null())
-                batches_[i].material_ = lightmapMaterial_;
-        }
-    }
-    else if (batches_.Size() == 1)
-    {
-        batches_[0].lightmapTilingOffset_ = &lightmapTilingOffset_;
-        batches_[0].geometryType_ = GEOM_STATIC_NOINSTANCING;
-        if (!lightmapMaterial_.Null())
-            batches_[0].material_ = lightmapMaterial_;
-
-    }
-    */
-
-}
-
-
-}
-
-

+ 0 - 46
Source/Atomic/Atomic3D/LMStaticModel.h

@@ -1,46 +0,0 @@
-// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
-// Please see LICENSE.md in repository root for license information
-// https://github.com/AtomicGameEngine/AtomicGameEngine
-
-#pragma once
-
-#include "../Atomic3D/StaticModel.h"
-#include "../Graphics/Texture2D.h"
-#include "../Graphics/Material.h"
-
-namespace Atomic
-{
-
-class LMStaticModel: public StaticModel
-{
-    ATOMIC_OBJECT(LMStaticModel, StaticModel);
-
-public:
-    /// Construct.
-    LMStaticModel(Context* context);
-    /// Destruct.
-    virtual ~LMStaticModel();
-
-    /// Register object factory. StaticModel must be registered first.
-    static void RegisterObject(Context* context);
-
-    virtual void UpdateBatches(const FrameInfo& frame);
-
-    Vector4 lightmapTilingOffset_;
-
-    void SetLightmapTextureAttr(const ResourceRef& value);
-    ResourceRef GetLightmapTextureAttr() const;
-
-    void SetLightmapTexure(Texture2D* texture)
-    {
-        lightmapTexture_ = texture;
-    }
-
-private:
-    SharedPtr<Texture2D> lightmapTexture_;
-
-    SharedPtr<Material> lightmapMaterial_;
-
-};
-
-}

+ 0 - 756
Source/Atomic/Atomic3D/Model.cpp

@@ -1,756 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/Geometry.h"
-#include "../Graphics/IndexBuffer.h"
-#include "../Graphics/Model.h"
-#include "../Graphics/Graphics.h"
-#include "../Graphics/VertexBuffer.h"
-#include "../IO/Log.h"
-#include "../IO/File.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-unsigned LookupVertexBuffer(VertexBuffer* buffer, const Vector<SharedPtr<VertexBuffer> >& buffers)
-{
-    for (unsigned i = 0; i < buffers.Size(); ++i)
-    {
-        if (buffers[i] == buffer)
-            return i;
-    }
-    return 0;
-}
-
-unsigned LookupIndexBuffer(IndexBuffer* buffer, const Vector<SharedPtr<IndexBuffer> >& buffers)
-{
-    for (unsigned i = 0; i < buffers.Size(); ++i)
-    {
-        if (buffers[i] == buffer)
-            return i;
-    }
-    return 0;
-}
-
-Model::Model(Context* context) :
-    Resource(context)
-{
-}
-
-Model::~Model()
-{
-}
-
-void Model::RegisterObject(Context* context)
-{
-    context->RegisterFactory<Model>();
-}
-
-bool Model::BeginLoad(Deserializer& source)
-{
-    // Check ID
-    String fileID = source.ReadFileID();
-    if (fileID != "UMDL" && fileID != "UMD2")
-    {
-        ATOMIC_LOGERROR(source.GetName() + " is not a valid model file");
-        return false;
-    }
-
-    bool hasVertexDeclarations = (fileID == "UMD2");
-
-    geometries_.Clear();
-    geometryBoneMappings_.Clear();
-    geometryCenters_.Clear();
-    morphs_.Clear();
-    vertexBuffers_.Clear();
-    indexBuffers_.Clear();
-
-    unsigned memoryUse = sizeof(Model);
-    bool async = GetAsyncLoadState() == ASYNC_LOADING;
-
-    // Read vertex buffers
-    unsigned numVertexBuffers = source.ReadUInt();
-    vertexBuffers_.Reserve(numVertexBuffers);
-    morphRangeStarts_.Resize(numVertexBuffers);
-    morphRangeCounts_.Resize(numVertexBuffers);
-    loadVBData_.Resize(numVertexBuffers);
-    for (unsigned i = 0; i < numVertexBuffers; ++i)
-    {
-        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();
-        morphRangeCounts_[i] = source.ReadUInt();
-
-        SharedPtr<VertexBuffer> buffer(new VertexBuffer(context_));
-        unsigned vertexSize = VertexBuffer::GetVertexSize(desc.vertexElements_);
-        desc.dataSize_ = desc.vertexCount_ * vertexSize;
-
-        // Prepare vertex buffer data to be uploaded during EndLoad()
-        if (async)
-        {
-            desc.data_ = new unsigned char[desc.dataSize_];
-            source.Read(desc.data_.Get(), desc.dataSize_);
-        }
-        else
-        {
-            // If not async loading, use locking to avoid extra allocation & copy
-            desc.data_.Reset(); // Make sure no previous data
-            buffer->SetShadowed(true);
-            buffer->SetSize(desc.vertexCount_, desc.vertexElements_);
-            void* dest = buffer->Lock(0, desc.vertexCount_);
-            source.Read(dest, desc.vertexCount_ * vertexSize);
-            buffer->Unlock();
-        }
-
-        memoryUse += sizeof(VertexBuffer) + desc.vertexCount_ * vertexSize;
-        vertexBuffers_.Push(buffer);
-    }
-
-    // Read index buffers
-    unsigned numIndexBuffers = source.ReadUInt();
-    indexBuffers_.Reserve(numIndexBuffers);
-    loadIBData_.Resize(numIndexBuffers);
-    for (unsigned i = 0; i < numIndexBuffers; ++i)
-    {
-        unsigned indexCount = source.ReadUInt();
-        unsigned indexSize = source.ReadUInt();
-
-        SharedPtr<IndexBuffer> buffer(new IndexBuffer(context_));
-
-        // Prepare index buffer data to be uploaded during EndLoad()
-        if (async)
-        {
-            loadIBData_[i].indexCount_ = indexCount;
-            loadIBData_[i].indexSize_ = indexSize;
-            loadIBData_[i].dataSize_ = indexCount * indexSize;
-            loadIBData_[i].data_ = new unsigned char[loadIBData_[i].dataSize_];
-            source.Read(loadIBData_[i].data_.Get(), loadIBData_[i].dataSize_);
-        }
-        else
-        {
-            // If not async loading, use locking to avoid extra allocation & copy
-            loadIBData_[i].data_.Reset(); // Make sure no previous data
-            buffer->SetShadowed(true);
-            buffer->SetSize(indexCount, indexSize > sizeof(unsigned short));
-            void* dest = buffer->Lock(0, indexCount);
-            source.Read(dest, indexCount * indexSize);
-            buffer->Unlock();
-        }
-
-        memoryUse += sizeof(IndexBuffer) + indexCount * indexSize;
-        indexBuffers_.Push(buffer);
-    }
-
-    // Read geometries
-    unsigned numGeometries = source.ReadUInt();
-    geometries_.Reserve(numGeometries);
-    geometryBoneMappings_.Reserve(numGeometries);
-    geometryCenters_.Reserve(numGeometries);
-    loadGeometries_.Resize(numGeometries);
-    for (unsigned i = 0; i < numGeometries; ++i)
-    {
-        // Read bone mappings
-        unsigned boneMappingCount = source.ReadUInt();
-        PODVector<unsigned> boneMapping(boneMappingCount);
-        for (unsigned j = 0; j < boneMappingCount; ++j)
-            boneMapping[j] = source.ReadUInt();
-        geometryBoneMappings_.Push(boneMapping);
-
-        unsigned numLodLevels = source.ReadUInt();
-        Vector<SharedPtr<Geometry> > geometryLodLevels;
-        geometryLodLevels.Reserve(numLodLevels);
-        loadGeometries_[i].Resize(numLodLevels);
-
-        for (unsigned j = 0; j < numLodLevels; ++j)
-        {
-            float distance = source.ReadFloat();
-            PrimitiveType type = (PrimitiveType)source.ReadUInt();
-
-            unsigned vbRef = source.ReadUInt();
-            unsigned ibRef = source.ReadUInt();
-            unsigned indexStart = source.ReadUInt();
-            unsigned indexCount = source.ReadUInt();
-
-            if (vbRef >= vertexBuffers_.Size())
-            {
-                ATOMIC_LOGERROR("Vertex buffer index out of bounds");
-                loadVBData_.Clear();
-                loadIBData_.Clear();
-                loadGeometries_.Clear();
-                return false;
-            }
-            if (ibRef >= indexBuffers_.Size())
-            {
-                ATOMIC_LOGERROR("Index buffer index out of bounds");
-                loadVBData_.Clear();
-                loadIBData_.Clear();
-                loadGeometries_.Clear();
-                return false;
-            }
-
-            SharedPtr<Geometry> geometry(new Geometry(context_));
-            geometry->SetLodDistance(distance);
-
-            // Prepare geometry to be defined during EndLoad()
-            loadGeometries_[i][j].type_ = type;
-            loadGeometries_[i][j].vbRef_ = vbRef;
-            loadGeometries_[i][j].ibRef_ = ibRef;
-            loadGeometries_[i][j].indexStart_ = indexStart;
-            loadGeometries_[i][j].indexCount_ = indexCount;
-
-            geometryLodLevels.Push(geometry);
-            memoryUse += sizeof(Geometry);
-        }
-
-        geometries_.Push(geometryLodLevels);
-    }
-
-    // Read morphs
-    unsigned numMorphs = source.ReadUInt();
-    morphs_.Reserve(numMorphs);
-    for (unsigned i = 0; i < numMorphs; ++i)
-    {
-        ModelMorph newMorph;
-
-        newMorph.name_ = source.ReadString();
-        newMorph.nameHash_ = newMorph.name_;
-        newMorph.weight_ = 0.0f;
-        unsigned numBuffers = source.ReadUInt();
-
-        for (unsigned j = 0; j < numBuffers; ++j)
-        {
-            VertexBufferMorph newBuffer;
-            unsigned bufferIndex = source.ReadUInt();
-
-            newBuffer.elementMask_ = source.ReadUInt();
-            newBuffer.vertexCount_ = source.ReadUInt();
-
-            // Base size: size of each vertex index
-            unsigned vertexSize = sizeof(unsigned);
-            // Add size of individual elements
-            if (newBuffer.elementMask_ & MASK_POSITION)
-                vertexSize += sizeof(Vector3);
-            if (newBuffer.elementMask_ & MASK_NORMAL)
-                vertexSize += sizeof(Vector3);
-            if (newBuffer.elementMask_ & MASK_TANGENT)
-                vertexSize += sizeof(Vector3);
-            newBuffer.dataSize_ = newBuffer.vertexCount_ * vertexSize;
-            newBuffer.morphData_ = new unsigned char[newBuffer.dataSize_];
-
-            source.Read(&newBuffer.morphData_[0], newBuffer.vertexCount_ * vertexSize);
-
-            newMorph.buffers_[bufferIndex] = newBuffer;
-            memoryUse += sizeof(VertexBufferMorph) + newBuffer.vertexCount_ * vertexSize;
-        }
-
-        morphs_.Push(newMorph);
-        memoryUse += sizeof(ModelMorph);
-    }
-
-    // Read skeleton
-    skeleton_.Load(source);
-    memoryUse += skeleton_.GetNumBones() * sizeof(Bone);
-
-    // Read bounding box
-    boundingBox_ = source.ReadBoundingBox();
-
-    // Read geometry centers
-    for (unsigned i = 0; i < geometries_.Size() && !source.IsEof(); ++i)
-        geometryCenters_.Push(source.ReadVector3());
-    while (geometryCenters_.Size() < geometries_.Size())
-        geometryCenters_.Push(Vector3::ZERO);
-    memoryUse += sizeof(Vector3) * geometries_.Size();
-
-    SetMemoryUse(memoryUse);
-    return true;
-}
-
-bool Model::EndLoad()
-{
-    // Upload vertex buffer data
-    for (unsigned i = 0; i < vertexBuffers_.Size(); ++i)
-    {
-        VertexBuffer* buffer = vertexBuffers_[i];
-        VertexBufferDesc& desc = loadVBData_[i];
-        if (desc.data_)
-        {
-            buffer->SetShadowed(true);
-            buffer->SetSize(desc.vertexCount_, desc.vertexElements_);
-            buffer->SetData(desc.data_.Get());
-        }
-    }
-
-    // Upload index buffer data
-    for (unsigned i = 0; i < indexBuffers_.Size(); ++i)
-    {
-        IndexBuffer* buffer = indexBuffers_[i];
-        IndexBufferDesc& desc = loadIBData_[i];
-        if (desc.data_)
-        {
-            buffer->SetShadowed(true);
-            buffer->SetSize(desc.indexCount_, desc.indexSize_ > sizeof(unsigned short));
-            buffer->SetData(desc.data_.Get());
-        }
-    }
-
-    // Set up geometries
-    for (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        for (unsigned j = 0; j < geometries_[i].Size(); ++j)
-        {
-            Geometry* geometry = geometries_[i][j];
-            GeometryDesc& desc = loadGeometries_[i][j];
-            geometry->SetVertexBuffer(0, vertexBuffers_[desc.vbRef_]);
-            geometry->SetIndexBuffer(indexBuffers_[desc.ibRef_]);
-            geometry->SetDrawRange(desc.type_, desc.indexStart_, desc.indexCount_);
-        }
-    }
-
-    loadVBData_.Clear();
-    loadIBData_.Clear();
-    loadGeometries_.Clear();
-    return true;
-}
-
-bool Model::Save(Serializer& dest) const
-{
-    // Write ID
-    if (!dest.WriteFileID("UMD2"))
-        return false;
-
-    // Write vertex buffers
-    dest.WriteUInt(vertexBuffers_.Size());
-    for (unsigned i = 0; i < vertexBuffers_.Size(); ++i)
-    {
-        VertexBuffer* buffer = vertexBuffers_[i];
-        dest.WriteUInt(buffer->GetVertexCount());
-        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(morphRangeCounts_[i]);
-        dest.Write(buffer->GetShadowData(), buffer->GetVertexCount() * buffer->GetVertexSize());
-    }
-    // Write index buffers
-    dest.WriteUInt(indexBuffers_.Size());
-    for (unsigned i = 0; i < indexBuffers_.Size(); ++i)
-    {
-        IndexBuffer* buffer = indexBuffers_[i];
-        dest.WriteUInt(buffer->GetIndexCount());
-        dest.WriteUInt(buffer->GetIndexSize());
-        dest.Write(buffer->GetShadowData(), buffer->GetIndexCount() * buffer->GetIndexSize());
-    }
-    // Write geometries
-    dest.WriteUInt(geometries_.Size());
-    for (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        // Write bone mappings
-        dest.WriteUInt(geometryBoneMappings_[i].Size());
-        for (unsigned j = 0; j < geometryBoneMappings_[i].Size(); ++j)
-            dest.WriteUInt(geometryBoneMappings_[i][j]);
-
-        // Write the LOD levels
-        dest.WriteUInt(geometries_[i].Size());
-        for (unsigned j = 0; j < geometries_[i].Size(); ++j)
-        {
-            Geometry* geometry = geometries_[i][j];
-            dest.WriteFloat(geometry->GetLodDistance());
-            dest.WriteUInt(geometry->GetPrimitiveType());
-            dest.WriteUInt(LookupVertexBuffer(geometry->GetVertexBuffer(0), vertexBuffers_));
-            dest.WriteUInt(LookupIndexBuffer(geometry->GetIndexBuffer(), indexBuffers_));
-            dest.WriteUInt(geometry->GetIndexStart());
-            dest.WriteUInt(geometry->GetIndexCount());
-        }
-    }
-
-    // Write morphs
-    dest.WriteUInt(morphs_.Size());
-    for (unsigned i = 0; i < morphs_.Size(); ++i)
-    {
-        dest.WriteString(morphs_[i].name_);
-        dest.WriteUInt(morphs_[i].buffers_.Size());
-
-        // Write morph vertex buffers
-        for (HashMap<unsigned, VertexBufferMorph>::ConstIterator j = morphs_[i].buffers_.Begin();
-             j != morphs_[i].buffers_.End(); ++j)
-        {
-            dest.WriteUInt(j->first_);
-            dest.WriteUInt(j->second_.elementMask_);
-            dest.WriteUInt(j->second_.vertexCount_);
-
-            // Base size: size of each vertex index
-            unsigned vertexSize = sizeof(unsigned);
-            // Add size of individual elements
-            if (j->second_.elementMask_ & MASK_POSITION)
-                vertexSize += sizeof(Vector3);
-            if (j->second_.elementMask_ & MASK_NORMAL)
-                vertexSize += sizeof(Vector3);
-            if (j->second_.elementMask_ & MASK_TANGENT)
-                vertexSize += sizeof(Vector3);
-
-            dest.Write(j->second_.morphData_.Get(), vertexSize * j->second_.vertexCount_);
-        }
-    }
-
-    // Write skeleton
-    skeleton_.Save(dest);
-
-    // Write bounding box
-    dest.WriteBoundingBox(boundingBox_);
-
-    // Write geometry centers
-    for (unsigned i = 0; i < geometryCenters_.Size(); ++i)
-        dest.WriteVector3(geometryCenters_[i]);
-
-    return true;
-}
-
-void Model::SetBoundingBox(const BoundingBox& box)
-{
-    boundingBox_ = box;
-}
-
-bool Model::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>& morphRangeStarts,
-    const PODVector<unsigned>& morphRangeCounts)
-{
-    for (unsigned i = 0; i < buffers.Size(); ++i)
-    {
-        if (!buffers[i])
-        {
-            ATOMIC_LOGERROR("Null model vertex buffers specified");
-            return false;
-        }
-        if (!buffers[i]->IsShadowed())
-        {
-            ATOMIC_LOGERROR("Model vertex buffers must be shadowed");
-            return false;
-        }
-    }
-
-    vertexBuffers_ = buffers;
-    morphRangeStarts_.Resize(buffers.Size());
-    morphRangeCounts_.Resize(buffers.Size());
-
-    // If morph ranges are not specified for buffers, assume to be zero
-    for (unsigned i = 0; i < buffers.Size(); ++i)
-    {
-        morphRangeStarts_[i] = i < morphRangeStarts.Size() ? morphRangeStarts[i] : 0;
-        morphRangeCounts_[i] = i < morphRangeCounts.Size() ? morphRangeCounts[i] : 0;
-    }
-
-    return true;
-}
-
-bool Model::SetIndexBuffers(const Vector<SharedPtr<IndexBuffer> >& buffers)
-{
-    for (unsigned i = 0; i < buffers.Size(); ++i)
-    {
-        if (!buffers[i])
-        {
-            ATOMIC_LOGERROR("Null model index buffers specified");
-            return false;
-        }
-        if (!buffers[i]->IsShadowed())
-        {
-            ATOMIC_LOGERROR("Model index buffers must be shadowed");
-            return false;
-        }
-    }
-
-    indexBuffers_ = buffers;
-    return true;
-}
-
-void Model::SetNumGeometries(unsigned num)
-{
-    geometries_.Resize(num);
-    geometryBoneMappings_.Resize(num);
-    geometryCenters_.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 (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        if (geometries_[i].Empty())
-            geometries_[i].Resize(1);
-    }
-}
-
-bool Model::SetNumGeometryLodLevels(unsigned index, unsigned num)
-{
-    if (index >= geometries_.Size())
-    {
-        ATOMIC_LOGERROR("Geometry index out of bounds");
-        return false;
-    }
-    if (!num)
-    {
-        ATOMIC_LOGERROR("Zero LOD levels not allowed");
-        return false;
-    }
-
-    geometries_[index].Resize(num);
-    return true;
-}
-
-bool Model::SetGeometry(unsigned index, unsigned lodLevel, Geometry* geometry)
-{
-    if (index >= geometries_.Size())
-    {
-        ATOMIC_LOGERROR("Geometry index out of bounds");
-        return false;
-    }
-    if (lodLevel >= geometries_[index].Size())
-    {
-        ATOMIC_LOGERROR("LOD level index out of bounds");
-        return false;
-    }
-
-    geometries_[index][lodLevel] = geometry;
-    return true;
-}
-
-bool Model::SetGeometryCenter(unsigned index, const Vector3& center)
-{
-    if (index >= geometryCenters_.Size())
-    {
-        ATOMIC_LOGERROR("Geometry index out of bounds");
-        return false;
-    }
-
-    geometryCenters_[index] = center;
-    return true;
-}
-
-void Model::SetSkeleton(const Skeleton& skeleton)
-{
-    skeleton_ = skeleton;
-}
-
-void Model::SetGeometryBoneMappings(const Vector<PODVector<unsigned> >& geometryBoneMappings)
-{
-    geometryBoneMappings_ = geometryBoneMappings;
-}
-
-void Model::SetMorphs(const Vector<ModelMorph>& morphs)
-{
-    morphs_ = morphs;
-}
-
-SharedPtr<Model> Model::Clone(const String& cloneName) const
-{
-    SharedPtr<Model> ret(new Model(context_));
-
-    ret->SetName(cloneName);
-    ret->boundingBox_ = boundingBox_;
-    ret->skeleton_ = skeleton_;
-    ret->geometryBoneMappings_ = geometryBoneMappings_;
-    ret->geometryCenters_ = geometryCenters_;
-    ret->morphs_ = morphs_;
-    ret->morphRangeStarts_ = morphRangeStarts_;
-    ret->morphRangeCounts_ = morphRangeCounts_;
-
-    // Deep copy vertex/index buffers
-    HashMap<VertexBuffer*, VertexBuffer*> vbMapping;
-    for (Vector<SharedPtr<VertexBuffer> >::ConstIterator i = vertexBuffers_.Begin(); i != vertexBuffers_.End(); ++i)
-    {
-        VertexBuffer* origBuffer = *i;
-        SharedPtr<VertexBuffer> cloneBuffer;
-
-        if (origBuffer)
-        {
-            cloneBuffer = new VertexBuffer(context_);
-            cloneBuffer->SetSize(origBuffer->GetVertexCount(), origBuffer->GetElementMask(), origBuffer->IsDynamic());
-            cloneBuffer->SetShadowed(origBuffer->IsShadowed());
-            if (origBuffer->IsShadowed())
-                cloneBuffer->SetData(origBuffer->GetShadowData());
-            else
-            {
-                void* origData = origBuffer->Lock(0, origBuffer->GetVertexCount());
-                if (origData)
-                    cloneBuffer->SetData(origData);
-                else
-                    ATOMIC_LOGERROR("Failed to lock original vertex buffer for copying");
-            }
-            vbMapping[origBuffer] = cloneBuffer;
-        }
-
-        ret->vertexBuffers_.Push(cloneBuffer);
-    }
-
-    HashMap<IndexBuffer*, IndexBuffer*> ibMapping;
-    for (Vector<SharedPtr<IndexBuffer> >::ConstIterator i = indexBuffers_.Begin(); i != indexBuffers_.End(); ++i)
-    {
-        IndexBuffer* origBuffer = *i;
-        SharedPtr<IndexBuffer> cloneBuffer;
-
-        if (origBuffer)
-        {
-            cloneBuffer = new IndexBuffer(context_);
-            cloneBuffer->SetSize(origBuffer->GetIndexCount(), origBuffer->GetIndexSize() == sizeof(unsigned),
-                origBuffer->IsDynamic());
-            cloneBuffer->SetShadowed(origBuffer->IsShadowed());
-            if (origBuffer->IsShadowed())
-                cloneBuffer->SetData(origBuffer->GetShadowData());
-            else
-            {
-                void* origData = origBuffer->Lock(0, origBuffer->GetIndexCount());
-                if (origData)
-                    cloneBuffer->SetData(origData);
-                else
-                    ATOMIC_LOGERROR("Failed to lock original index buffer for copying");
-            }
-            ibMapping[origBuffer] = cloneBuffer;
-        }
-
-        ret->indexBuffers_.Push(cloneBuffer);
-    }
-
-    // Deep copy all the geometry LOD levels and refer to the copied vertex/index buffers
-    ret->geometries_.Resize(geometries_.Size());
-    for (unsigned i = 0; i < geometries_.Size(); ++i)
-    {
-        ret->geometries_[i].Resize(geometries_[i].Size());
-        for (unsigned j = 0; j < geometries_[i].Size(); ++j)
-        {
-            SharedPtr<Geometry> cloneGeometry;
-            Geometry* origGeometry = geometries_[i][j];
-
-            if (origGeometry)
-            {
-                cloneGeometry = new Geometry(context_);
-                cloneGeometry->SetIndexBuffer(ibMapping[origGeometry->GetIndexBuffer()]);
-                unsigned numVbs = origGeometry->GetNumVertexBuffers();
-                for (unsigned k = 0; k < numVbs; ++k)
-                {
-                    cloneGeometry->SetVertexBuffer(k, vbMapping[origGeometry->GetVertexBuffer(k)]);
-                }
-                cloneGeometry->SetDrawRange(origGeometry->GetPrimitiveType(), origGeometry->GetIndexStart(),
-                    origGeometry->GetIndexCount(), origGeometry->GetVertexStart(), origGeometry->GetVertexCount(), false);
-                cloneGeometry->SetLodDistance(origGeometry->GetLodDistance());
-            }
-
-            ret->geometries_[i][j] = cloneGeometry;
-        }
-    }
-
-
-    // Deep copy the morph data (if any) to allow modifying it
-    for (Vector<ModelMorph>::Iterator i = ret->morphs_.Begin(); i != ret->morphs_.End(); ++i)
-    {
-        ModelMorph& morph = *i;
-        for (HashMap<unsigned, VertexBufferMorph>::Iterator j = morph.buffers_.Begin(); j != morph.buffers_.End(); ++j)
-        {
-            VertexBufferMorph& vbMorph = j->second_;
-            if (vbMorph.dataSize_)
-            {
-                SharedArrayPtr<unsigned char> cloneData(new unsigned char[vbMorph.dataSize_]);
-                memcpy(cloneData.Get(), vbMorph.morphData_.Get(), vbMorph.dataSize_);
-                vbMorph.morphData_ = cloneData;
-            }
-        }
-    }
-
-    ret->SetMemoryUse(GetMemoryUse());
-
-    return ret;
-}
-
-unsigned Model::GetNumGeometryLodLevels(unsigned index) const
-{
-    return index < geometries_.Size() ? geometries_[index].Size() : 0;
-}
-
-Geometry* Model::GetGeometry(unsigned index, unsigned lodLevel) const
-{
-    if (index >= geometries_.Size() || geometries_[index].Empty())
-        return 0;
-
-    if (lodLevel >= geometries_[index].Size())
-        lodLevel = geometries_[index].Size() - 1;
-
-    return geometries_[index][lodLevel];
-}
-
-const ModelMorph* Model::GetMorph(unsigned index) const
-{
-    return index < morphs_.Size() ? &morphs_[index] : 0;
-}
-
-const ModelMorph* Model::GetMorph(const String& name) const
-{
-    return GetMorph(StringHash(name));
-}
-
-const ModelMorph* Model::GetMorph(StringHash nameHash) const
-{
-    for (Vector<ModelMorph>::ConstIterator i = morphs_.Begin(); i != morphs_.End(); ++i)
-    {
-        if (i->nameHash_ == nameHash)
-            return &(*i);
-    }
-
-    return 0;
-}
-
-unsigned Model::GetMorphRangeStart(unsigned bufferIndex) const
-{
-    return bufferIndex < vertexBuffers_.Size() ? morphRangeStarts_[bufferIndex] : 0;
-}
-
-unsigned Model::GetMorphRangeCount(unsigned bufferIndex) const
-{
-    return bufferIndex < vertexBuffers_.Size() ? morphRangeCounts_[bufferIndex] : 0;
-}
-
-}

+ 0 - 233
Source/Atomic/Atomic3D/Model.h

@@ -1,233 +0,0 @@
-//
-// 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 "../Container/ArrayPtr.h"
-#include "../Container/Ptr.h"
-#include "../Graphics/GraphicsDefs.h"
-#include "../Graphics/Skeleton.h"
-#include "../Math/BoundingBox.h"
-#include "../Resource/Resource.h"
-
-namespace Atomic
-{
-
-class Geometry;
-class IndexBuffer;
-class Graphics;
-class VertexBuffer;
-
-/// Vertex buffer morph data.
-struct VertexBufferMorph
-{
-    /// Vertex elements.
-    unsigned elementMask_;
-    /// Number of vertices.
-    unsigned vertexCount_;
-    /// Morphed vertices data size as bytes.
-    unsigned dataSize_;
-    /// Morphed vertices. Stored packed as <index, data> pairs.
-    SharedArrayPtr<unsigned char> morphData_;
-};
-
-/// Definition of a model's vertex morph.
-struct ModelMorph
-{
-    /// Morph name.
-    String name_;
-    /// Morph name hash.
-    StringHash nameHash_;
-    /// Current morph weight.
-    float weight_;
-    /// Morph data per vertex buffer.
-    HashMap<unsigned, VertexBufferMorph> buffers_;
-};
-
-/// Description of vertex buffer data for asynchronous loading.
-struct VertexBufferDesc
-{
-    /// Vertex count.
-    unsigned vertexCount_;
-    /// Vertex declaration.
-    PODVector<VertexElement> vertexElements_;
-    /// Vertex data size.
-    unsigned dataSize_;
-    /// Vertex data.
-    SharedArrayPtr<unsigned char> data_;
-};
-
-/// Description of index buffer data for asynchronous loading.
-struct IndexBufferDesc
-{
-    /// Index count.
-    unsigned indexCount_;
-    /// Index size.
-    unsigned indexSize_;
-    /// Index data size.
-    unsigned dataSize_;
-    /// Index data.
-    SharedArrayPtr<unsigned char> data_;
-};
-
-/// Description of a geometry for asynchronous loading.
-struct GeometryDesc
-{
-    /// Primitive type.
-    PrimitiveType type_;
-    /// Vertex buffer ref.
-    unsigned vbRef_;
-    /// Index buffer ref.
-    unsigned ibRef_;
-    /// Index start.
-    unsigned indexStart_;
-    /// Index count.
-    unsigned indexCount_;
-};
-
-/// 3D model resource.
-class ATOMIC_API Model : public Resource
-{
-    ATOMIC_OBJECT(Model, Resource);
-
-public:
-    /// Construct.
-    Model(Context* context);
-    /// Destruct.
-    virtual ~Model();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-
-    /// Load resource from stream. May be called from a worker thread. Return true if successful.
-    virtual bool BeginLoad(Deserializer& source);
-    /// Finish resource loading. Always called from the main thread. Return true if successful.
-    virtual bool EndLoad();
-    /// Save resource. Return true if successful.
-    virtual bool Save(Serializer& dest) const;
-
-    /// Set local-space bounding box.
-    void SetBoundingBox(const BoundingBox& box);
-    /// Set vertex buffers and their morph ranges.
-    bool SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>& morphRangeStarts,
-        const PODVector<unsigned>& morphRangeCounts);
-    /// Set index buffers.
-    bool SetIndexBuffers(const Vector<SharedPtr<IndexBuffer> >& buffers);
-    /// Set number of geometries.
-    void SetNumGeometries(unsigned num);
-    /// Set number of LOD levels in a geometry.
-    bool SetNumGeometryLodLevels(unsigned index, unsigned num);
-    /// Set geometry.
-    bool SetGeometry(unsigned index, unsigned lodLevel, Geometry* geometry);
-    /// Set geometry center.
-    bool SetGeometryCenter(unsigned index, const Vector3& center);
-    /// Set skeleton.
-    void SetSkeleton(const Skeleton& skeleton);
-    /// Set bone mappings when model has more bones than the skinning shader can handle.
-    void SetGeometryBoneMappings(const Vector<PODVector<unsigned> >& mappings);
-    /// Set vertex morphs.
-    void SetMorphs(const Vector<ModelMorph>& morphs);
-    /// Clone the model. The geometry data is deep-copied and can be modified in the clone without affecting the original.
-    SharedPtr<Model> Clone(const String& cloneName = String::EMPTY) const;
-
-    /// Return bounding box.
-    const BoundingBox& GetBoundingBox() const { return boundingBox_; }
-
-    /// Return skeleton.
-    Skeleton& GetSkeleton() { return skeleton_; }
-
-    /// Return vertex buffers.
-    const Vector<SharedPtr<VertexBuffer> >& GetVertexBuffers() const { return vertexBuffers_; }
-
-    /// Return index buffers.
-    const Vector<SharedPtr<IndexBuffer> >& GetIndexBuffers() const { return indexBuffers_; }
-
-    /// Return number of geometries.
-    unsigned GetNumGeometries() const { return geometries_.Size(); }
-
-    /// Return number of LOD levels in geometry.
-    unsigned GetNumGeometryLodLevels(unsigned index) const;
-
-    /// Return geometry pointers.
-    const Vector<Vector<SharedPtr<Geometry> > >& GetGeometries() const { return geometries_; }
-
-    /// Return geometry center points.
-    const PODVector<Vector3>& GetGeometryCenters() const { return geometryCenters_; }
-
-    /// Return geometry by index and LOD level. The LOD level is clamped if out of range.
-    Geometry* GetGeometry(unsigned index, unsigned lodLevel) const;
-
-    /// Return geometry center by index.
-    const Vector3& GetGeometryCenter(unsigned index) const
-    {
-        return index < geometryCenters_.Size() ? geometryCenters_[index] : Vector3::ZERO;
-    }
-
-    /// Return geometery bone mappings.
-    const Vector<PODVector<unsigned> >& GetGeometryBoneMappings() const { return geometryBoneMappings_; }
-
-    /// Return vertex morphs.
-    const Vector<ModelMorph>& GetMorphs() const { return morphs_; }
-
-    /// Return number of vertex morphs.
-    unsigned GetNumMorphs() const { return morphs_.Size(); }
-
-    /// Return vertex morph by index.
-    const ModelMorph* GetMorph(unsigned index) const;
-    /// Return vertex morph by name.
-    const ModelMorph* GetMorph(const String& name) const;
-    /// Return vertex morph by name hash.
-    const ModelMorph* GetMorph(StringHash nameHash) const;
-    /// Return vertex buffer morph range start.
-    unsigned GetMorphRangeStart(unsigned bufferIndex) const;
-    /// Return vertex buffer morph range vertex count.
-    unsigned GetMorphRangeCount(unsigned bufferIndex) const;
-
-private:
-    /// Bounding box.
-    BoundingBox boundingBox_;
-    /// Skeleton.
-    Skeleton skeleton_;
-    /// Vertex buffers.
-    Vector<SharedPtr<VertexBuffer> > vertexBuffers_;
-    /// Index buffers.
-    Vector<SharedPtr<IndexBuffer> > indexBuffers_;
-    /// Geometries.
-    Vector<Vector<SharedPtr<Geometry> > > geometries_;
-    /// Geometry bone mappings.
-    Vector<PODVector<unsigned> > geometryBoneMappings_;
-    /// Geometry centers.
-    PODVector<Vector3> geometryCenters_;
-    /// Vertex morphs.
-    Vector<ModelMorph> morphs_;
-    /// Vertex buffer morph range start.
-    PODVector<unsigned> morphRangeStarts_;
-    /// Vertex buffer morph range vertex count.
-    PODVector<unsigned> morphRangeCounts_;
-    /// Vertex buffer data for asynchronous loading.
-    Vector<VertexBufferDesc> loadVBData_;
-    /// Index buffer data for asynchronous loading.
-    Vector<IndexBufferDesc> loadIBData_;
-    /// Geometry definitions for asynchronous loading.
-    Vector<PODVector<GeometryDesc> > loadGeometries_;
-};
-
-}

+ 0 - 866
Source/Atomic/Atomic3D/ParticleEffect.cpp

@@ -1,866 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/StringUtils.h"
-#include "../Graphics/Material.h"
-#include "../Graphics/ParticleEffect.h"
-#include "../Graphics/BillboardSet.h"
-#include "../IO/Log.h"
-#include "../Resource/ResourceCache.h"
-#include "../Resource/XMLFile.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-extern const char* faceCameraModeNames[];
-
-static const char* emitterTypeNames[] =
-{
-    "Sphere",
-    "Box",
-    0
-};
-
-static const Vector2 DEFAULT_PARTICLE_SIZE(0.1f, 0.1f);
-static const float DEFAULT_EMISSION_RATE = 10.0f;
-static const float MIN_EMISSION_RATE = 0.01f;
-static const float DEFAULT_TIME_TO_LIVE = 1.0f;
-static const float DEFAULT_VELOCITY = 1.0f;
-static const Vector3 DEFAULT_DIRECTION_MIN(-1.0f, -1.0f, -1.0f);
-static const Vector3 DEFAULT_DIRECTION_MAX(1.0f, 1.0f, 1.0f);
-
-ParticleEffect::ParticleEffect(Context* context) :
-    Resource(context),
-    numParticles_(DEFAULT_NUM_PARTICLES),
-    updateInvisible_(false),
-    relative_(true),
-    scaled_(true),
-    sorted_(false),
-    fixedScreenSize_(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.0f),
-    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),
-    faceCameraMode_(FC_ROTATE_XYZ)
-{
-}
-
-ParticleEffect::~ParticleEffect()
-{
-}
-
-void ParticleEffect::RegisterObject(Context* context)
-{
-    context->RegisterFactory<ParticleEffect>();
-}
-
-bool ParticleEffect::BeginLoad(Deserializer& source)
-{
-    loadMaterialName_.Clear();
-
-    XMLFile file(context_);
-    if (!file.Load(source))
-    {
-        ATOMIC_LOGERROR("Load particle effect file failed");
-        return false;
-    }
-
-    XMLElement rootElem = file.GetRoot();
-
-    bool success = Load(rootElem);
-    if (success)
-        SetMemoryUse(source.GetSize());
-    return success;
-}
-
-bool ParticleEffect::EndLoad()
-{
-    // Apply the material now
-    if (!loadMaterialName_.Empty())
-    {
-        SetMaterial(GetSubsystem<ResourceCache>()->GetResource<Material>(loadMaterialName_));
-        loadMaterialName_.Clear();
-    }
-
-    return true;
-}
-
-bool ParticleEffect::Load(const XMLElement& source)
-{
-    // 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;
-    fixedScreenSize_ = 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();
-    faceCameraMode_ = FC_ROTATE_XYZ;
-
-    if (source.IsNull())
-    {
-        ATOMIC_LOGERROR("Can not load particle effect from null XML element");
-        return false;
-    }
-
-    if (source.HasChild("material"))
-    {
-        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"))
-        SetNumParticles((unsigned)source.GetChild("numparticles").GetInt("value"));
-
-    if (source.HasChild("updateinvisible"))
-        updateInvisible_ = source.GetChild("updateinvisible").GetBool("enable");
-
-    if (source.HasChild("relative"))
-        relative_ = source.GetChild("relative").GetBool("enable");
-
-    if (source.HasChild("scaled"))
-        scaled_ = source.GetChild("scaled").GetBool("enable");
-
-    if (source.HasChild("sorted"))
-        sorted_ = source.GetChild("sorted").GetBool("enable");
-
-    if (source.HasChild("fixedscreensize"))
-        fixedScreenSize_ = source.GetChild("fixedscreensize").GetBool("enable");
-
-    if (source.HasChild("animlodbias"))
-        SetAnimationLodBias(source.GetChild("animlodbias").GetFloat("value"));
-
-    if (source.HasChild("emittertype"))
-    {
-        String type = source.GetChild("emittertype").GetAttributeLower("value");
-        if (type == "point")
-        {
-            // Point emitter type is deprecated, handled as zero sized sphere
-            emitterType_ = EMITTER_SPHERE;
-            emitterSize_ = Vector3::ZERO;
-        }
-        else
-            emitterType_ = (EmitterType)GetStringListIndex(type.CString(), emitterTypeNames, EMITTER_SPHERE);
-    }
-
-    if (source.HasChild("emittersize"))
-        emitterSize_ = source.GetChild("emittersize").GetVector3("value");
-
-    if (source.HasChild("emitterradius"))
-        emitterSize_.x_ = emitterSize_.y_ = emitterSize_.z_ = source.GetChild("emitterradius").GetFloat("value");
-
-    if (source.HasChild("direction"))
-        GetVector3MinMax(source.GetChild("direction"), directionMin_, directionMax_);
-
-    if (source.HasChild("constantforce"))
-        constantForce_ = source.GetChild("constantforce").GetVector3("value");
-
-    if (source.HasChild("dampingforce"))
-        dampingForce_ = source.GetChild("dampingforce").GetFloat("value");
-
-    if (source.HasChild("activetime"))
-        activeTime_ = source.GetChild("activetime").GetFloat("value");
-    if (activeTime_ < 0.0f)
-        activeTime_ = M_INFINITY;
-
-    if (source.HasChild("inactivetime"))
-        inactiveTime_ = source.GetChild("inactivetime").GetFloat("value");
-    if (inactiveTime_ < 0.0f)
-        inactiveTime_ = M_INFINITY;
-
-    if (source.HasChild("emissionrate"))
-        GetFloatMinMax(source.GetChild("emissionrate"), emissionRateMin_, emissionRateMax_);
-
-    if (source.HasChild("interval"))
-    {
-        float intervalMin = 0.0f;
-        float intervalMax = 0.0f;
-        GetFloatMinMax(source.GetChild("interval"), intervalMin, intervalMax);
-        emissionRateMax_ = 1.0f / intervalMin;
-        emissionRateMin_ = 1.0f / intervalMax;
-    }
-
-    if (source.HasChild("particlesize"))
-        GetVector2MinMax(source.GetChild("particlesize"), sizeMin_, sizeMax_);
-
-    if (source.HasChild("timetolive"))
-        GetFloatMinMax(source.GetChild("timetolive"), timeToLiveMin_, timeToLiveMax_);
-
-    if (source.HasChild("velocity"))
-        GetFloatMinMax(source.GetChild("velocity"), velocityMin_, velocityMax_);
-
-    if (source.HasChild("rotation"))
-        GetFloatMinMax(source.GetChild("rotation"), rotationMin_, rotationMax_);
-
-    if (source.HasChild("rotationspeed"))
-        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"))
-    {
-        XMLElement deltaElem = source.GetChild("sizedelta");
-        if (deltaElem.HasAttribute("add"))
-            sizeAdd_ = deltaElem.GetFloat("add");
-        if (deltaElem.HasAttribute("mul"))
-            sizeMul_ = deltaElem.GetFloat("mul");
-    }
-
-    if (source.HasChild("color"))
-    {
-        ColorFrame colorFrame(source.GetChild("color").GetColor("value"));
-        SetColorFrame(0, colorFrame);
-    }
-
-    if (source.HasChild("colorfade"))
-    {
-        Vector<ColorFrame> fades;
-        for (XMLElement colorFadeElem = source.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 (source.HasChild("texanim"))
-    {
-        Vector<TextureFrame> animations;
-        for (XMLElement animElem = source.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 ParticleEffect::Save(Serializer& dest) const
-{
-    SharedPtr<XMLFile> xml(new XMLFile(context_));
-    XMLElement materialElem = xml->CreateRoot("particleeffect");
-
-    Save(materialElem);
-    return xml->Save(dest);
-}
-
-bool ParticleEffect::Save(XMLElement& dest) const
-{
-    if (dest.IsNull())
-    {
-        ATOMIC_LOGERROR("Can not save particle effect to null XML element");
-        return false;
-    }
-
-    XMLElement childElem = dest.CreateChild("material");
-    childElem.SetAttribute("name", GetResourceName(material_));
-
-    childElem = dest.CreateChild("numparticles");
-    childElem.SetInt("value", numParticles_);
-
-    childElem = dest.CreateChild("updateinvisible");
-    childElem.SetBool("enable", updateInvisible_);
-
-    childElem = dest.CreateChild("relative");
-    childElem.SetBool("enable", relative_);
-
-    childElem = dest.CreateChild("scaled");
-    childElem.SetBool("enable", scaled_);
-
-    childElem = dest.CreateChild("sorted");
-    childElem.SetBool("enable", sorted_);
-
-    childElem = dest.CreateChild("fixedscreensize");
-    childElem.SetBool("enable", fixedScreenSize_);
-
-    childElem = dest.CreateChild("animlodbias");
-    childElem.SetFloat("value", animationLodBias_);
-
-    childElem = dest.CreateChild("emittertype");
-    childElem.SetAttribute("value", emitterTypeNames[emitterType_]);
-
-    childElem = dest.CreateChild("emittersize");
-    childElem.SetVector3("value", emitterSize_);
-
-    childElem = dest.CreateChild("direction");
-    childElem.SetVector3("min", directionMin_);
-    childElem.SetVector3("max", directionMax_);
-
-    childElem = dest.CreateChild("constantforce");
-    childElem.SetVector3("value", constantForce_);
-
-    childElem = dest.CreateChild("dampingforce");
-    childElem.SetFloat("value", dampingForce_);
-
-    childElem = dest.CreateChild("activetime");
-    childElem.SetFloat("value", activeTime_);
-
-    childElem = dest.CreateChild("inactivetime");
-    childElem.SetFloat("value", inactiveTime_);
-
-    childElem = dest.CreateChild("emissionrate");
-    childElem.SetFloat("min", emissionRateMin_);
-    childElem.SetFloat("max", emissionRateMax_);
-
-    childElem = dest.CreateChild("particlesize");
-    childElem.SetVector2("min", sizeMin_);
-    childElem.SetVector2("max", sizeMax_);
-
-    childElem = dest.CreateChild("timetolive");
-    childElem.SetFloat("min", timeToLiveMin_);
-    childElem.SetFloat("max", timeToLiveMax_);
-
-    childElem = dest.CreateChild("velocity");
-    childElem.SetFloat("min", velocityMin_);
-    childElem.SetFloat("max", velocityMax_);
-
-    childElem = dest.CreateChild("rotation");
-    childElem.SetFloat("min", rotationMin_);
-    childElem.SetFloat("max", rotationMax_);
-
-    childElem = dest.CreateChild("rotationspeed");
-    childElem.SetFloat("min", rotationSpeedMin_);
-    childElem.SetFloat("max", rotationSpeedMax_);
-
-    childElem = dest.CreateChild("sizedelta");
-    childElem.SetFloat("add", sizeAdd_);
-    childElem.SetFloat("mul", sizeMul_);
-
-    childElem = dest.CreateChild("faceCameraMode");
-    childElem.SetAttribute("value", faceCameraModeNames[faceCameraMode_]);
-
-    if (colorFrames_.Size() == 1)
-    {
-        childElem = dest.CreateChild("color");
-        childElem.SetColor("value", colorFrames_[0].color_);
-    }
-
-    if (colorFrames_.Size() > 1)
-    {
-        for (unsigned i = 0; i < colorFrames_.Size(); ++i)
-        {
-            childElem = dest.CreateChild("colorfade");
-            childElem.SetColor("color", colorFrames_[i].color_);
-            childElem.SetFloat("time", colorFrames_[i].time_);
-        }
-    }
-
-    for (unsigned i = 0; i < textureFrames_.Size(); ++i)
-    {
-        childElem = dest.CreateChild("texanim");
-        childElem.SetRect("uv", textureFrames_[i].uv_);
-        childElem.SetFloat("time", textureFrames_[i].time_);
-    }
-
-    return true;
-}
-
-void ParticleEffect::SetMaterial(Material* material)
-{
-    material_ = material;
-}
-
-void ParticleEffect::SetNumParticles(unsigned num)
-{
-    numParticles_ = Max(0U, num);
-}
-
-void ParticleEffect::SetUpdateInvisible(bool enable)
-{
-    updateInvisible_ = enable;
-}
-
-void ParticleEffect::SetRelative(bool enable)
-{
-    relative_ = enable;
-}
-
-void ParticleEffect::SetScaled(bool enable)
-{
-    scaled_ = enable;
-}
-
-void ParticleEffect::SetSorted(bool enable)
-{
-    sorted_ = enable;
-}
-
-void ParticleEffect::SetFixedScreenSize(bool enable)
-{
-    fixedScreenSize_ = enable;
-}
-
-void ParticleEffect::SetAnimationLodBias(float lodBias)
-{
-    animationLodBias_ = lodBias;
-}
-
-void ParticleEffect::SetEmitterType(EmitterType type)
-{
-    emitterType_ = type;
-}
-
-void ParticleEffect::SetEmitterSize(const Vector3& size)
-{
-    emitterSize_ = size;
-}
-
-void ParticleEffect::SetMinDirection(const Vector3& direction)
-{
-    directionMin_ = direction;
-}
-
-void ParticleEffect::SetMaxDirection(const Vector3& direction)
-{
-    directionMax_ = direction;
-}
-
-void ParticleEffect::SetConstantForce(const Vector3& force)
-{
-    constantForce_ = force;
-}
-
-void ParticleEffect::SetDampingForce(float force)
-{
-    dampingForce_ = force;
-}
-
-void ParticleEffect::SetActiveTime(float time)
-{
-    activeTime_ = time;
-}
-
-void ParticleEffect::SetInactiveTime(float time)
-{
-    inactiveTime_ = time;
-}
-
-void ParticleEffect::SetMinEmissionRate(float rate)
-{
-    emissionRateMin_ = Max(rate, MIN_EMISSION_RATE);
-}
-
-void ParticleEffect::SetMaxEmissionRate(float rate)
-{
-    emissionRateMax_ = Max(rate, MIN_EMISSION_RATE);
-}
-
-void ParticleEffect::SetMinParticleSize(const Vector2& size)
-{
-    sizeMin_ = size;
-}
-
-void ParticleEffect::SetMaxParticleSize(const Vector2& size)
-{
-    sizeMax_ = size;
-}
-
-void ParticleEffect::SetMinTimeToLive(float time)
-{
-    timeToLiveMin_ = Max(time, 0.0f);
-}
-
-void ParticleEffect::SetMaxTimeToLive(float time)
-{
-    timeToLiveMax_ = Max(time, 0.0f);
-}
-
-void ParticleEffect::SetMinVelocity(float velocity)
-{
-    velocityMin_ = velocity;
-}
-
-void ParticleEffect::SetMaxVelocity(float velocity)
-{
-    velocityMax_ = velocity;
-}
-
-void ParticleEffect::SetMinRotation(float rotation)
-{
-    rotationMin_ = rotation;
-}
-
-void ParticleEffect::SetMaxRotation(float rotation)
-{
-    rotationMax_ = rotation;
-}
-
-void ParticleEffect::SetMinRotationSpeed(float speed)
-{
-    rotationSpeedMin_ = speed;
-}
-
-void ParticleEffect::SetMaxRotationSpeed(float speed)
-{
-    rotationSpeedMax_ = speed;
-}
-
-
-void ParticleEffect::SetSizeAdd(float sizeAdd)
-{
-    sizeAdd_ = sizeAdd;
-}
-
-void ParticleEffect::SetSizeMul(float sizeMul)
-{
-    sizeMul_ = sizeMul;
-}
-
-void ParticleEffect::AddColorTime(const Color& color, const float time)
-{
-    unsigned s = colorFrames_.Size();
-    colorFrames_.Resize(s + 1);
-
-    for (unsigned i = 0; i < s; i++)
-    {
-        if (colorFrames_[i].time_ > time)
-        {
-            for (unsigned j = s; j > i; j--)
-            {
-                colorFrames_[j].color_ = colorFrames_[j - 1].color_;
-                colorFrames_[j].time_ = colorFrames_[j - 1].time_;
-            }
-            colorFrames_[i].color_ = color;
-            colorFrames_[i].time_ = time;
-            return;
-        }
-    }
-
-    // highest time, add last:
-    colorFrames_[s].color_ = color;
-    colorFrames_[s].time_ = time;
-}
-
-void ParticleEffect::AddColorFrame(const ColorFrame& colorFrame)
-{
-    AddColorTime(colorFrame.color_, colorFrame.time_);
-}
-
-void ParticleEffect::RemoveColorFrame(unsigned index)
-{
-    unsigned s = colorFrames_.Size();
-
-    for (unsigned i = index; i < s - 1; i++)
-    {
-        colorFrames_[i].color_ = colorFrames_[i + 1].color_;
-        colorFrames_[i].time_ = colorFrames_[i + 1].time_;
-    }
-
-    colorFrames_.Resize(s - 1);
-}
-
-void ParticleEffect::SetColorFrames(const Vector<ColorFrame>& colorFrames)
-{
-    colorFrames_ = colorFrames;
-}
-
-void ParticleEffect::SetColorFrame(unsigned index, const ColorFrame& colorFrame)
-{
-    if (colorFrames_.Size() < index + 1)
-        colorFrames_.Resize(index + 1);
-    colorFrames_[index] = colorFrame;
-}
-
-void ParticleEffect::SetNumColorFrames(unsigned number)
-{
-    unsigned s = colorFrames_.Size();
-    if (s != number)
-        colorFrames_.Resize(number);
-}
-
-void ParticleEffect::SetFaceCameraMode(FaceCameraMode mode)
-{
-    faceCameraMode_ = mode;
-}
-
-void ParticleEffect::SortColorFrames()
-{
-    Vector<ColorFrame> cf = colorFrames_;
-    colorFrames_.Clear();
-    for (unsigned i = 0; i < cf.Size(); i++)
-        AddColorFrame(cf[i]);
-}
-
-void ParticleEffect::AddTextureTime(const Rect& uv, const float time)
-{
-    unsigned s = textureFrames_.Size();
-    textureFrames_.Resize(s + 1);
-
-    for (unsigned i = 0; i < s; i++)
-    {
-        if (textureFrames_[i].time_ > time)
-        {
-            for (unsigned j = s; j > i; j--)
-            {
-                textureFrames_[j].uv_ = textureFrames_[j - 1].uv_;
-                textureFrames_[j].time_ = textureFrames_[j - 1].time_;
-            }
-            textureFrames_[i].uv_ = uv;
-            textureFrames_[i].time_ = time;
-            return;
-        }
-    }
-
-    // Highest time, add last
-    textureFrames_[s].uv_ = uv;
-    textureFrames_[s].time_ = time;
-}
-
-void ParticleEffect::AddTextureFrame(const TextureFrame& textureFrame)
-{
-    AddTextureTime(textureFrame.uv_, textureFrame.time_);
-}
-
-void ParticleEffect::RemoveTextureFrame(unsigned index)
-{
-    unsigned s = textureFrames_.Size();
-
-    for (unsigned i = index; i < s - 1; i++)
-    {
-        textureFrames_[i].uv_ = textureFrames_[i + 1].uv_;
-        textureFrames_[i].time_ = textureFrames_[i + 1].time_;
-    }
-
-    textureFrames_.Resize(s - 1);
-}
-
-void ParticleEffect::SetTextureFrames(const Vector<TextureFrame>& textureFrames)
-{
-    textureFrames_ = textureFrames;
-}
-
-void ParticleEffect::SetTextureFrame(unsigned index, const TextureFrame& textureFrame)
-{
-    if (textureFrames_.Size() < index + 1)
-        textureFrames_.Resize(index + 1);
-    textureFrames_[index] = textureFrame;
-}
-
-void ParticleEffect::SetNumTextureFrames(unsigned number)
-{
-    unsigned s = textureFrames_.Size();
-    if (s != number)
-        textureFrames_.Resize(number);
-}
-
-void ParticleEffect::SortTextureFrames()
-{
-    Vector<TextureFrame> tf = textureFrames_;
-    textureFrames_.Clear();
-    for (unsigned i = 0; i < tf.Size(); 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
-{
-    return index < colorFrames_.Size() ? &colorFrames_[index] : (ColorFrame*)0;
-}
-
-const TextureFrame* ParticleEffect::GetTextureFrame(unsigned index) const
-{
-    return index < textureFrames_.Size() ? &textureFrames_[index] : (TextureFrame*)0;
-}
-
-Vector3 ParticleEffect::GetRandomDirection() const
-{
-    return Vector3(Lerp(directionMin_.x_, directionMax_.x_, Random(1.0f)), Lerp(directionMin_.y_, directionMax_.y_, Random(1.0f)),
-        Lerp(directionMin_.z_, directionMax_.z_, Random(1.0f)));
-}
-
-Vector2 ParticleEffect::GetRandomSize() const
-{
-    return sizeMin_.Lerp(sizeMax_, Random(1.0f));
-}
-
-float ParticleEffect::GetRandomVelocity() const
-{
-    return Lerp(velocityMin_, velocityMax_, Random(1.0f));
-}
-
-float ParticleEffect::GetRandomTimeToLive() const
-{
-    return Lerp(timeToLiveMin_, timeToLiveMax_, Random(1.0f));
-}
-
-float ParticleEffect::GetRandomRotationSpeed() const
-{
-    return Lerp(rotationSpeedMin_, rotationSpeedMax_, Random(1.0f));
-}
-
-float ParticleEffect::GetRandomRotation() const
-{
-    return Lerp(rotationMin_, rotationMax_, Random(1.0f));
-}
-
-void ParticleEffect::GetFloatMinMax(const XMLElement& element, float& minValue, float& maxValue)
-{
-    if (element.IsNull())
-        return;
-
-    if (element.HasAttribute("value"))
-        minValue = maxValue = element.GetFloat("value");
-
-    if (element.HasAttribute("min") && element.HasAttribute("max"))
-    {
-        minValue = element.GetFloat("min");
-        maxValue = element.GetFloat("max");
-    }
-}
-
-void ParticleEffect::GetVector2MinMax(const XMLElement& element, Vector2& minValue, Vector2& maxValue)
-{
-    if (element.IsNull())
-        return;
-
-    if (element.HasAttribute("value"))
-        minValue = maxValue = element.GetVector2("value");
-
-    if (element.HasAttribute("min") && element.HasAttribute("max"))
-    {
-        minValue = element.GetVector2("min");
-        maxValue = element.GetVector2("max");
-    }
-}
-
-void ParticleEffect::GetVector3MinMax(const XMLElement& element, Vector3& minValue, Vector3& maxValue)
-{
-    if (element.IsNull())
-        return;
-
-    if (element.HasAttribute("value"))
-        minValue = maxValue = element.GetVector3("value");
-
-    if (element.HasAttribute("min") && element.HasAttribute("max"))
-    {
-        minValue = element.GetVector3("min");
-        maxValue = element.GetVector3("max");
-    }
-}
-
-
-}

+ 0 - 423
Source/Atomic/Atomic3D/ParticleEffect.h

@@ -1,423 +0,0 @@
-//
-// 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 "../Graphics/GraphicsDefs.h"
-#include "../Resource/Resource.h"
-
-namespace Atomic
-{
-
-/// Particle emitter shapes.
-enum EmitterType
-{
-    EMITTER_SPHERE = 0,
-    EMITTER_BOX
-};
-
-/// %Color animation frame definition.
-struct ColorFrame
-{
-    /// Construct with default values.
-    ColorFrame() :
-        time_(0.0f)
-    {
-    }
-
-    /// Construct with a color and zero time.
-    ColorFrame(const Color& color) :
-        color_(color),
-        time_(0.0f)
-    {
-    }
-
-    /// Construct from a color and time.
-    ColorFrame(const Color& color, float time) :
-        color_(color),
-        time_(time)
-    {
-    }
-
-    /// Return interpolated value with another color-time pair at the time specified.
-    Color Interpolate(const ColorFrame& next, float time) const
-    {
-        float timeInterval = next.time_ - time_;
-        if (timeInterval > 0.0f)
-        {
-            float t = (time - time_) / timeInterval;
-            return color_.Lerp(next.color_, t);
-        }
-        else
-            return next.color_;
-    }
-
-    /// Color.
-    Color color_;
-    /// Time.
-    float time_;
-};
-
-/// %Texture animation frame definition.
-struct TextureFrame
-{
-    /// Construct with default values.
-    TextureFrame() :
-        uv_(0.0f, 0.0f, 1.0f, 1.0f),
-        time_(0.0f)
-    {
-    }
-
-    /// UV coordinates.
-    Rect uv_;
-    /// Time.
-    float time_;
-};
-
-static const unsigned DEFAULT_NUM_PARTICLES = 10;
-
-class Material;
-class XMLFile;
-class XMLElement;
-
-/// %Particle effect definition.
-class ATOMIC_API ParticleEffect : public Resource
-{
-    ATOMIC_OBJECT(ParticleEffect, Resource);
-
-public:
-    /// Construct.
-    ParticleEffect(Context* context);
-    /// Destruct.
-    virtual ~ParticleEffect();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-
-    /// Load resource from stream. May be called from a worker thread. Return true if successful.
-    virtual bool BeginLoad(Deserializer& source);
-    /// Finish resource loading. Always called from the main thread. Return true if successful.
-    virtual bool EndLoad();
-    /// Save resource. Return true if successful.
-    virtual bool Save(Serializer& dest) const;
-
-    /// Save resource to XMLElement. Return true if successful.
-    bool Save(XMLElement& dest) const;
-    /// Load resource from XMLElement synchronously. Return true if successful.
-    bool Load(const XMLElement& source);
-    /// Set material.
-    void SetMaterial(Material* material);
-    /// Set maximum number of particles.
-    void SetNumParticles(unsigned num);
-    /// Set whether to update when particles are not visible.
-    void SetUpdateInvisible(bool enable);
-    /// Set whether billboards are relative to the scene node.
-    void SetRelative(bool enable);
-    /// Set whether scene node scale affects billboards' size.
-    void SetScaled(bool enable);
-    /// Set whether billboards are sorted by distance.
-    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.
-    void SetAnimationLodBias(float lodBias);
-    /// Set emitter type.
-    void SetEmitterType(EmitterType type);
-    /// Set emitter size.
-    void SetEmitterSize(const Vector3& size);
-    /// Set negative direction limit.
-    void SetMinDirection(const Vector3& direction);
-    /// Set positive direction limit.
-    void SetMaxDirection(const Vector3& direction);
-    /// Set constant force acting on particles.
-    void SetConstantForce(const Vector3& force);
-    /// Set particle velocity damping force.
-    void SetDampingForce(float force);
-    /// Set emission active period length (0 = infinite.)
-    void SetActiveTime(float time);
-    /// Set emission inactive period length (0 = infinite.)
-    void SetInactiveTime(float time);
-    /// Set minimum emission rate.
-    void SetMinEmissionRate(float rate);
-    /// Set maximum emission rate.
-    void SetMaxEmissionRate(float rate);
-    /// Set particle minimum size.
-    void SetMinParticleSize(const Vector2& size);
-    /// Set particle maximum size.
-    void SetMaxParticleSize(const Vector2& size);
-    /// Set particle minimum time to live.
-    void SetMinTimeToLive(float time);
-    /// Set particle maximum time to live.
-    void SetMaxTimeToLive(float time);
-    /// Set particle minimum velocity.
-    void SetMinVelocity(float velocity);
-    /// Set particle maximum velocity.
-    void SetMaxVelocity(float velocity);
-    /// Set particle minimum rotation.
-    void SetMinRotation(float rotation);
-    /// Set particle maximum rotation.
-    void SetMaxRotation(float rotation);
-    /// Set particle minimum rotation speed.
-    void SetMinRotationSpeed(float speed);
-    /// Set particle maximum rotation speed.
-    void SetMaxRotationSpeed(float speed);
-    /// Set particle size additive modifier.
-    void SetSizeAdd(float sizeAdd);
-    /// Set particle size multiplicative modifier.
-    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.
-    void AddColorTime(const Color& color, const float time);
-    /// Add a color frame sorted in the correct position based on time.
-    void AddColorFrame(const ColorFrame& colorFrame);
-    /// Remove color frame at index
-    void RemoveColorFrame(unsigned index);
-    /// Set color animation of particles.
-    void SetColorFrames(const Vector<ColorFrame>& colorFrames);
-    /// Set color animation frame at index. If index is greater than number of color frames, new color frames are added.
-    void SetColorFrame(unsigned index, const ColorFrame& colorFrame);
-    /// Set number of color frames.
-    void SetNumColorFrames(unsigned number);
-    /// Sort the list of color frames based on time.
-    void SortColorFrames();
-
-    /// Add a texture frame sorted in the correct position based on time.
-    void AddTextureTime(const Rect& uv, const float time);
-    /// Add a texture frame sorted in the correct position based on time.
-    void AddTextureFrame(const TextureFrame& textureFrame);
-    /// Remove texture frame at index
-    void RemoveTextureFrame(unsigned index);
-    /// Set particle texture animation.
-    void SetTextureFrames(const Vector<TextureFrame>& animation);
-    /// Set number of texture animation frames.
-    void SetTextureFrame(unsigned index, const TextureFrame& textureFrame);
-    /// Set number of texture frames.
-    void SetNumTextureFrames(unsigned number);
-    /// Sort the list of texture frames based on time.
-    void SortTextureFrames();
-    /// Clone the particle effect.
-    SharedPtr<ParticleEffect> Clone(const String& cloneName = String::EMPTY) const;
-
-    /// Return material.
-    Material* GetMaterial() const { return material_; }
-
-    /// Return maximum number of particles.
-    unsigned GetNumParticles() const { return numParticles_; }
-
-    /// Return whether to update when particles are not visible.
-    bool GetUpdateInvisible() const { return updateInvisible_; }
-
-    /// Return whether billboards are relative to the scene node.
-    bool IsRelative() const { return relative_; }
-
-    /// Return whether scene node scale affects billboards' size.
-    bool IsScaled() const { return scaled_; }
-
-    /// Return whether billboards are sorted.
-    bool IsSorted() const { return sorted_; }
-
-    /// Return whether billboards are fixed screen size.
-    bool IsFixedScreenSize() const { return fixedScreenSize_; }
-
-    /// Return animation Lod bias.
-    float GetAnimationLodBias() const { return animationLodBias_; }
-
-    /// Return emitter type.
-    EmitterType GetEmitterType() const { return emitterType_; }
-
-    /// Return emitter size.
-    const Vector3& GetEmitterSize() const { return emitterSize_; }
-
-    /// Return negative direction limit.
-    const Vector3& GetMinDirection() const { return directionMin_; }
-
-    /// Return positive direction limit.
-    const Vector3& GetMaxDirection() const { return directionMax_; }
-
-    /// Return constant force acting on particles.
-    const Vector3& GetConstantForce() const { return constantForce_; }
-
-    /// Return particle velocity damping force.
-    float GetDampingForce() const { return dampingForce_; }
-
-    /// Return emission active period length (0 = infinite.)
-    float GetActiveTime() const { return activeTime_; }
-
-    /// Return emission inactive period length (0 = infinite.)
-    float GetInactiveTime() const { return inactiveTime_; }
-
-    /// Return minimum emission rate.
-    float GetMinEmissionRate() const { return emissionRateMin_; }
-
-    /// Return maximum emission rate.
-    float GetMaxEmissionRate() const { return emissionRateMax_; }
-
-    /// Return particle minimum size.
-    const Vector2& GetMinParticleSize() const { return sizeMin_; }
-
-    /// Return particle maximum size.
-    const Vector2& GetMaxParticleSize() const { return sizeMax_; }
-
-    /// Return particle minimum time to live.
-    float GetMinTimeToLive() const { return timeToLiveMin_; }
-
-    /// Return particle maximum time to live.
-    float GetMaxTimeToLive() const { return timeToLiveMax_; }
-
-    /// Return particle minimum velocity.
-    float GetMinVelocity() const { return velocityMin_; }
-
-    /// Return particle maximum velocity.
-    float GetMaxVelocity() const { return velocityMax_; }
-
-    /// Return particle minimum rotation.
-    float GetMinRotation() const { return rotationMin_; }
-
-    /// Return particle maximum rotation.
-    float GetMaxRotation() const { return rotationMax_; }
-
-    /// Return particle minimum rotation speed.
-    float GetMinRotationSpeed() const { return rotationSpeedMin_; }
-
-    /// Return particle maximum rotation speed.
-    float GetMaxRotationSpeed() const { return rotationSpeedMax_; }
-
-    /// Return particle size additive modifier.
-    float GetSizeAdd() const { return sizeAdd_; }
-
-    /// Return particle size multiplicative modifier.
-    float GetSizeMul() const { return sizeMul_; }
-
-    /// Return all color animation frames.
-    const Vector<ColorFrame>& GetColorFrames() const { return colorFrames_; }
-
-    /// Return number of color animation frames.
-    unsigned GetNumColorFrames() const { return colorFrames_.Size(); }
-
-    /// Return a color animation frame, or null if outside range.
-    const ColorFrame* GetColorFrame(unsigned index) const;
-
-    /// Return all texture animation frames.
-    const Vector<TextureFrame>& GetTextureFrames() const { return textureFrames_; }
-
-    /// Return number of texture animation frames.
-    unsigned GetNumTextureFrames() const { return textureFrames_.Size(); }
-
-    /// Return a texture animation frame, or null if outside range.
-    const TextureFrame* GetTextureFrame(unsigned index) const;
-
-    /// Return how the particles rotate in relation to the camera.
-    FaceCameraMode GetFaceCameraMode() const { return faceCameraMode_; }
-
-    /// Return random direction.
-    Vector3 GetRandomDirection() const;
-    /// Return random size.
-    Vector2 GetRandomSize() const;
-    /// Return random velocity.
-    float GetRandomVelocity() const;
-    /// Return random timetolive.
-    float GetRandomTimeToLive() const;
-    /// Return random rotationspeed.
-    float GetRandomRotationSpeed() const;
-    /// Return random rotation.
-    float GetRandomRotation() const;
-
-private:
-    /// Read a float range from an XML element.
-    void GetFloatMinMax(const XMLElement& element, float& minValue, float& maxValue);
-    /// Read a Vector2 range from an XML element.
-    void GetVector2MinMax(const XMLElement& element, Vector2& minValue, Vector2& maxValue);
-    /// Read a Vector3 from an XML element.
-    void GetVector3MinMax(const XMLElement& element, Vector3& minValue, Vector3& maxValue);
-
-    /// Material.
-    SharedPtr<Material> material_;
-    /// Number of particles.
-    unsigned numParticles_;
-    /// Update when invisible flag.
-    bool updateInvisible_;
-    /// Billboards relative flag.
-    bool relative_;
-    /// Scale affects billboard scale flag.
-    bool scaled_;
-    /// Billboards sorted flag.
-    bool sorted_;
-    /// Billboards fixed screen size flag.
-    bool fixedScreenSize_;
-    /// Animation LOD bias.
-    float animationLodBias_;
-    /// Emitter shape.
-    EmitterType emitterType_;
-    /// Emitter size.
-    Vector3 emitterSize_;
-    /// Particle direction minimum.
-    Vector3 directionMin_;
-    /// Particle direction maximum.
-    Vector3 directionMax_;
-    /// Particle constant force.
-    Vector3 constantForce_;
-    /// Particle velocity damping force.
-    float dampingForce_;
-    /// Active period.
-    float activeTime_;
-    /// Inactive period.
-    float inactiveTime_;
-    /// Particles per second minimum.
-    float emissionRateMin_;
-    /// Particles per second maximum.
-    float emissionRateMax_;
-    /// Particle size minimum.
-    Vector2 sizeMin_;
-    /// Particle size maximum.
-    Vector2 sizeMax_;
-    /// Particle time to live minimum.
-    float timeToLiveMin_;
-    /// Particle time to live maximum.
-    float timeToLiveMax_;
-    /// Particle velocity minimum.
-    float velocityMin_;
-    /// Particle velocity maximum.
-    float velocityMax_;
-    /// Particle rotation angle minimum.
-    float rotationMin_;
-    /// Particle rotation angle maximum.
-    float rotationMax_;
-    /// Particle rotation speed minimum.
-    float rotationSpeedMin_;
-    /// Particle rotation speed maximum.
-    float rotationSpeedMax_;
-    /// Particle size additive parameter.
-    float sizeAdd_;
-    /// Particle size multiplicative parameter.
-    float sizeMul_;
-    /// Particle color animation frames.
-    Vector<ColorFrame> colorFrames_;
-    /// Texture animation frames.
-    Vector<TextureFrame> textureFrames_;
-    /// Material name acquired during BeginLoad().
-    String loadMaterialName_;
-    /// Particle rotation mode in relation to the camera.
-    FaceCameraMode faceCameraMode_;
-};
-
-}

+ 0 - 577
Source/Atomic/Atomic3D/ParticleEmitter.cpp

@@ -1,577 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/DrawableEvents.h"
-#include "../Graphics/ParticleEffect.h"
-#include "../Graphics/ParticleEmitter.h"
-#include "../Resource/ResourceCache.h"
-#include "../Resource/ResourceEvents.h"
-#include "../Scene/Scene.h"
-#include "../Scene/SceneEvents.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-extern const char* faceCameraModeNames[];
-static const unsigned MAX_PARTICLES_IN_FRAME = 100;
-
-ParticleEmitter::ParticleEmitter(Context* context) :
-    BillboardSet(context),
-    periodTimer_(0.0f),
-    emissionTimer_(0.0f),
-    lastTimeStep_(0.0f),
-    lastUpdateFrameNumber_(M_MAX_UNSIGNED),
-    emitting_(true),
-    needUpdate_(false),
-    serializeParticles_(true),
-    sendFinishEvent_(true)
-{
-    SetNumParticles(DEFAULT_NUM_PARTICLES);
-}
-
-ParticleEmitter::~ParticleEmitter()
-{
-}
-
-void ParticleEmitter::RegisterObject(Context* context)
-{
-    context->RegisterFactory<ParticleEmitter>(GEOMETRY_CATEGORY);
-
-    ATOMIC_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Effect", GetEffectAttr, SetEffectAttr, ResourceRef, ResourceRef(ParticleEffect::GetTypeStatic()),
-        AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Is Emitting", bool, emitting_, true, AM_FILE);
-    ATOMIC_ATTRIBUTE("Period Timer", float, periodTimer_, 0.0f, AM_FILE | AM_NOEDIT);
-    ATOMIC_ATTRIBUTE("Emission Timer", float, emissionTimer_, 0.0f, AM_FILE | AM_NOEDIT);
-    ATOMIC_COPY_BASE_ATTRIBUTES(Drawable);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Particles", GetParticlesAttr, SetParticlesAttr, VariantVector, Variant::emptyVariantVector,
-        AM_FILE | AM_NOEDIT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Billboards", GetParticleBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector,
-        AM_FILE | AM_NOEDIT);
-    ATOMIC_ATTRIBUTE("Serialize Particles", bool, serializeParticles_, true, AM_FILE);
-}
-
-void ParticleEmitter::OnSetEnabled()
-{
-    BillboardSet::OnSetEnabled();
-
-    Scene* scene = GetScene();
-    if (scene)
-    {
-        if (IsEnabledEffective())
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, ATOMIC_HANDLER(ParticleEmitter, HandleScenePostUpdate));
-        else
-            UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
-    }
-}
-
-void ParticleEmitter::Update(const FrameInfo& frame)
-{
-    if (!effect_)
-        return;
-
-    // Cancel update if has only moved but does not actually need to animate the particles
-    if (!needUpdate_)
-        return;
-
-    // If there is an amount mismatch between particles and billboards, correct it
-    if (particles_.Size() != billboards_.Size())
-        SetNumBillboards(particles_.Size());
-
-    bool needCommit = false;
-
-    // Check active/inactive period switching
-    periodTimer_ += lastTimeStep_;
-    if (emitting_)
-    {
-        float activeTime = effect_->GetActiveTime();
-        if (activeTime && periodTimer_ >= activeTime)
-        {
-            emitting_ = false;
-            periodTimer_ -= activeTime;
-        }
-    }
-    else
-    {
-        float inactiveTime = effect_->GetInactiveTime();
-        if (inactiveTime && periodTimer_ >= inactiveTime)
-        {
-            emitting_ = true;
-            sendFinishEvent_ = true;
-            periodTimer_ -= inactiveTime;
-        }
-        // If emitter has an indefinite stop interval, keep period timer reset to allow restarting emission in the editor
-        if (inactiveTime == 0.0f)
-            periodTimer_ = 0.0f;
-    }
-
-    // Check for emitting new particles
-    if (emitting_)
-    {
-        emissionTimer_ += lastTimeStep_;
-
-        float intervalMin = 1.0f / effect_->GetMaxEmissionRate();
-        float intervalMax = 1.0f / effect_->GetMinEmissionRate();
-
-        // If emission timer has a longer delay than max. interval, clamp it
-        if (emissionTimer_ < -intervalMax)
-            emissionTimer_ = -intervalMax;
-
-        unsigned counter = MAX_PARTICLES_IN_FRAME;
-
-        while (emissionTimer_ > 0.0f && counter)
-        {
-            emissionTimer_ -= Lerp(intervalMin, intervalMax, Random(1.0f));
-            if (EmitNewParticle())
-            {
-                --counter;
-                needCommit = true;
-            }
-            else
-                break;
-        }
-    }
-
-    // Update existing particles
-    Vector3 relativeConstantForce = node_->GetWorldRotation().Inverse() * effect_->GetConstantForce();
-    // If billboards are not relative, apply scaling to the position update
-    Vector3 scaleVector = Vector3::ONE;
-    if (scaled_ && !relative_)
-        scaleVector = node_->GetWorldScale();
-
-    for (unsigned i = 0; i < particles_.Size(); ++i)
-    {
-        Particle& particle = particles_[i];
-        Billboard& billboard = billboards_[i];
-
-        if (billboard.enabled_)
-        {
-            needCommit = true;
-
-            // Time to live
-            if (particle.timer_ >= particle.timeToLive_)
-            {
-                billboard.enabled_ = false;
-                continue;
-            }
-            particle.timer_ += lastTimeStep_;
-
-            // Velocity & position
-            const Vector3& constantForce = effect_->GetConstantForce();
-            if (constantForce != Vector3::ZERO)
-            {
-                if (relative_)
-                    particle.velocity_ += lastTimeStep_ * relativeConstantForce;
-                else
-                    particle.velocity_ += lastTimeStep_ * constantForce;
-            }
-
-            float dampingForce = effect_->GetDampingForce();
-            if (dampingForce != 0.0f)
-            {
-                Vector3 force = -dampingForce * particle.velocity_;
-                particle.velocity_ += lastTimeStep_ * force;
-            }
-            billboard.position_ += lastTimeStep_ * particle.velocity_ * scaleVector;
-            billboard.direction_ = particle.velocity_.Normalized();
-
-            // Rotation
-            billboard.rotation_ += lastTimeStep_ * particle.rotationSpeed_;
-
-            // Scaling
-            float sizeAdd = effect_->GetSizeAdd();
-            float sizeMul = effect_->GetSizeMul();
-            if (sizeAdd != 0.0f || sizeMul != 1.0f)
-            {
-                particle.scale_ += lastTimeStep_ * sizeAdd;
-                if (particle.scale_ < 0.0f)
-                    particle.scale_ = 0.0f;
-                if (sizeMul != 1.0f)
-                    particle.scale_ *= (lastTimeStep_ * (sizeMul - 1.0f)) + 1.0f;
-                billboard.size_ = particle.size_ * particle.scale_;
-            }
-
-            // Color interpolation
-            unsigned& index = particle.colorIndex_;
-            const Vector<ColorFrame>& colorFrames_ = effect_->GetColorFrames();
-            if (index < colorFrames_.Size())
-            {
-                if (index < colorFrames_.Size() - 1)
-                {
-                    if (particle.timer_ >= colorFrames_[index + 1].time_)
-                        ++index;
-                }
-                if (index < colorFrames_.Size() - 1)
-                    billboard.color_ = colorFrames_[index].Interpolate(colorFrames_[index + 1], particle.timer_);
-                else
-                    billboard.color_ = colorFrames_[index].color_;
-            }
-
-            // Texture animation
-            unsigned& texIndex = particle.texIndex_;
-            const Vector<TextureFrame>& textureFrames_ = effect_->GetTextureFrames();
-            if (textureFrames_.Size() && texIndex < textureFrames_.Size() - 1)
-            {
-                if (particle.timer_ >= textureFrames_[texIndex + 1].time_)
-                {
-                    billboard.uv_ = textureFrames_[texIndex + 1].uv_;
-                    ++texIndex;
-                }
-            }
-        }
-    }
-
-    if (needCommit)
-        Commit();
-
-    needUpdate_ = false;
-}
-
-void ParticleEmitter::SetEffect(ParticleEffect* effect)
-{
-    if (effect == effect_)
-        return;
-
-    Reset();
-
-    // Unsubscribe from the reload event of previous effect (if any), then subscribe to the new
-    if (effect_)
-        UnsubscribeFromEvent(effect_, E_RELOADFINISHED);
-
-    effect_ = effect;
-
-    if (effect_)
-        SubscribeToEvent(effect_, E_RELOADFINISHED, ATOMIC_HANDLER(ParticleEmitter, HandleEffectReloadFinished));
-
-    ApplyEffect();
-    MarkNetworkUpdate();
-}
-
-void ParticleEmitter::SetNumParticles(unsigned num)
-{
-    // Prevent negative value being assigned from the editor
-    if (num > M_MAX_INT)
-        num = 0;
-    if (num > MAX_BILLBOARDS)
-        num = MAX_BILLBOARDS;
-
-    particles_.Resize(num);
-    SetNumBillboards(num);
-}
-
-void ParticleEmitter::SetEmitting(bool enable)
-{
-    if (enable != emitting_)
-    {
-        emitting_ = enable;
-        sendFinishEvent_ = enable;
-        periodTimer_ = 0.0f;
-        // Note: network update does not need to be marked as this is a file only attribute
-    }
-}
-
-void ParticleEmitter::SetSerializeParticles(bool enable)
-{
-    serializeParticles_ = enable;
-    // Note: network update does not need to be marked as this is a file only attribute
-}
-
-void ParticleEmitter::ResetEmissionTimer()
-{
-    emissionTimer_ = 0.0f;
-}
-
-void ParticleEmitter::RemoveAllParticles()
-{
-    for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
-        i->enabled_ = false;
-
-    Commit();
-}
-
-void ParticleEmitter::Reset()
-{
-    RemoveAllParticles();
-    ResetEmissionTimer();
-    SetEmitting(true);
-}
-
-void ParticleEmitter::ApplyEffect()
-{
-    if (!effect_)
-        return;
-
-    SetMaterial(effect_->GetMaterial());
-    SetNumParticles(effect_->GetNumParticles());
-    SetRelative(effect_->IsRelative());
-    SetScaled(effect_->IsScaled());
-    SetSorted(effect_->IsSorted());
-    SetFixedScreenSize(effect_->IsFixedScreenSize());
-    SetAnimationLodBias(effect_->GetAnimationLodBias());
-    SetFaceCameraMode(effect_->GetFaceCameraMode());
-}
-
-ParticleEffect* ParticleEmitter::GetEffect() const
-{
-    return effect_;
-}
-
-void ParticleEmitter::SetEffectAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    SetEffect(cache->GetResource<ParticleEffect>(value.name_));
-}
-
-ResourceRef ParticleEmitter::GetEffectAttr() const
-{
-    return GetResourceRef(effect_, ParticleEffect::GetTypeStatic());
-}
-
-void ParticleEmitter::SetParticlesAttr(const VariantVector& value)
-{
-    unsigned index = 0;
-    SetNumParticles(index < value.Size() ? value[index++].GetUInt() : 0);
-
-    for (PODVector<Particle>::Iterator i = particles_.Begin(); i != particles_.End() && index < value.Size(); ++i)
-    {
-        i->velocity_ = value[index++].GetVector3();
-        i->size_ = value[index++].GetVector2();
-        i->timer_ = value[index++].GetFloat();
-        i->timeToLive_ = value[index++].GetFloat();
-        i->scale_ = value[index++].GetFloat();
-        i->rotationSpeed_ = value[index++].GetFloat();
-        i->colorIndex_ = (unsigned)value[index++].GetInt();
-        i->texIndex_ = (unsigned)value[index++].GetInt();
-    }
-}
-
-VariantVector ParticleEmitter::GetParticlesAttr() const
-{
-    VariantVector ret;
-    if (!serializeParticles_)
-    {
-        ret.Push(particles_.Size());
-        return ret;
-    }
-
-    ret.Reserve(particles_.Size() * 8 + 1);
-    ret.Push(particles_.Size());
-    for (PODVector<Particle>::ConstIterator i = particles_.Begin(); i != particles_.End(); ++i)
-    {
-        ret.Push(i->velocity_);
-        ret.Push(i->size_);
-        ret.Push(i->timer_);
-        ret.Push(i->timeToLive_);
-        ret.Push(i->scale_);
-        ret.Push(i->rotationSpeed_);
-        ret.Push(i->colorIndex_);
-        ret.Push(i->texIndex_);
-    }
-    return ret;
-}
-
-VariantVector ParticleEmitter::GetParticleBillboardsAttr() const
-{
-    VariantVector ret;
-    if (!serializeParticles_)
-    {
-        ret.Push(billboards_.Size());
-        return ret;
-    }
-
-    ret.Reserve(billboards_.Size() * 7 + 1);
-    ret.Push(billboards_.Size());
-
-    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
-    {
-        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;
-}
-
-void ParticleEmitter::OnSceneSet(Scene* scene)
-{
-    BillboardSet::OnSceneSet(scene);
-
-    if (scene && IsEnabledEffective())
-        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, ATOMIC_HANDLER(ParticleEmitter, HandleScenePostUpdate));
-    else if (!scene)
-         UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
-}
-
-bool ParticleEmitter::EmitNewParticle()
-{
-    unsigned index = GetFreeParticle();
-    if (index == M_MAX_UNSIGNED)
-        return false;
-    assert(index < particles_.Size());
-    Particle& particle = particles_[index];
-    Billboard& billboard = billboards_[index];
-
-    Vector3 startDir;
-    Vector3 startPos;
-
-    startDir = effect_->GetRandomDirection();
-    startDir.Normalize();
-
-    switch (effect_->GetEmitterType())
-    {
-    case EMITTER_SPHERE:
-        {
-            Vector3 dir(
-                Random(2.0f) - 1.0f,
-                Random(2.0f) - 1.0f,
-                Random(2.0f) - 1.0f
-            );
-            dir.Normalize();
-            startPos = effect_->GetEmitterSize() * dir * 0.5f;
-        }
-        break;
-
-    case EMITTER_BOX:
-        {
-            const Vector3& emitterSize = effect_->GetEmitterSize();
-            startPos = Vector3(
-                Random(emitterSize.x_) - emitterSize.x_ * 0.5f,
-                Random(emitterSize.y_) - emitterSize.y_ * 0.5f,
-                Random(emitterSize.z_) - emitterSize.z_ * 0.5f
-            );
-        }
-        break;
-    }
-
-    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_)
-    {
-        startPos = node_->GetWorldTransform() * startPos;
-        startDir = node_->GetWorldRotation() * startDir;
-    };
-
-    particle.velocity_ = effect_->GetRandomVelocity() * startDir;
-
-    billboard.position_ = startPos;
-    billboard.size_ = particles_[index].size_;
-    const Vector<TextureFrame>& textureFrames_ = effect_->GetTextureFrames();
-    billboard.uv_ = textureFrames_.Size() ? textureFrames_[0].uv_ : Rect::POSITIVE;
-    billboard.rotation_ = effect_->GetRandomRotation();
-    const Vector<ColorFrame>& colorFrames_ = effect_->GetColorFrames();
-    billboard.color_ = colorFrames_.Size() ? colorFrames_[0].color_ : Color();
-    billboard.enabled_ = true;
-    billboard.direction_ = startDir;
-
-    return true;
-}
-
-unsigned ParticleEmitter::GetFreeParticle() const
-{
-    for (unsigned i = 0; i < billboards_.Size(); ++i)
-    {
-        if (!billboards_[i].enabled_)
-            return i;
-    }
-
-    return M_MAX_UNSIGNED;
-}
-
-void ParticleEmitter::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
-{
-    // Store scene's timestep and use it instead of global timestep, as time scale may be other than 1
-    using namespace ScenePostUpdate;
-
-    lastTimeStep_ = eventData[P_TIMESTEP].GetFloat();
-
-    // If no invisible update, check that the billboardset is in view (framenumber has changed)
-    if ((effect_ && effect_->GetUpdateInvisible()) || viewFrameNumber_ != lastUpdateFrameNumber_)
-    {
-        lastUpdateFrameNumber_ = viewFrameNumber_;
-        needUpdate_ = true;
-        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)
-{
-    // When particle effect file is live-edited, remove existing particles and reapply the effect parameters
-    Reset();
-    ApplyEffect();
-}
-
-}

+ 0 - 148
Source/Atomic/Atomic3D/ParticleEmitter.h

@@ -1,148 +0,0 @@
-//
-// 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 "../Graphics/BillboardSet.h"
-
-namespace Atomic
-{
-
-class ParticleEffect;
-
-/// One particle in the particle system.
-struct Particle
-{
-    /// Velocity.
-    Vector3 velocity_;
-    /// Original billboard size.
-    Vector2 size_;
-    /// Time elapsed from creation.
-    float timer_;
-    /// Lifetime.
-    float timeToLive_;
-    /// Size scaling value.
-    float scale_;
-    /// Rotation speed.
-    float rotationSpeed_;
-    /// Current color animation index.
-    unsigned colorIndex_;
-    /// Current texture animation index.
-    unsigned texIndex_;
-};
-
-/// %Particle emitter component.
-class ATOMIC_API ParticleEmitter : public BillboardSet
-{
-    ATOMIC_OBJECT(ParticleEmitter, BillboardSet);
-
-public:
-    /// Construct.
-    ParticleEmitter(Context* context);
-    /// Destruct.
-    virtual ~ParticleEmitter();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-
-    /// Handle enabled/disabled state change.
-    virtual void OnSetEnabled();
-    /// Update before octree reinsertion. Is called from a worker thread.
-    virtual void Update(const FrameInfo& frame);
-
-    /// Set particle effect.
-    void SetEffect(ParticleEffect* effect);
-    /// Set maximum number of particles.
-    void SetNumParticles(unsigned num);
-    /// Set whether should be emitting. If the state was changed, also resets the emission period timer.
-    void SetEmitting(bool enable);
-    /// Set whether particles should be serialized. Default true, set false to reduce scene file size.
-    void SetSerializeParticles(bool enable);
-    /// Reset the emission period timer.
-    void ResetEmissionTimer();
-    /// Remove all current particles.
-    void RemoveAllParticles();
-    /// Reset the particle emitter completely. Removes current particles, sets emitting state on, and resets the emission timer.
-    void Reset();
-    /// Apply not continuously updated values such as the material, the number of particles and sorting mode from the particle effect. Call this if you change the effect programmatically.
-    void ApplyEffect();
-
-    /// Return particle effect.
-    ParticleEffect* GetEffect() const;
-
-    /// Return maximum number of particles.
-    unsigned GetNumParticles() const { return particles_.Size(); }
-
-    /// Return whether is currently emitting.
-    bool IsEmitting() const { return emitting_; }
-
-    /// Return whether particles are to be serialized.
-    bool GetSerializeParticles() const { return serializeParticles_; }
-
-    /// Set particles effect attribute.
-    void SetEffectAttr(const ResourceRef& value);
-    /// Set particles effect attribute.
-    ResourceRef GetEffectAttr() const;
-    /// Set particles attribute.
-    void SetParticlesAttr(const VariantVector& value);
-    /// Return particles attribute. Returns particle amount only if particles are not to be serialized.
-    VariantVector GetParticlesAttr() const;
-    /// Return billboards attribute. Returns billboard amount only if particles are not to be serialized.
-    VariantVector GetParticleBillboardsAttr() const;
-
-protected:
-    /// Handle scene being assigned.
-    virtual void OnSceneSet(Scene* scene);
-
-    /// Create a new particle. Return true if there was room.
-    bool EmitNewParticle();
-    /// Return a free particle index.
-    unsigned GetFreeParticle() const;
-
-private:
-    /// Handle scene post-update event.
-    void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
-    /// Handle live reload of the particle effect.
-    void HandleEffectReloadFinished(StringHash eventType, VariantMap& eventData);
-
-    /// Particle effect.
-    SharedPtr<ParticleEffect> effect_;
-    /// Particles.
-    PODVector<Particle> particles_;
-    /// Active/inactive period timer.
-    float periodTimer_;
-    /// New particle emission timer.
-    float emissionTimer_;
-    /// Last scene timestep.
-    float lastTimeStep_;
-    /// Rendering framenumber on which was last updated.
-    unsigned lastUpdateFrameNumber_;
-    /// Currently emitting flag.
-    bool emitting_;
-    /// Need update flag.
-    bool needUpdate_;
-    /// Serialize particles flag.
-    bool serializeParticles_;
-    /// Ready to send effect finish event flag.
-    bool sendFinishEvent_;
-};
-
-}

+ 0 - 181
Source/Atomic/Atomic3D/Skeleton.cpp

@@ -1,181 +0,0 @@
-//
-// 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/Skeleton.h"
-#include "../IO/Log.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-Skeleton::Skeleton() :
-    rootBoneIndex_(M_MAX_UNSIGNED)
-{
-}
-
-Skeleton::~Skeleton()
-{
-}
-
-bool Skeleton::Load(Deserializer& source)
-{
-    ClearBones();
-
-    if (source.IsEof())
-        return false;
-
-    unsigned bones = source.ReadUInt();
-    bones_.Reserve(bones);
-
-    for (unsigned i = 0; i < bones; ++i)
-    {
-        Bone newBone;
-        newBone.name_ = source.ReadString();
-        newBone.nameHash_ = newBone.name_;
-        newBone.parentIndex_ = source.ReadUInt();
-        newBone.initialPosition_ = source.ReadVector3();
-        newBone.initialRotation_ = source.ReadQuaternion();
-        newBone.initialScale_ = source.ReadVector3();
-        source.Read(&newBone.offsetMatrix_.m00_, sizeof(Matrix3x4));
-
-        // Read bone collision data
-        newBone.collisionMask_ = source.ReadUByte();
-        if (newBone.collisionMask_ & BONECOLLISION_SPHERE)
-            newBone.radius_ = source.ReadFloat();
-        if (newBone.collisionMask_ & BONECOLLISION_BOX)
-            newBone.boundingBox_ = source.ReadBoundingBox();
-
-        if (newBone.parentIndex_ == i)
-            rootBoneIndex_ = i;
-
-        bones_.Push(newBone);
-    }
-
-    return true;
-}
-
-bool Skeleton::Save(Serializer& dest) const
-{
-    if (!dest.WriteUInt(bones_.Size()))
-        return false;
-
-    for (unsigned i = 0; i < bones_.Size(); ++i)
-    {
-        const Bone& bone = bones_[i];
-        dest.WriteString(bone.name_);
-        dest.WriteUInt(bone.parentIndex_);
-        dest.WriteVector3(bone.initialPosition_);
-        dest.WriteQuaternion(bone.initialRotation_);
-        dest.WriteVector3(bone.initialScale_);
-        dest.Write(bone.offsetMatrix_.Data(), sizeof(Matrix3x4));
-
-        // Collision info
-        dest.WriteUByte(bone.collisionMask_);
-        if (bone.collisionMask_ & BONECOLLISION_SPHERE)
-            dest.WriteFloat(bone.radius_);
-        if (bone.collisionMask_ & BONECOLLISION_BOX)
-            dest.WriteBoundingBox(bone.boundingBox_);
-    }
-
-    return true;
-}
-
-void Skeleton::Define(const Skeleton& src)
-{
-    ClearBones();
-
-    bones_ = src.bones_;
-    // Make sure we clear node references, if they exist
-    // (AnimatedModel will create new nodes on its own)
-    for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
-        i->node_.Reset();
-    rootBoneIndex_ = src.rootBoneIndex_;
-}
-
-void Skeleton::SetRootBoneIndex(unsigned index)
-{
-    if (index < bones_.Size())
-        rootBoneIndex_ = index;
-    else
-        ATOMIC_LOGERROR("Root bone index out of bounds");
-}
-
-void Skeleton::ClearBones()
-{
-    bones_.Clear();
-    rootBoneIndex_ = M_MAX_UNSIGNED;
-}
-
-void Skeleton::Reset()
-{
-    for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
-    {
-        if (i->animated_ && i->node_)
-            i->node_->SetTransform(i->initialPosition_, i->initialRotation_, i->initialScale_);
-    }
-}
-
-void Skeleton::ResetSilent()
-{
-    for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
-    {
-        if (i->animated_ && i->node_)
-            i->node_->SetTransformSilent(i->initialPosition_, i->initialRotation_, i->initialScale_);
-    }
-}
-
-
-Bone* Skeleton::GetRootBone()
-{
-    return GetBone(rootBoneIndex_);
-}
-
-Bone* Skeleton::GetBone(unsigned index)
-{
-    return index < bones_.Size() ? &bones_[index] : (Bone*)0;
-}
-
-Bone* Skeleton::GetBone(const String& name)
-{
-    return GetBone(StringHash(name));
-}
-
-Bone* Skeleton::GetBone(const char* name)
-{
-    return GetBone(StringHash(name));
-}
-
-Bone* Skeleton::GetBone(StringHash nameHash)
-{
-    for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
-    {
-        if (i->nameHash_ == nameHash)
-            return &(*i);
-    }
-
-    return 0;
-}
-
-}

+ 0 - 132
Source/Atomic/Atomic3D/Skeleton.h

@@ -1,132 +0,0 @@
-//
-// 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 "../Math/BoundingBox.h"
-#include "../Scene/Node.h"
-
-namespace Atomic
-{
-
-static const unsigned BONECOLLISION_NONE = 0x0;
-static const unsigned BONECOLLISION_SPHERE = 0x1;
-static const unsigned BONECOLLISION_BOX = 0x2;
-
-class Deserializer;
-class ResourceCache;
-class Serializer;
-
-/// %Bone in a skeleton.
-struct Bone
-{
-    /// Construct with defaults.
-    Bone() :
-        parentIndex_(0),
-        initialPosition_(Vector3::ZERO),
-        initialRotation_(Quaternion::IDENTITY),
-        initialScale_(Vector3::ONE),
-        animated_(true),
-        collisionMask_(0),
-        radius_(0.0f)
-    {
-    }
-
-    /// Bone name.
-    String name_;
-    /// Bone name hash.
-    StringHash nameHash_;
-    /// Parent bone index.
-    unsigned parentIndex_;
-    /// Reset position.
-    Vector3 initialPosition_;
-    /// Reset rotation.
-    Quaternion initialRotation_;
-    /// Reset scale.
-    Vector3 initialScale_;
-    /// Offset matrix.
-    Matrix3x4 offsetMatrix_;
-    /// Animation enable flag.
-    bool animated_;
-    /// Supported collision types.
-    unsigned char collisionMask_;
-    /// Radius.
-    float radius_;
-    /// Local-space bounding box.
-    BoundingBox boundingBox_;
-    /// Scene node.
-    WeakPtr<Node> node_;
-};
-
-/// Hierarchical collection of bones.
-class ATOMIC_API Skeleton
-{
-public:
-    /// Construct an empty skeleton.
-    Skeleton();
-    /// Destruct.
-    ~Skeleton();
-
-    /// Read from a stream. Return true if successful.
-    bool Load(Deserializer& source);
-    /// Write to a stream. Return true if successful.
-    bool Save(Serializer& dest) const;
-    /// Define from another skeleton.
-    void Define(const Skeleton& src);
-    /// Set root bone's index.
-    void SetRootBoneIndex(unsigned index);
-    /// Clear bones.
-    void ClearBones();
-    /// Reset all animating bones to initial positions.
-    void Reset();
-
-    /// Return all bones.
-    const Vector<Bone>& GetBones() const { return bones_; }
-
-    /// Return modifiable bones.
-    Vector<Bone>& GetModifiableBones() { return bones_; }
-
-    /// Return number of bones.
-    unsigned GetNumBones() const { return bones_.Size(); }
-
-    /// Return root bone.
-    Bone* GetRootBone();
-    /// Return bone by index.
-    Bone* GetBone(unsigned index);
-    /// Return bone by name.
-    Bone* GetBone(const String& boneName);
-    /// Return bone by name.
-    Bone* GetBone(const char* boneName);
-    /// Return bone by name hash.
-    Bone* GetBone(StringHash boneNameHash);
-
-    /// Reset all animating bones to initial positions without marking the nodes dirty. Requires the node dirtying to be performed later.
-    void ResetSilent();
-
-private:
-    /// Bones.
-    Vector<Bone> bones_;
-    /// Root bone index.
-    unsigned rootBoneIndex_;
-};
-
-}

+ 0 - 88
Source/Atomic/Atomic3D/Skybox.cpp

@@ -1,88 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Graphics/Batch.h"
-#include "../Graphics/Camera.h"
-#include "../Graphics/Skybox.h"
-#include "../Scene/Node.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-
-Skybox::Skybox(Context* context) :
-    StaticModel(context),
-    lastFrame_(0)
-{
-}
-
-Skybox::~Skybox()
-{
-}
-
-void Skybox::RegisterObject(Context* context)
-{
-    context->RegisterFactory<Skybox>(GEOMETRY_CATEGORY);
-
-    ATOMIC_COPY_BASE_ATTRIBUTES(StaticModel);
-}
-
-void Skybox::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
-{
-    // Do not record a raycast result for a skybox, as it would block all other results
-}
-
-void Skybox::UpdateBatches(const FrameInfo& frame)
-{
-    distance_ = 0.0f;
-
-    if (frame.frameNumber_ != lastFrame_)
-    {
-        customWorldTransforms_.Clear();
-        lastFrame_ = frame.frameNumber_;
-    }
-
-    // Add camera position to fix the skybox in space. Use effective world transform to take reflection into account
-    Matrix3x4 customWorldTransform = node_->GetWorldTransform();
-    customWorldTransform.SetTranslation(node_->GetWorldPosition() + frame.camera_->GetEffectiveWorldTransform().Translation());
-    HashMap<Camera*, Matrix3x4>::Iterator it = customWorldTransforms_.Insert(MakePair(frame.camera_, customWorldTransform));
-
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-    {
-        batches_[i].worldTransform_ = &it->second_;
-        batches_[i].distance_ = 0.0f;
-    }
-}
-
-void Skybox::OnWorldBoundingBoxUpdate()
-{
-    // The skybox is supposed to be visible everywhere, so set a humongous bounding box
-    worldBoundingBox_.Define(-M_LARGE_VALUE, M_LARGE_VALUE);
-}
-
-}

+ 0 - 58
Source/Atomic/Atomic3D/Skybox.h

@@ -1,58 +0,0 @@
-//
-// 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 "../Graphics/StaticModel.h"
-
-namespace Atomic
-{
-
-/// Static model component with fixed position in relation to the camera.
-class ATOMIC_API Skybox : public StaticModel
-{
-    ATOMIC_OBJECT(Skybox, StaticModel);
-
-public:
-    /// Construct.
-    Skybox(Context* context);
-    /// Destruct.
-    virtual ~Skybox();
-    /// Register object factory. StaticModel must be registered first.
-    static void RegisterObject(Context* context);
-
-    /// Process octree raycast. May be called from a worker thread.
-    virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
-    /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
-    virtual void UpdateBatches(const FrameInfo& frame);
-
-protected:
-    /// Recalculate the world-space bounding box.
-    virtual void OnWorldBoundingBoxUpdate();
-
-    /// Custom world transform per camera.
-    HashMap<Camera*, Matrix3x4> customWorldTransforms_;
-    /// Last frame counter for knowing when to erase the custom world transforms of previous frame.
-    unsigned lastFrame_;
-};
-
-}

+ 0 - 458
Source/Atomic/Atomic3D/StaticModel.cpp

@@ -1,458 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/AnimatedModel.h"
-#include "../Graphics/Batch.h"
-#include "../Graphics/Camera.h"
-#include "../Graphics/Geometry.h"
-#include "../Graphics/Material.h"
-#include "../Graphics/OcclusionBuffer.h"
-#include "../Graphics/OctreeQuery.h"
-#include "../Graphics/VertexBuffer.h"
-#include "../IO/FileSystem.h"
-#include "../IO/Log.h"
-#include "../Resource/ResourceCache.h"
-#include "../Resource/ResourceEvents.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-
-StaticModel::StaticModel(Context* context) :
-    Drawable(context, DRAWABLE_GEOMETRY),
-    occlusionLodLevel_(M_MAX_UNSIGNED),
-    materialsAttr_(Material::GetTypeStatic())
-{
-}
-
-StaticModel::~StaticModel()
-{
-}
-
-void StaticModel::RegisterObject(Context* context)
-{
-    context->RegisterFactory<StaticModel>(GEOMETRY_CATEGORY);
-
-    ATOMIC_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Material", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()),
-        AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Is Occluder", bool, occluder_, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Cast Shadows", bool, castShadows_, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
-    ATOMIC_COPY_BASE_ATTRIBUTES(Drawable);
-    ATOMIC_ATTRIBUTE("Occlusion LOD Level", int, occlusionLodLevel_, M_MAX_UNSIGNED, AM_DEFAULT);
-}
-
-void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
-{
-    RayQueryLevel level = query.level_;
-
-    switch (level)
-    {
-    case RAY_AABB:
-        Drawable::ProcessRayQuery(query, results);
-        break;
-
-    case RAY_OBB:
-    case RAY_TRIANGLE:
-    case RAY_TRIANGLE_UV:
-        Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-        Ray localRay = query.ray_.Transformed(inverse);
-        float distance = localRay.HitDistance(boundingBox_);
-        Vector3 normal = -query.ray_.direction_;
-        Vector2 geometryUV;
-        unsigned hitBatch = M_MAX_UNSIGNED;
-
-        if (level >= RAY_TRIANGLE && distance < query.maxDistance_)
-        {
-            distance = M_INFINITY;
-
-            for (unsigned i = 0; i < batches_.Size(); ++i)
-            {
-                Geometry* geometry = batches_[i].geometry_;
-                if (geometry)
-                {
-                    Vector3 geometryNormal;
-                    float geometryDistance = level == RAY_TRIANGLE ? geometry->GetHitDistance(localRay, &geometryNormal) :
-                        geometry->GetHitDistance(localRay, &geometryNormal, &geometryUV);
-                    if (geometryDistance < query.maxDistance_ && geometryDistance < distance)
-                    {
-                        distance = geometryDistance;
-                        normal = (node_->GetWorldTransform() * Vector4(geometryNormal, 0.0f)).Normalized();
-                        hitBatch = i;
-                    }
-                }
-            }
-        }
-
-        if (distance < query.maxDistance_)
-        {
-            RayQueryResult result;
-            result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
-            result.normal_ = normal;
-            result.textureUV_ = geometryUV;
-            result.distance_ = distance;
-            result.drawable_ = this;
-            result.node_ = node_;
-            result.subObject_ = hitBatch;
-            results.Push(result);
-        }
-        break;
-    }
-}
-
-void StaticModel::UpdateBatches(const FrameInfo& frame)
-{
-    const BoundingBox& worldBoundingBox = GetWorldBoundingBox();
-    distance_ = frame.camera_->GetDistance(worldBoundingBox.Center());
-
-    if (batches_.Size() == 1)
-        batches_[0].distance_ = distance_;
-    else
-    {
-        const Matrix3x4& worldTransform = node_->GetWorldTransform();
-        for (unsigned i = 0; i < batches_.Size(); ++i)
-            batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_);
-    }
-
-    float scale = worldBoundingBox.Size().DotProduct(DOT_SCALE);
-    float newLodDistance = frame.camera_->GetLodDistance(distance_, scale, lodBias_);
-
-    if (newLodDistance != lodDistance_)
-    {
-        lodDistance_ = newLodDistance;
-        CalculateLodLevels();
-    }
-}
-
-Geometry* StaticModel::GetLodGeometry(unsigned batchIndex, unsigned level)
-{
-    if (batchIndex >= geometries_.Size())
-        return 0;
-
-    // If level is out of range, use visible geometry
-    if (level < geometries_[batchIndex].Size())
-        return geometries_[batchIndex][level];
-    else
-        return batches_[batchIndex].geometry_;
-}
-
-unsigned StaticModel::GetNumOccluderTriangles()
-{
-    unsigned triangles = 0;
-
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-    {
-        Geometry* geometry = GetLodGeometry(i, occlusionLodLevel_);
-        if (!geometry)
-            continue;
-
-        // Check that the material is suitable for occlusion (default material always is)
-        Material* mat = batches_[i].material_;
-        if (mat && !mat->GetOcclusion())
-            continue;
-
-        triangles += geometry->GetIndexCount() / 3;
-    }
-
-    return triangles;
-}
-
-bool StaticModel::DrawOcclusion(OcclusionBuffer* buffer)
-{
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-    {
-        Geometry* geometry = GetLodGeometry(i, occlusionLodLevel_);
-        if (!geometry)
-            continue;
-
-        // Check that the material is suitable for occlusion (default material always is) and set culling mode
-        Material* material = batches_[i].material_;
-        if (material)
-        {
-            if (!material->GetOcclusion())
-                continue;
-            buffer->SetCullMode(material->GetCullMode());
-        }
-        else
-            buffer->SetCullMode(CULL_CCW);
-
-        const unsigned char* vertexData;
-        unsigned vertexSize;
-        const unsigned char* indexData;
-        unsigned indexSize;
-        const PODVector<VertexElement>* elements;
-
-        geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elements);
-        // Check for valid geometry data
-        if (!vertexData || !indexData || !elements || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0)
-            continue;
-
-        unsigned indexStart = geometry->GetIndexStart();
-        unsigned indexCount = geometry->GetIndexCount();
-
-        // Draw and check for running out of triangles
-        if (!buffer->AddTriangles(node_->GetWorldTransform(), vertexData, vertexSize, indexData, indexSize, indexStart, indexCount))
-            return false;
-    }
-
-    return true;
-}
-
-void StaticModel::SetModel(Model* model)
-{
-    if (model == model_)
-        return;
-
-    // If script erroneously calls StaticModel::SetModel on an AnimatedModel, warn and redirect
-    if (GetType() == AnimatedModel::GetTypeStatic())
-    {
-        ATOMIC_LOGWARNING("StaticModel::SetModel() called on AnimatedModel. Redirecting to AnimatedModel::SetModel()");
-        AnimatedModel* animatedModel = static_cast<AnimatedModel*>(this);
-        animatedModel->SetModel(model);
-        return;
-    }
-
-    // Unsubscribe from the reload event of previous model (if any), then subscribe to the new
-    if (model_)
-        UnsubscribeFromEvent(model_, E_RELOADFINISHED);
-
-    model_ = model;
-
-    if (model)
-    {
-        SubscribeToEvent(model, E_RELOADFINISHED, ATOMIC_HANDLER(StaticModel, HandleModelReloadFinished));
-
-        // Copy the subgeometry & LOD level structure
-        SetNumGeometries(model->GetNumGeometries());
-        const Vector<Vector<SharedPtr<Geometry> > >& geometries = model->GetGeometries();
-        const PODVector<Vector3>& geometryCenters = model->GetGeometryCenters();
-        const Matrix3x4* worldTransform = node_ ? &node_->GetWorldTransform() : (const Matrix3x4*)0;
-        for (unsigned i = 0; i < geometries.Size(); ++i)
-        {
-            batches_[i].worldTransform_ = worldTransform;
-            geometries_[i] = geometries[i];
-            geometryData_[i].center_ = geometryCenters[i];
-        }
-
-        SetBoundingBox(model->GetBoundingBox());
-        ResetLodLevels();
-    }
-    else
-    {
-        SetNumGeometries(0);
-        SetBoundingBox(BoundingBox());
-    }
-
-    MarkNetworkUpdate();
-}
-
-void StaticModel::SetMaterial(Material* material)
-{
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-        batches_[i].material_ = material;
-
-    MarkNetworkUpdate();
-}
-
-bool StaticModel::SetMaterial(unsigned index, Material* material)
-{
-    if (index >= batches_.Size())
-    {
-        ATOMIC_LOGERROR("Material index out of bounds");
-        return false;
-    }
-
-    batches_[index].material_ = material;
-    MarkNetworkUpdate();
-    return true;
-}
-
-void StaticModel::SetOcclusionLodLevel(unsigned level)
-{
-    occlusionLodLevel_ = level;
-    MarkNetworkUpdate();
-}
-
-void StaticModel::ApplyMaterialList(const String& fileName)
-{
-    String useFileName = fileName;
-    if (useFileName.Trimmed().Empty() && model_)
-        useFileName = ReplaceExtension(model_->GetName(), ".txt");
-
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    SharedPtr<File> file = cache->GetFile(useFileName, false);
-    if (!file)
-        return;
-
-    unsigned index = 0;
-    while (!file->IsEof() && index < batches_.Size())
-    {
-        Material* material = cache->GetResource<Material>(file->ReadLine());
-        if (material)
-            SetMaterial(index, material);
-
-        ++index;
-    }
-}
-
-Material* StaticModel::GetMaterial(unsigned index) const
-{
-    return index < batches_.Size() ? batches_[index].material_ : (Material*)0;
-}
-
-bool StaticModel::IsInside(const Vector3& point) const
-{
-    if (!node_)
-        return false;
-
-    Vector3 localPosition = node_->GetWorldTransform().Inverse() * point;
-    return IsInsideLocal(localPosition);
-}
-
-bool StaticModel::IsInsideLocal(const Vector3& point) const
-{
-    // Early-out if point is not inside bounding box
-    if (boundingBox_.IsInside(point) == OUTSIDE)
-        return false;
-
-    Ray localRay(point, Vector3(1.0f, -1.0f, 1.0f));
-
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-    {
-        Geometry* geometry = batches_[i].geometry_;
-        if (geometry)
-        {
-            if (geometry->IsInside(localRay))
-                return true;
-        }
-    }
-
-    return false;
-}
-
-void StaticModel::SetBoundingBox(const BoundingBox& box)
-{
-    boundingBox_ = box;
-    OnMarkedDirty(node_);
-}
-
-void StaticModel::SetNumGeometries(unsigned num)
-{
-    batches_.Resize(num);
-    geometries_.Resize(num);
-    geometryData_.Resize(num);
-    ResetLodLevels();
-}
-
-void StaticModel::SetModelAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    SetModel(cache->GetResource<Model>(value.name_));
-}
-
-void StaticModel::SetMaterialsAttr(const ResourceRefList& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    for (unsigned i = 0; i < value.names_.Size(); ++i)
-        SetMaterial(i, cache->GetResource<Material>(value.names_[i]));
-}
-
-ResourceRef StaticModel::GetModelAttr() const
-{
-    return GetResourceRef(model_, Model::GetTypeStatic());
-}
-
-const ResourceRefList& StaticModel::GetMaterialsAttr() const
-{
-    materialsAttr_.names_.Resize(batches_.Size());
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-        materialsAttr_.names_[i] = GetResourceName(batches_[i].material_);
-
-    return materialsAttr_;
-}
-
-void StaticModel::OnWorldBoundingBoxUpdate()
-{
-    worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
-}
-
-void StaticModel::ResetLodLevels()
-{
-    // Ensure that each subgeometry has at least one LOD level, and reset the current LOD level
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-    {
-        if (!geometries_[i].Size())
-            geometries_[i].Resize(1);
-        batches_[i].geometry_ = geometries_[i][0];
-        geometryData_[i].lodLevel_ = 0;
-    }
-
-    // Find out the real LOD levels on next geometry update
-    lodDistance_ = M_INFINITY;
-}
-
-void StaticModel::CalculateLodLevels()
-{
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-    {
-        const Vector<SharedPtr<Geometry> >& batchGeometries = geometries_[i];
-        // If only one LOD geometry, no reason to go through the LOD calculation
-        if (batchGeometries.Size() <= 1)
-            continue;
-
-        unsigned j;
-
-        for (j = 1; j < batchGeometries.Size(); ++j)
-        {
-            if (batchGeometries[j] && lodDistance_ <= batchGeometries[j]->GetLodDistance())
-                break;
-        }
-
-        unsigned newLodLevel = j - 1;
-        if (geometryData_[i].lodLevel_ != newLodLevel)
-        {
-            geometryData_[i].lodLevel_ = newLodLevel;
-            batches_[i].geometry_ = batchGeometries[newLodLevel];
-        }
-    }
-}
-
-void StaticModel::HandleModelReloadFinished(StringHash eventType, VariantMap& eventData)
-{
-    Model* currentModel = model_;
-    model_.Reset(); // Set null to allow to be re-set
-    SetModel(currentModel);
-}
-
-}

+ 0 - 130
Source/Atomic/Atomic3D/StaticModel.h

@@ -1,130 +0,0 @@
-//
-// 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 "../Graphics/Drawable.h"
-
-namespace Atomic
-{
-
-class Model;
-
-/// Static model per-geometry extra data.
-struct StaticModelGeometryData
-{
-    /// Geometry center.
-    Vector3 center_;
-    /// Current LOD level.
-    unsigned lodLevel_;
-};
-
-/// Static model component.
-class ATOMIC_API StaticModel : public Drawable
-{
-    ATOMIC_OBJECT(StaticModel, Drawable);
-
-public:
-    /// Construct.
-    StaticModel(Context* context);
-    /// Destruct.
-    ~StaticModel();
-    /// Register object factory. Drawable must be registered first.
-    static void RegisterObject(Context* context);
-
-    /// Process octree raycast. May be called from a worker thread.
-    virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
-    /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
-    virtual void UpdateBatches(const FrameInfo& frame);
-    /// Return the geometry for a specific LOD level.
-    virtual Geometry* GetLodGeometry(unsigned batchIndex, unsigned level);
-    /// Return number of occlusion geometry triangles.
-    virtual unsigned GetNumOccluderTriangles();
-    /// Draw to occlusion buffer. Return true if did not run out of triangles.
-    virtual bool DrawOcclusion(OcclusionBuffer* buffer);
-
-    /// Set model.
-    void SetModel(Model* model);
-    /// Set material on all geometries.
-    void SetMaterial(Material* material);
-    /// Set material on one geometry. Return true if successful.
-    bool SetMaterial(unsigned index, Material* material);
-    /// Set occlusion LOD level. By default (M_MAX_UNSIGNED) same as visible.
-    void SetOcclusionLodLevel(unsigned level);
-    /// Apply default materials from a material list file. If filename is empty (default), the model's resource name with extension .txt will be used.
-    void ApplyMaterialList(const String& fileName = String::EMPTY);
-
-    /// Return model.
-    Model* GetModel() const { return model_; }
-
-    /// Return number of geometries.
-    unsigned GetNumGeometries() const { return geometries_.Size(); }
-
-    /// Return material by geometry index.
-    Material* GetMaterial(unsigned index = 0) const;
-
-    /// Return occlusion LOD level.
-    unsigned GetOcclusionLodLevel() const { return occlusionLodLevel_; }
-
-    /// Determines if the given world space point is within the model geometry.
-    bool IsInside(const Vector3& point) const;
-    /// Determines if the given local space point is within the model geometry.
-    bool IsInsideLocal(const Vector3& point) const;
-
-    /// Set model attribute.
-    void SetModelAttr(const ResourceRef& value);
-    /// Set materials attribute.
-    void SetMaterialsAttr(const ResourceRefList& value);
-    /// Return model attribute.
-    ResourceRef GetModelAttr() const;
-    /// Return materials attribute.
-    const ResourceRefList& GetMaterialsAttr() const;
-
-protected:
-    /// Recalculate the world-space bounding box.
-    virtual void OnWorldBoundingBoxUpdate();
-    /// Set local-space bounding box.
-    void SetBoundingBox(const BoundingBox& box);
-    /// Set number of geometries.
-    void SetNumGeometries(unsigned num);
-    /// Reset LOD levels.
-    void ResetLodLevels();
-    /// Choose LOD levels based on distance.
-    void CalculateLodLevels();
-
-    /// Extra per-geometry data.
-    PODVector<StaticModelGeometryData> geometryData_;
-    /// All geometries.
-    Vector<Vector<SharedPtr<Geometry> > > geometries_;
-    /// Model.
-    SharedPtr<Model> model_;
-    /// Occlusion LOD level.
-    unsigned occlusionLodLevel_;
-    /// Material list attribute.
-    mutable ResourceRefList materialsAttr_;
-
-private:
-    /// Handle model reload finished.
-    void HandleModelReloadFinished(StringHash eventType, VariantMap& eventData);
-};
-
-}

+ 0 - 400
Source/Atomic/Atomic3D/StaticModelGroup.cpp

@@ -1,400 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Graphics/Batch.h"
-#include "../Graphics/Camera.h"
-#include "../Graphics/Geometry.h"
-#include "../Graphics/Material.h"
-#include "../Graphics/OcclusionBuffer.h"
-#include "../Graphics/OctreeQuery.h"
-#include "../Graphics/StaticModelGroup.h"
-#include "../Graphics/VertexBuffer.h"
-#include "../Scene/Scene.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-
-StaticModelGroup::StaticModelGroup(Context* context) :
-    StaticModel(context),
-    nodeIDsDirty_(false)
-{
-    // Initialize the default node IDs attribute
-    UpdateNodeIDs();
-}
-
-StaticModelGroup::~StaticModelGroup()
-{
-}
-
-void StaticModelGroup::RegisterObject(Context* context)
-{
-    context->RegisterFactory<StaticModelGroup>(GEOMETRY_CATEGORY);
-
-    ATOMIC_COPY_BASE_ATTRIBUTES(StaticModel);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Instance Nodes", GetNodeIDsAttr, SetNodeIDsAttr, VariantVector, Variant::emptyVariantVector,
-        AM_DEFAULT | AM_NODEIDVECTOR);
-}
-
-void StaticModelGroup::ApplyAttributes()
-{
-    if (!nodeIDsDirty_)
-        return;
-
-    // Remove all old instance nodes before searching for new. Can not call RemoveAllInstances() as that would modify
-    // the ID list on its own
-    for (unsigned i = 0; i < instanceNodes_.Size(); ++i)
-    {
-        Node* node = instanceNodes_[i];
-        if (node)
-            node->RemoveListener(this);
-    }
-
-    instanceNodes_.Clear();
-
-    Scene* scene = GetScene();
-
-    if (scene)
-    {
-        // The first index stores the number of IDs redundantly. This is for editing
-        for (unsigned i = 1; i < nodeIDsAttr_.Size(); ++i)
-        {
-            Node* node = scene->GetNode(nodeIDsAttr_[i].GetUInt());
-            if (node)
-            {
-                WeakPtr<Node> instanceWeak(node);
-                node->AddListener(this);
-                instanceNodes_.Push(instanceWeak);
-            }
-        }
-    }
-
-    worldTransforms_.Resize(instanceNodes_.Size());
-    nodeIDsDirty_ = false;
-    OnMarkedDirty(GetNode());
-}
-
-void StaticModelGroup::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
-{
-    // If no bones or no bone-level testing, use the Drawable test
-    RayQueryLevel level = query.level_;
-    if (level < RAY_AABB)
-    {
-        Drawable::ProcessRayQuery(query, results);
-        return;
-    }
-
-    // Check ray hit distance to AABB before proceeding with more accurate tests
-    // GetWorldBoundingBox() updates the world transforms
-    if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_)
-        return;
-
-    for (unsigned i = 0; i < numWorldTransforms_; ++i)
-    {
-        // Initial test using AABB
-        float distance = query.ray_.HitDistance(boundingBox_.Transformed(worldTransforms_[i]));
-        Vector3 normal = -query.ray_.direction_;
-
-        // Then proceed to OBB and triangle-level tests if necessary
-        if (level >= RAY_OBB && distance < query.maxDistance_)
-        {
-            Matrix3x4 inverse = worldTransforms_[i].Inverse();
-            Ray localRay = query.ray_.Transformed(inverse);
-            distance = localRay.HitDistance(boundingBox_);
-
-            if (level == RAY_TRIANGLE && distance < query.maxDistance_)
-            {
-                distance = M_INFINITY;
-
-                for (unsigned j = 0; j < batches_.Size(); ++j)
-                {
-                    Geometry* geometry = batches_[j].geometry_;
-                    if (geometry)
-                    {
-                        Vector3 geometryNormal;
-                        float geometryDistance = geometry->GetHitDistance(localRay, &geometryNormal);
-                        if (geometryDistance < query.maxDistance_ && geometryDistance < distance)
-                        {
-                            distance = geometryDistance;
-                            normal = (worldTransforms_[i] * Vector4(geometryNormal, 0.0f)).Normalized();
-                        }
-                    }
-                }
-            }
-        }
-
-        if (distance < query.maxDistance_)
-        {
-            RayQueryResult result;
-            result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
-            result.normal_ = normal;
-            result.distance_ = distance;
-            result.drawable_ = this;
-            result.node_ = node_;
-            result.subObject_ = i;
-            results.Push(result);
-        }
-    }
-}
-
-void StaticModelGroup::UpdateBatches(const FrameInfo& frame)
-{
-    // Getting the world bounding box ensures the transforms are updated
-    const BoundingBox& worldBoundingBox = GetWorldBoundingBox();
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-    distance_ = frame.camera_->GetDistance(worldBoundingBox.Center());
-
-    if (batches_.Size() > 1)
-    {
-        for (unsigned i = 0; i < batches_.Size(); ++i)
-        {
-            batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_);
-            batches_[i].worldTransform_ = numWorldTransforms_ ? &worldTransforms_[0] : &Matrix3x4::IDENTITY;
-            batches_[i].numWorldTransforms_ = numWorldTransforms_;
-        }
-    }
-    else if (batches_.Size() == 1)
-    {
-        batches_[0].distance_ = distance_;
-        batches_[0].worldTransform_ = numWorldTransforms_ ? &worldTransforms_[0] : &Matrix3x4::IDENTITY;
-        batches_[0].numWorldTransforms_ = numWorldTransforms_;
-    }
-
-    float scale = worldBoundingBox.Size().DotProduct(DOT_SCALE);
-    float newLodDistance = frame.camera_->GetLodDistance(distance_, scale, lodBias_);
-
-    if (newLodDistance != lodDistance_)
-    {
-        lodDistance_ = newLodDistance;
-        CalculateLodLevels();
-    }
-}
-
-unsigned StaticModelGroup::GetNumOccluderTriangles()
-{
-    // Make sure instance transforms are up-to-date
-    GetWorldBoundingBox();
-
-    unsigned triangles = 0;
-
-    for (unsigned i = 0; i < batches_.Size(); ++i)
-    {
-        Geometry* geometry = GetLodGeometry(i, occlusionLodLevel_);
-        if (!geometry)
-            continue;
-
-        // Check that the material is suitable for occlusion (default material always is)
-        Material* mat = batches_[i].material_;
-        if (mat && !mat->GetOcclusion())
-            continue;
-
-        triangles += numWorldTransforms_ * geometry->GetIndexCount() / 3;
-    }
-
-    return triangles;
-}
-
-bool StaticModelGroup::DrawOcclusion(OcclusionBuffer* buffer)
-{
-    // Make sure instance transforms are up-to-date
-    GetWorldBoundingBox();
-
-    for (unsigned i = 0; i < numWorldTransforms_; ++i)
-    {
-        for (unsigned j = 0; j < batches_.Size(); ++j)
-        {
-            Geometry* geometry = GetLodGeometry(j, occlusionLodLevel_);
-            if (!geometry)
-                continue;
-
-            // Check that the material is suitable for occlusion (default material always is) and set culling mode
-            Material* material = batches_[j].material_;
-            if (material)
-            {
-                if (!material->GetOcclusion())
-                    continue;
-                buffer->SetCullMode(material->GetCullMode());
-            }
-            else
-                buffer->SetCullMode(CULL_CCW);
-
-            const unsigned char* vertexData;
-            unsigned vertexSize;
-            const unsigned char* indexData;
-            unsigned indexSize;
-            const PODVector<VertexElement>* elements;
-
-            geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elements);
-            // Check for valid geometry data
-            if (!vertexData || !indexData || !elements || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0)
-                continue;
-
-            unsigned indexStart = geometry->GetIndexStart();
-            unsigned indexCount = geometry->GetIndexCount();
-
-            // Draw and check for running out of triangles
-            if (!buffer->AddTriangles(worldTransforms_[i], vertexData, vertexSize, indexData, indexSize, indexStart, indexCount))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-void StaticModelGroup::AddInstanceNode(Node* node)
-{
-    if (!node)
-        return;
-
-    WeakPtr<Node> instanceWeak(node);
-    if (instanceNodes_.Contains(instanceWeak))
-        return;
-
-    // Add as a listener for the instance node, so that we know to dirty the transforms when the node moves or is enabled/disabled
-    node->AddListener(this);
-    instanceNodes_.Push(instanceWeak);
-
-    UpdateNodeIDs();
-    OnMarkedDirty(GetNode());
-    MarkNetworkUpdate();
-}
-
-void StaticModelGroup::RemoveInstanceNode(Node* node)
-{
-    if (!node)
-        return;
-
-    WeakPtr<Node> instanceWeak(node);
-    node->RemoveListener(this);
-    instanceNodes_.Remove(instanceWeak);
-
-    UpdateNodeIDs();
-    OnMarkedDirty(GetNode());
-    MarkNetworkUpdate();
-}
-
-void StaticModelGroup::RemoveAllInstanceNodes()
-{
-    for (unsigned i = 0; i < instanceNodes_.Size(); ++i)
-    {
-        Node* node = instanceNodes_[i];
-        if (node)
-            node->RemoveListener(this);
-    }
-
-    instanceNodes_.Clear();
-
-    UpdateNodeIDs();
-    OnMarkedDirty(GetNode());
-    MarkNetworkUpdate();
-}
-
-Node* StaticModelGroup::GetInstanceNode(unsigned index) const
-{
-    return index < instanceNodes_.Size() ? instanceNodes_[index] : (Node*)0;
-}
-
-void StaticModelGroup::SetNodeIDsAttr(const VariantVector& value)
-{
-    // Just remember the node IDs. They need to go through the SceneResolver, and we actually find the nodes during
-    // ApplyAttributes()
-    if (value.Size())
-    {
-        nodeIDsAttr_.Clear();
-
-        unsigned index = 0;
-        unsigned numInstances = value[index++].GetUInt();
-        // Prevent crash on entering negative value in the editor
-        if (numInstances > M_MAX_INT)
-            numInstances = 0;
-
-        nodeIDsAttr_.Push(numInstances);
-        while (numInstances--)
-        {
-            // If vector contains less IDs than should, fill the rest with zeroes
-            if (index < value.Size())
-                nodeIDsAttr_.Push(value[index++].GetUInt());
-            else
-                nodeIDsAttr_.Push(0);
-        }
-    }
-    else
-    {
-        nodeIDsAttr_.Clear();
-        nodeIDsAttr_.Push(0);
-    }
-    nodeIDsDirty_ = true;
-}
-
-void StaticModelGroup::OnNodeSetEnabled(Node* node)
-{
-    Drawable::OnMarkedDirty(node);
-}
-
-void StaticModelGroup::OnWorldBoundingBoxUpdate()
-{
-    // Update transforms and bounding box at the same time to have to go through the objects only once
-    unsigned index = 0;
-
-    BoundingBox worldBox;
-
-    for (unsigned i = 0; i < instanceNodes_.Size(); ++i)
-    {
-        Node* node = instanceNodes_[i];
-        if (!node || !node->IsEnabled())
-            continue;
-
-        const Matrix3x4& worldTransform = node->GetWorldTransform();
-        worldTransforms_[index++] = worldTransform;
-        worldBox.Merge(boundingBox_.Transformed(worldTransform));
-    }
-
-    worldBoundingBox_ = worldBox;
-
-    // Store the amount of valid instances we found instead of resizing worldTransforms_. This is because this function may be 
-    // called from multiple worker threads simultaneously
-    numWorldTransforms_ = index;
-}
-
-void StaticModelGroup::UpdateNodeIDs()
-{
-    unsigned numInstances = instanceNodes_.Size();
-
-    nodeIDsAttr_.Clear();
-    nodeIDsAttr_.Push(numInstances);
-    worldTransforms_.Resize(numInstances);
-    numWorldTransforms_ = 0; // For safety. OnWorldBoundingBoxUpdate() will calculate the proper amount
-
-    for (unsigned i = 0; i < numInstances; ++i)
-    {
-        Node* node = instanceNodes_[i];
-        nodeIDsAttr_.Push(node ? node->GetID() : 0);
-    }
-}
-
-}

+ 0 - 95
Source/Atomic/Atomic3D/StaticModelGroup.h

@@ -1,95 +0,0 @@
-//
-// 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 "../Graphics/StaticModel.h"
-
-namespace Atomic
-{
-
-/// Renders several object instances while culling and receiving light as one unit. Can be used as a CPU-side optimization, but note that also regular StaticModels will use instanced rendering if possible.
-class ATOMIC_API StaticModelGroup : public StaticModel
-{
-    ATOMIC_OBJECT(StaticModelGroup, StaticModel);
-
-public:
-    /// Construct.
-    StaticModelGroup(Context* context);
-    /// Destruct.
-    virtual ~StaticModelGroup();
-    /// Register object factory. StaticModel must be registered first.
-    static void RegisterObject(Context* context);
-
-    /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
-    void ApplyAttributes();
-    /// Process octree raycast. May be called from a worker thread.
-    virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
-    /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
-    virtual void UpdateBatches(const FrameInfo& frame);
-    /// Return number of occlusion geometry triangles.
-    virtual unsigned GetNumOccluderTriangles();
-    /// Draw to occlusion buffer. Return true if did not run out of triangles.
-    virtual bool DrawOcclusion(OcclusionBuffer* buffer);
-
-    /// Add an instance scene node. It does not need any drawable components of its own.
-    void AddInstanceNode(Node* node);
-    /// Remove an instance scene node.
-    void RemoveInstanceNode(Node* node);
-    /// Remove all instance scene nodes.
-    void RemoveAllInstanceNodes();
-
-    /// Return number of instance nodes.
-    unsigned GetNumInstanceNodes() const { return instanceNodes_.Size(); }
-
-    /// Return instance node by index.
-    Node* GetInstanceNode(unsigned index) const;
-
-    /// Set node IDs attribute.
-    void SetNodeIDsAttr(const VariantVector& value);
-
-    /// Return node IDs attribute.
-    const VariantVector& GetNodeIDsAttr() const { return nodeIDsAttr_; }
-
-protected:
-    /// Handle scene node enabled status changing.
-    virtual void OnNodeSetEnabled(Node* node);
-    /// Recalculate the world-space bounding box.
-    virtual void OnWorldBoundingBoxUpdate();
-
-private:
-    /// Update node IDs attribute and ensure the transforms vector has the right size.
-    void UpdateNodeIDs();
-
-    /// Instance nodes.
-    Vector<WeakPtr<Node> > instanceNodes_;
-    /// World transforms of valid (existing and visible) instances.
-    PODVector<Matrix3x4> worldTransforms_;
-    /// IDs of instance nodes for serialization.
-    mutable VariantVector nodeIDsAttr_;
-    /// Number of valid instance node transforms.
-    unsigned numWorldTransforms_;
-    /// Whether node IDs have been set and nodes should be searched for during ApplyAttributes.
-    bool nodeIDsDirty_;
-};
-
-}

+ 0 - 1286
Source/Atomic/Atomic3D/Terrain.cpp

@@ -1,1286 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Core/Profiler.h"
-#include "../Graphics/DrawableEvents.h"
-#include "../Graphics/Geometry.h"
-#include "../Graphics/IndexBuffer.h"
-#include "../Graphics/Material.h"
-#include "../Graphics/Octree.h"
-#include "../Graphics/Terrain.h"
-#include "../Graphics/TerrainPatch.h"
-#include "../Graphics/VertexBuffer.h"
-#include "../IO/Log.h"
-#include "../Resource/Image.h"
-#include "../Resource/ResourceCache.h"
-#include "../Resource/ResourceEvents.h"
-#include "../Scene/Node.h"
-#include "../Scene/Scene.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-extern const char* GEOMETRY_CATEGORY;
-
-static const Vector3 DEFAULT_SPACING(1.0f, 0.25f, 1.0f);
-static const unsigned MIN_LOD_LEVELS = 1;
-static const unsigned MAX_LOD_LEVELS = 4;
-static const int DEFAULT_PATCH_SIZE = 32;
-static const int MIN_PATCH_SIZE = 4;
-static const int MAX_PATCH_SIZE = 128;
-static const unsigned STITCH_NORTH = 1;
-static const unsigned STITCH_SOUTH = 2;
-static const unsigned STITCH_WEST = 4;
-static const unsigned STITCH_EAST = 8;
-
-inline void GrowUpdateRegion(IntRect& updateRegion, int x, int y)
-{
-    if (updateRegion.left_ < 0)
-    {
-        updateRegion.left_ = updateRegion.right_ = x;
-        updateRegion.top_ = updateRegion.bottom_ = y;
-    }
-    else
-    {
-        if (x < updateRegion.left_)
-            updateRegion.left_ = x;
-        if (x > updateRegion.right_)
-            updateRegion.right_ = x;
-        if (y < updateRegion.top_)
-            updateRegion.top_ = y;
-        if (y > updateRegion.bottom_)
-            updateRegion.bottom_ = y;
-    }
-}
-
-Terrain::Terrain(Context* context) :
-    Component(context),
-    indexBuffer_(new IndexBuffer(context)),
-    spacing_(DEFAULT_SPACING),
-    lastSpacing_(Vector3::ZERO),
-    patchWorldOrigin_(Vector2::ZERO),
-    patchWorldSize_(Vector2::ZERO),
-    numVertices_(IntVector2::ZERO),
-    lastNumVertices_(IntVector2::ZERO),
-    numPatches_(IntVector2::ZERO),
-    patchSize_(DEFAULT_PATCH_SIZE),
-    lastPatchSize_(0),
-    numLodLevels_(1),
-    maxLodLevels_(MAX_LOD_LEVELS),
-    occlusionLodLevel_(M_MAX_UNSIGNED),
-    smoothing_(false),
-    visible_(true),
-    castShadows_(false),
-    occluder_(false),
-    occludee_(true),
-    viewMask_(DEFAULT_VIEWMASK),
-    lightMask_(DEFAULT_LIGHTMASK),
-    shadowMask_(DEFAULT_SHADOWMASK),
-    zoneMask_(DEFAULT_ZONEMASK),
-    drawDistance_(0.0f),
-    shadowDistance_(0.0f),
-    lodBias_(1.0f),
-    maxLights_(0),
-    recreateTerrain_(false)
-{
-    indexBuffer_->SetShadowed(true);
-}
-
-Terrain::~Terrain()
-{
-}
-
-void Terrain::RegisterObject(Context* context)
-{
-    context->RegisterFactory<Terrain>(GEOMETRY_CATEGORY);
-
-    ATOMIC_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Height Map", GetHeightMapAttr, SetHeightMapAttr, ResourceRef, ResourceRef(Image::GetTypeStatic()),
-        AM_DEFAULT);
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()),
-        AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Vertex Spacing", Vector3, spacing_, DEFAULT_SPACING, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Patch Size", GetPatchSize, SetPatchSizeAttr, int, DEFAULT_PATCH_SIZE, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Max LOD Levels", GetMaxLodLevels, SetMaxLodLevelsAttr, unsigned, MAX_LOD_LEVELS, AM_DEFAULT);
-    ATOMIC_ATTRIBUTE("Smooth Height Map", bool, smoothing_, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Is Occluder", IsOccluder, SetOccluder, bool, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Cast Shadows", GetCastShadows, SetCastShadows, bool, false, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Max Lights", GetMaxLights, SetMaxLights, unsigned, 0, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("View Mask", GetViewMask, SetViewMask, unsigned, DEFAULT_VIEWMASK, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Light Mask", GetLightMask, SetLightMask, unsigned, DEFAULT_LIGHTMASK, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Shadow Mask", GetShadowMask, SetShadowMask, unsigned, DEFAULT_SHADOWMASK, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Zone Mask", GetZoneMask, SetZoneMask, unsigned, DEFAULT_ZONEMASK, AM_DEFAULT);
-    ATOMIC_ACCESSOR_ATTRIBUTE("Occlusion LOD level", GetOcclusionLodLevel, SetOcclusionLodLevelAttr, unsigned, M_MAX_UNSIGNED, AM_DEFAULT);
-}
-
-void Terrain::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
-{
-    Serializable::OnSetAttribute(attr, src);
-
-    // Change of any non-accessor attribute requires recreation of the terrain
-    if (!attr.accessor_)
-        recreateTerrain_ = true;
-}
-
-void Terrain::ApplyAttributes()
-{
-    if (recreateTerrain_)
-        CreateGeometry();
-}
-
-void Terrain::OnSetEnabled()
-{
-    bool enabled = IsEnabledEffective();
-
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetEnabled(enabled);
-    }
-}
-
-void Terrain::SetPatchSize(int size)
-{
-    if (size < MIN_PATCH_SIZE || size > MAX_PATCH_SIZE || !IsPowerOfTwo((unsigned)size))
-        return;
-
-    if (size != patchSize_)
-    {
-        patchSize_ = size;
-
-        CreateGeometry();
-        MarkNetworkUpdate();
-    }
-}
-
-void Terrain::SetSpacing(const Vector3& spacing)
-{
-    if (spacing != spacing_)
-    {
-        spacing_ = spacing;
-
-        CreateGeometry();
-        MarkNetworkUpdate();
-    }
-}
-
-void Terrain::SetMaxLodLevels(unsigned levels)
-{
-    levels = Clamp(levels, MIN_LOD_LEVELS, MAX_LOD_LEVELS);
-    if (levels != maxLodLevels_)
-    {
-        maxLodLevels_ = levels;
-        lastPatchSize_ = 0; // Force full recreate
-        
-        CreateGeometry();
-        MarkNetworkUpdate();
-    }
-}
-
-void Terrain::SetOcclusionLodLevel(unsigned level)
-{
-    if (level != occlusionLodLevel_)
-    {
-        occlusionLodLevel_ = level;
-        lastPatchSize_ = 0; // Force full recreate
-        
-        CreateGeometry();
-        MarkNetworkUpdate();
-    }
-}
-
-void Terrain::SetSmoothing(bool enable)
-{
-    if (enable != smoothing_)
-    {
-        smoothing_ = enable;
-
-        CreateGeometry();
-        MarkNetworkUpdate();
-    }
-}
-
-bool Terrain::SetHeightMap(Image* image)
-{
-    bool success = SetHeightMapInternal(image, true);
-
-    MarkNetworkUpdate();
-    return success;
-}
-
-void Terrain::SetMaterial(Material* material)
-{
-    material_ = material;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetMaterial(material);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetDrawDistance(float distance)
-{
-    drawDistance_ = distance;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetDrawDistance(distance);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetShadowDistance(float distance)
-{
-    shadowDistance_ = distance;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetShadowDistance(distance);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetLodBias(float bias)
-{
-    lodBias_ = bias;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetLodBias(bias);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetViewMask(unsigned mask)
-{
-    viewMask_ = mask;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetViewMask(mask);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetLightMask(unsigned mask)
-{
-    lightMask_ = mask;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetLightMask(mask);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetShadowMask(unsigned mask)
-{
-    shadowMask_ = mask;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetShadowMask(mask);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetZoneMask(unsigned mask)
-{
-    zoneMask_ = mask;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetZoneMask(mask);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetMaxLights(unsigned num)
-{
-    maxLights_ = num;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetMaxLights(num);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetCastShadows(bool enable)
-{
-    castShadows_ = enable;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetCastShadows(enable);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetOccluder(bool enable)
-{
-    occluder_ = enable;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetOccluder(enable);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::SetOccludee(bool enable)
-{
-    occludee_ = enable;
-    for (unsigned i = 0; i < patches_.Size(); ++i)
-    {
-        if (patches_[i])
-            patches_[i]->SetOccludee(enable);
-    }
-
-    MarkNetworkUpdate();
-}
-
-void Terrain::ApplyHeightMap()
-{
-    if (heightMap_)
-        CreateGeometry();
-}
-
-Image* Terrain::GetHeightMap() const
-{
-    return heightMap_;
-}
-
-Material* Terrain::GetMaterial() const
-{
-    return material_;
-}
-
-TerrainPatch* Terrain::GetPatch(unsigned index) const
-{
-    return index < patches_.Size() ? patches_[index] : (TerrainPatch*)0;
-}
-
-TerrainPatch* Terrain::GetPatch(int x, int z) const
-{
-    if (x < 0 || x >= numPatches_.x_ || z < 0 || z >= numPatches_.y_)
-        return 0;
-    else
-        return GetPatch((unsigned)(z * numPatches_.x_ + x));
-}
-
-float Terrain::GetHeight(const Vector3& worldPosition) const
-{
-    if (node_)
-    {
-        Vector3 position = node_->GetWorldTransform().Inverse() * worldPosition;
-        float xPos = (position.x_ - patchWorldOrigin_.x_) / spacing_.x_;
-        float zPos = (position.z_ - patchWorldOrigin_.y_) / spacing_.z_;
-        float xFrac = xPos - floorf(xPos);
-        float zFrac = zPos - floorf(zPos);
-        float h1, h2, h3;
-
-        if (xFrac + zFrac >= 1.0f)
-        {
-            h1 = GetRawHeight((unsigned)xPos + 1, (unsigned)zPos + 1);
-            h2 = GetRawHeight((unsigned)xPos, (unsigned)zPos + 1);
-            h3 = GetRawHeight((unsigned)xPos + 1, (unsigned)zPos);
-            xFrac = 1.0f - xFrac;
-            zFrac = 1.0f - zFrac;
-        }
-        else
-        {
-            h1 = GetRawHeight((unsigned)xPos, (unsigned)zPos);
-            h2 = GetRawHeight((unsigned)xPos + 1, (unsigned)zPos);
-            h3 = GetRawHeight((unsigned)xPos, (unsigned)zPos + 1);
-        }
-
-        float h = h1 * (1.0f - xFrac - zFrac) + h2 * xFrac + h3 * zFrac;
-        /// \todo This assumes that the terrain scene node is upright
-        return node_->GetWorldScale().y_ * h + node_->GetWorldPosition().y_;
-    }
-    else
-        return 0.0f;
-}
-
-Vector3 Terrain::GetNormal(const Vector3& worldPosition) const
-{
-    if (node_)
-    {
-        Vector3 position = node_->GetWorldTransform().Inverse() * worldPosition;
-        float xPos = (position.x_ - patchWorldOrigin_.x_) / spacing_.x_;
-        float zPos = (position.z_ - patchWorldOrigin_.y_) / spacing_.z_;
-        float xFrac = xPos - floorf(xPos);
-        float zFrac = zPos - floorf(zPos);
-        Vector3 n1, n2, n3;
-
-        if (xFrac + zFrac >= 1.0f)
-        {
-            n1 = GetRawNormal((unsigned)xPos + 1, (unsigned)zPos + 1);
-            n2 = GetRawNormal((unsigned)xPos, (unsigned)zPos + 1);
-            n3 = GetRawNormal((unsigned)xPos + 1, (unsigned)zPos);
-            xFrac = 1.0f - xFrac;
-            zFrac = 1.0f - zFrac;
-        }
-        else
-        {
-            n1 = GetRawNormal((unsigned)xPos, (unsigned)zPos);
-            n2 = GetRawNormal((unsigned)xPos + 1, (unsigned)zPos);
-            n3 = GetRawNormal((unsigned)xPos, (unsigned)zPos + 1);
-        }
-
-        Vector3 n = (n1 * (1.0f - xFrac - zFrac) + n2 * xFrac + n3 * zFrac).Normalized();
-        return node_->GetWorldRotation() * n;
-    }
-    else
-        return Vector3::UP;
-}
-
-IntVector2 Terrain::WorldToHeightMap(const Vector3& worldPosition) const
-{
-    if (!node_)
-        return IntVector2::ZERO;
-
-    Vector3 position = node_->GetWorldTransform().Inverse() * worldPosition;
-    int xPos = (int)((position.x_ - patchWorldOrigin_.x_) / spacing_.x_ + 0.5f);
-    int zPos = (int)((position.z_ - patchWorldOrigin_.y_) / spacing_.z_ + 0.5f);
-    xPos = Clamp(xPos, 0, numVertices_.x_ - 1);
-    zPos = Clamp(zPos, 0, numVertices_.y_ - 1);
-
-    return IntVector2(xPos, numVertices_.y_ - 1 - zPos);
-}
-
-void Terrain::CreatePatchGeometry(TerrainPatch* patch)
-{
-    ATOMIC_PROFILE(CreatePatchGeometry);
-
-    unsigned row = (unsigned)(patchSize_ + 1);
-    VertexBuffer* vertexBuffer = patch->GetVertexBuffer();
-    Geometry* geometry = patch->GetGeometry();
-    Geometry* maxLodGeometry = patch->GetMaxLodGeometry();
-    Geometry* occlusionGeometry = patch->GetOcclusionGeometry();
-
-    if (vertexBuffer->GetVertexCount() != row * row)
-        vertexBuffer->SetSize(row * row, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
-
-    SharedArrayPtr<unsigned char> cpuVertexData(new unsigned char[row * row * sizeof(Vector3)]);
-    SharedArrayPtr<unsigned char> occlusionCpuVertexData(new unsigned char[row * row * sizeof(Vector3)]);
-
-    float* vertexData = (float*)vertexBuffer->Lock(0, vertexBuffer->GetVertexCount());
-    float* positionData = (float*)cpuVertexData.Get();
-    float* occlusionData = (float*)occlusionCpuVertexData.Get();
-    BoundingBox box;
-
-    unsigned occlusionLevel = occlusionLodLevel_;
-    if (occlusionLevel > numLodLevels_ - 1)
-        occlusionLevel = numLodLevels_ - 1;
-
-    if (vertexData)
-    {
-        const IntVector2& coords = patch->GetCoordinates();
-        int lodExpand = (1 << (occlusionLevel)) - 1;
-        int halfLodExpand = (1 << (occlusionLevel)) / 2;
-        
-        for (int z = 0; z <= patchSize_; ++z)
-        {
-            for (int x = 0; x <= patchSize_; ++x)
-            {
-                int xPos = coords.x_ * patchSize_ + x;
-                int zPos = coords.y_ * patchSize_ + z;
-
-                // Position
-                Vector3 position((float)x * spacing_.x_, GetRawHeight(xPos, zPos), (float)z * spacing_.z_);
-                *vertexData++ = position.x_;
-                *vertexData++ = position.y_;
-                *vertexData++ = position.z_;
-                *positionData++ = position.x_;
-                *positionData++ = position.y_;
-                *positionData++ = position.z_;
-
-                box.Merge(position);
-                
-                // For vertices that are part of the occlusion LOD, calculate the minimum height in the neighborhood
-                // to prevent false positive occlusion due to inaccuracy between occlusion LOD & visible LOD
-                float minHeight = position.y_;
-                if (halfLodExpand > 0 && (x & lodExpand) == 0 && (z & lodExpand) == 0)
-                {
-                    int minX = Max(xPos - halfLodExpand, 0);
-                    int maxX = Min(xPos + halfLodExpand, numVertices_.x_ - 1);
-                    int minZ = Max(zPos - halfLodExpand, 0);
-                    int maxZ = Min(zPos + halfLodExpand, numVertices_.y_ - 1);
-                    for (int nZ = minZ; nZ <= maxZ; ++nZ)
-                    {
-                        for (int nX = minX; nX <= maxX; ++nX)
-                            minHeight = Min(minHeight, GetRawHeight(nX, nZ));
-                    }
-                }
-                *occlusionData++ = position.x_;
-                *occlusionData++ = minHeight;
-                *occlusionData++ = position.z_;
-
-                // Normal
-                Vector3 normal = GetRawNormal(xPos, zPos);
-                *vertexData++ = normal.x_;
-                *vertexData++ = normal.y_;
-                *vertexData++ = normal.z_;
-
-                // Texture coordinate
-                Vector2 texCoord((float)xPos / (float)numVertices_.x_, 1.0f - (float)zPos / (float)numVertices_.y_);
-                *vertexData++ = texCoord.x_;
-                *vertexData++ = texCoord.y_;
-
-                // Tangent
-                Vector3 xyz = (Vector3::RIGHT - normal * normal.DotProduct(Vector3::RIGHT)).Normalized();
-                *vertexData++ = xyz.x_;
-                *vertexData++ = xyz.y_;
-                *vertexData++ = xyz.z_;
-                *vertexData++ = 1.0f;
-            }
-        }
-
-        vertexBuffer->Unlock();
-        vertexBuffer->ClearDataLost();
-    }
-
-    patch->SetBoundingBox(box);
-
-    if (drawRanges_.Size())
-    {
-        unsigned occlusionDrawRange = occlusionLevel << 4;
-
-        geometry->SetIndexBuffer(indexBuffer_);
-        geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false);
-        geometry->SetRawVertexData(cpuVertexData, MASK_POSITION);
-        maxLodGeometry->SetIndexBuffer(indexBuffer_);
-        maxLodGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false);
-        maxLodGeometry->SetRawVertexData(cpuVertexData, MASK_POSITION);
-        occlusionGeometry->SetIndexBuffer(indexBuffer_);
-        occlusionGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[occlusionDrawRange].first_, drawRanges_[occlusionDrawRange].second_, false);
-        occlusionGeometry->SetRawVertexData(occlusionCpuVertexData, MASK_POSITION);
-    }
-
-    patch->ResetLod();
-}
-
-void Terrain::UpdatePatchLod(TerrainPatch* patch)
-{
-    Geometry* geometry = patch->GetGeometry();
-
-    // All LOD levels except the coarsest have 16 versions for stitching
-    unsigned lodLevel = patch->GetLodLevel();
-    unsigned drawRangeIndex = lodLevel << 4;
-    if (lodLevel < numLodLevels_ - 1)
-    {
-        TerrainPatch* north = patch->GetNorthPatch();
-        TerrainPatch* south = patch->GetSouthPatch();
-        TerrainPatch* west = patch->GetWestPatch();
-        TerrainPatch* east = patch->GetEastPatch();
-
-        if (north && north->GetLodLevel() > lodLevel)
-            drawRangeIndex |= STITCH_NORTH;
-        if (south && south->GetLodLevel() > lodLevel)
-            drawRangeIndex |= STITCH_SOUTH;
-        if (west && west->GetLodLevel() > lodLevel)
-            drawRangeIndex |= STITCH_WEST;
-        if (east && east->GetLodLevel() > lodLevel)
-            drawRangeIndex |= STITCH_EAST;
-    }
-
-    if (drawRangeIndex < drawRanges_.Size())
-        geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[drawRangeIndex].first_, drawRanges_[drawRangeIndex].second_, false);
-}
-
-void Terrain::SetMaterialAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    SetMaterial(cache->GetResource<Material>(value.name_));
-}
-
-void Terrain::SetHeightMapAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    Image* image = cache->GetResource<Image>(value.name_);
-    SetHeightMapInternal(image, false);
-}
-
-void Terrain::SetPatchSizeAttr(int value)
-{
-    if (value < MIN_PATCH_SIZE || value > MAX_PATCH_SIZE || !IsPowerOfTwo((unsigned)value))
-        return;
-
-    if (value != patchSize_)
-    {
-        patchSize_ = value;
-        recreateTerrain_ = true;
-    }
-}
-
-void Terrain::SetMaxLodLevelsAttr(unsigned value)
-{
-    value = Clamp(value, MIN_LOD_LEVELS, MAX_LOD_LEVELS);
-    
-    if (value != maxLodLevels_)
-    {
-        maxLodLevels_ = value;
-        lastPatchSize_ = 0; // Force full recreate
-        recreateTerrain_ = true;
-    }
-}
-
-void Terrain::SetOcclusionLodLevelAttr(unsigned value)
-{
-    if (value != occlusionLodLevel_)
-    {
-        occlusionLodLevel_ = value;
-        lastPatchSize_ = 0; // Force full recreate
-        recreateTerrain_ = true;
-    }
-}
-
-ResourceRef Terrain::GetMaterialAttr() const
-{
-    return GetResourceRef(material_, Material::GetTypeStatic());
-}
-
-ResourceRef Terrain::GetHeightMapAttr() const
-{
-    return GetResourceRef(heightMap_, Image::GetTypeStatic());
-}
-
-void Terrain::CreateGeometry()
-{
-    recreateTerrain_ = false;
-
-    if (!node_)
-        return;
-
-    ATOMIC_PROFILE(CreateTerrainGeometry);
-
-    unsigned prevNumPatches = patches_.Size();
-
-    // Determine number of LOD levels
-    unsigned lodSize = (unsigned)patchSize_;
-    numLodLevels_ = 1;
-    while (lodSize > MIN_PATCH_SIZE && numLodLevels_ < maxLodLevels_)
-    {
-        lodSize >>= 1;
-        ++numLodLevels_;
-    }
-
-    // Determine total terrain size
-    patchWorldSize_ = Vector2(spacing_.x_ * (float)patchSize_, spacing_.z_ * (float)patchSize_);
-    bool updateAll = false;
-
-    if (heightMap_)
-    {
-        numPatches_ = IntVector2((heightMap_->GetWidth() - 1) / patchSize_, (heightMap_->GetHeight() - 1) / patchSize_);
-        numVertices_ = IntVector2(numPatches_.x_ * patchSize_ + 1, numPatches_.y_ * patchSize_ + 1);
-        patchWorldOrigin_ =
-            Vector2(-0.5f * (float)numPatches_.x_ * patchWorldSize_.x_, -0.5f * (float)numPatches_.y_ * patchWorldSize_.y_);
-        if (numVertices_ != lastNumVertices_ || lastSpacing_ != spacing_ || patchSize_ != lastPatchSize_)
-            updateAll = true;
-        unsigned newDataSize = (unsigned)(numVertices_.x_ * numVertices_.y_);
-
-        // Create new height data if terrain size changed
-        if (!heightData_ || updateAll)
-            heightData_ = new float[newDataSize];
-
-        // Ensure that the source (unsmoothed) data exists if smoothing is active
-        if (smoothing_ && (!sourceHeightData_ || updateAll))
-        {
-            sourceHeightData_ = new float[newDataSize];
-            updateAll = true;
-        }
-        else if (!smoothing_)
-            sourceHeightData_.Reset();
-    }
-    else
-    {
-        numPatches_ = IntVector2::ZERO;
-        numVertices_ = IntVector2::ZERO;
-        patchWorldOrigin_ = Vector2::ZERO;
-        heightData_.Reset();
-        sourceHeightData_.Reset();
-    }
-
-    lastNumVertices_ = numVertices_;
-    lastPatchSize_ = patchSize_;
-    lastSpacing_ = spacing_;
-
-    // Remove old patch nodes which are not needed
-    if (updateAll)
-    {
-        ATOMIC_PROFILE(RemoveOldPatches);
-
-        PODVector<Node*> oldPatchNodes;
-        node_->GetChildrenWithComponent<TerrainPatch>(oldPatchNodes);
-        for (PODVector<Node*>::Iterator i = oldPatchNodes.Begin(); i != oldPatchNodes.End(); ++i)
-        {
-            bool nodeOk = false;
-            Vector<String> coords = (*i)->GetName().Substring(6).Split('_');
-            if (coords.Size() == 2)
-            {
-                int x = ToInt(coords[0]);
-                int z = ToInt(coords[1]);
-                if (x < numPatches_.x_ && z < numPatches_.y_)
-                    nodeOk = true;
-            }
-
-            if (!nodeOk)
-                node_->RemoveChild(*i);
-        }
-    }
-
-    // Keep track of which patches actually need an update
-    PODVector<bool> dirtyPatches((unsigned)(numPatches_.x_ * numPatches_.y_));
-    for (unsigned i = 0; i < dirtyPatches.Size(); ++i)
-        dirtyPatches[i] = updateAll;
-
-    patches_.Clear();
-
-    if (heightMap_)
-    {
-        // Copy heightmap data
-        const unsigned char* src = heightMap_->GetData();
-        float* dest = smoothing_ ? sourceHeightData_ : heightData_;
-        unsigned imgComps = heightMap_->GetComponents();
-        unsigned imgRow = heightMap_->GetWidth() * imgComps;
-        IntRect updateRegion(-1, -1, -1, -1);
-
-        if (imgComps == 1)
-        {
-            ATOMIC_PROFILE(CopyHeightData);
-
-            for (int z = 0; z < numVertices_.y_; ++z)
-            {
-                for (int x = 0; x < numVertices_.x_; ++x)
-                {
-                    float newHeight = (float)src[imgRow * (numVertices_.y_ - 1 - z) + x] * spacing_.y_;
-
-                    if (updateAll)
-                        *dest = newHeight;
-                    else
-                    {
-                        if (*dest != newHeight)
-                        {
-                            *dest = newHeight;
-                            GrowUpdateRegion(updateRegion, x, z);
-                        }
-                    }
-
-                    ++dest;
-                }
-            }
-        }
-        else
-        {
-            ATOMIC_PROFILE(CopyHeightData);
-
-            // If more than 1 component, use the green channel for more accuracy
-            for (int z = 0; z < numVertices_.y_; ++z)
-            {
-                for (int x = 0; x < numVertices_.x_; ++x)
-                {
-                    float newHeight = ((float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x] +
-                                       (float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x + 1] / 256.0f) * spacing_.y_;
-
-                    if (updateAll)
-                        *dest = newHeight;
-                    else
-                    {
-                        if (*dest != newHeight)
-                        {
-                            *dest = newHeight;
-                            GrowUpdateRegion(updateRegion, x, z);
-                        }
-                    }
-
-                    ++dest;
-                }
-            }
-        }
-
-        // If updating a region of the heightmap, check which patches change
-        if (!updateAll)
-        {
-            int lodExpand = 1 << (numLodLevels_ - 1);
-            // Expand the right & bottom 1 pixel more, as patches share vertices at the edge
-            updateRegion.left_ -= lodExpand;
-            updateRegion.right_ += lodExpand + 1;
-            updateRegion.top_ -= lodExpand;
-            updateRegion.bottom_ += lodExpand + 1;
-
-            int sX = Max(updateRegion.left_ / patchSize_, 0);
-            int eX = Min(updateRegion.right_ / patchSize_, numPatches_.x_ - 1);
-            int sY = Max(updateRegion.top_ / patchSize_, 0);
-            int eY = Min(updateRegion.bottom_ / patchSize_, numPatches_.y_ - 1);
-            for (int y = sY; y <= eY; ++y)
-            {
-                for (int x = sX; x <= eX; ++x)
-                    dirtyPatches[y * numPatches_.x_ + x] = true;
-            }
-        }
-
-        patches_.Reserve((unsigned)(numPatches_.x_ * numPatches_.y_));
-
-        bool enabled = IsEnabledEffective();
-
-        {
-            ATOMIC_PROFILE(CreatePatches);
-
-            // Create patches and set node transforms
-            for (int z = 0; z < numPatches_.y_; ++z)
-            {
-                for (int x = 0; x < numPatches_.x_; ++x)
-                {
-                    String nodeName = "Patch_" + String(x) + "_" + String(z);
-                    Node* patchNode = node_->GetChild(nodeName);
-
-                    if (!patchNode)
-                    {
-                        // Create the patch scene node as local and temporary so that it is not unnecessarily serialized to either
-                        // file or replicated over the network
-                        patchNode = node_->CreateChild(nodeName, LOCAL);
-                        patchNode->SetTemporary(true);
-                    }
-
-                    patchNode->SetPosition(Vector3(patchWorldOrigin_.x_ + (float)x * patchWorldSize_.x_, 0.0f,
-                        patchWorldOrigin_.y_ + (float)z * patchWorldSize_.y_));
-
-                    TerrainPatch* patch = patchNode->GetComponent<TerrainPatch>();
-                    if (!patch)
-                    {
-                        patch = patchNode->CreateComponent<TerrainPatch>();
-                        patch->SetOwner(this);
-                        patch->SetCoordinates(IntVector2(x, z));
-
-                        // Copy initial drawable parameters
-                        patch->SetEnabled(enabled);
-                        patch->SetMaterial(material_);
-                        patch->SetDrawDistance(drawDistance_);
-                        patch->SetShadowDistance(shadowDistance_);
-                        patch->SetLodBias(lodBias_);
-                        patch->SetViewMask(viewMask_);
-                        patch->SetLightMask(lightMask_);
-                        patch->SetShadowMask(shadowMask_);
-                        patch->SetZoneMask(zoneMask_);
-                        patch->SetMaxLights(maxLights_);
-                        patch->SetCastShadows(castShadows_);
-                        patch->SetOccluder(occluder_);
-                        patch->SetOccludee(occludee_);
-                    }
-
-                    patches_.Push(WeakPtr<TerrainPatch>(patch));
-                }
-            }
-        }
-
-        // Create the shared index data
-        if (updateAll)
-            CreateIndexData();
-
-        // Create vertex data for patches. First update smoothing to ensure normals are calculated correctly across patch borders
-        if (smoothing_)
-        {
-            ATOMIC_PROFILE(UpdateSmoothing);
-
-            for (unsigned i = 0; i < patches_.Size(); ++i)
-            {
-                if (dirtyPatches[i])
-                {
-                    TerrainPatch* patch = patches_[i];
-                    const IntVector2& coords = patch->GetCoordinates();
-                    int startX = coords.x_ * patchSize_;
-                    int endX = startX + patchSize_;
-                    int startZ = coords.y_ * patchSize_;
-                    int endZ = startZ + patchSize_;
-
-                    for (int z = startZ; z <= endZ; ++z)
-                    {
-                        for (int x = startX; x <= endX; ++x)
-                        {
-                            float smoothedHeight = (
-                                GetSourceHeight(x - 1, z - 1) + GetSourceHeight(x, z - 1) * 2.0f + GetSourceHeight(x + 1, z - 1) +
-                                GetSourceHeight(x - 1, z) * 2.0f + GetSourceHeight(x, z) * 4.0f + GetSourceHeight(x + 1, z) * 2.0f +
-                                GetSourceHeight(x - 1, z + 1) + GetSourceHeight(x, z + 1) * 2.0f + GetSourceHeight(x + 1, z + 1)
-                            ) / 16.0f;
-
-                            heightData_[z * numVertices_.x_ + x] = smoothedHeight;
-                        }
-                    }
-                }
-            }
-        }
-
-        for (unsigned i = 0; i < patches_.Size(); ++i)
-        {
-            TerrainPatch* patch = patches_[i];
-
-            if (dirtyPatches[i])
-            {
-                CreatePatchGeometry(patch);
-                CalculateLodErrors(patch);
-            }
-
-            SetNeighbors(patch);
-        }
-    }
-
-    // Send event only if new geometry was generated, or the old was cleared
-    if (patches_.Size() || prevNumPatches)
-    {
-        using namespace TerrainCreated;
-
-        VariantMap& eventData = GetEventDataMap();
-        eventData[P_NODE] = node_;
-        node_->SendEvent(E_TERRAINCREATED, eventData);
-    }
-}
-
-void Terrain::CreateIndexData()
-{
-    ATOMIC_PROFILE(CreateIndexData);
-
-    PODVector<unsigned short> indices;
-    drawRanges_.Clear();
-    unsigned row = (unsigned)(patchSize_ + 1);
-
-    /* Build index data for each LOD level. Each LOD level except the lowest can stitch to the next lower LOD from the edges:
-       north, south, west, east, or any combination of them, requiring 16 different versions of each LOD level's index data
-
-       Normal edge:     Stitched edge:
-       +----+----+      +---------+
-       |\   |\   |      |\       /|
-       | \  | \  |      | \     / |
-       |  \ |  \ |      |  \   /  |
-       |   \|   \|      |   \ /   |
-       +----+----+      +----+----+
-    */
-    for (unsigned i = 0; i < numLodLevels_; ++i)
-    {
-        unsigned combinations = (i < numLodLevels_ - 1) ? 16 : 1;
-        int skip = 1 << i;
-
-        for (unsigned j = 0; j < combinations; ++j)
-        {
-            unsigned indexStart = indices.Size();
-
-            int zStart = 0;
-            int xStart = 0;
-            int zEnd = patchSize_;
-            int xEnd = patchSize_;
-
-            if (j & STITCH_NORTH)
-                zEnd -= skip;
-            if (j & STITCH_SOUTH)
-                zStart += skip;
-            if (j & STITCH_WEST)
-                xStart += skip;
-            if (j & STITCH_EAST)
-                xEnd -= skip;
-
-            // Build the main grid
-            for (int z = zStart; z < zEnd; z += skip)
-            {
-                for (int x = xStart; x < xEnd; x += skip)
-                {
-                    indices.Push((unsigned short)((z + skip) * row + x));
-                    indices.Push((unsigned short)(z * row + x + skip));
-                    indices.Push((unsigned short)(z * row + x));
-                    indices.Push((unsigned short)((z + skip) * row + x));
-                    indices.Push((unsigned short)((z + skip) * row + x + skip));
-                    indices.Push((unsigned short)(z * row + x + skip));
-                }
-            }
-
-            // Build the north edge
-            if (j & STITCH_NORTH)
-            {
-                int z = patchSize_ - skip;
-                for (int x = 0; x < patchSize_; x += skip * 2)
-                {
-                    if (x > 0 || (j & STITCH_WEST) == 0)
-                    {
-                        indices.Push((unsigned short)((z + skip) * row + x));
-                        indices.Push((unsigned short)(z * row + x + skip));
-                        indices.Push((unsigned short)(z * row + x));
-                    }
-                    indices.Push((unsigned short)((z + skip) * row + x));
-                    indices.Push((unsigned short)((z + skip) * row + x + 2 * skip));
-                    indices.Push((unsigned short)(z * row + x + skip));
-                    if (x < patchSize_ - skip * 2 || (j & STITCH_EAST) == 0)
-                    {
-                        indices.Push((unsigned short)((z + skip) * row + x + 2 * skip));
-                        indices.Push((unsigned short)(z * row + x + 2 * skip));
-                        indices.Push((unsigned short)(z * row + x + skip));
-                    }
-                }
-            }
-
-            // Build the south edge
-            if (j & STITCH_SOUTH)
-            {
-                int z = 0;
-                for (int x = 0; x < patchSize_; x += skip * 2)
-                {
-                    if (x > 0 || (j & STITCH_WEST) == 0)
-                    {
-                        indices.Push((unsigned short)((z + skip) * row + x));
-                        indices.Push((unsigned short)((z + skip) * row + x + skip));
-                        indices.Push((unsigned short)(z * row + x));
-                    }
-                    indices.Push((unsigned short)(z * row + x));
-                    indices.Push((unsigned short)((z + skip) * row + x + skip));
-                    indices.Push((unsigned short)(z * row + x + 2 * skip));
-                    if (x < patchSize_ - skip * 2 || (j & STITCH_EAST) == 0)
-                    {
-                        indices.Push((unsigned short)((z + skip) * row + x + skip));
-                        indices.Push((unsigned short)((z + skip) * row + x + 2 * skip));
-                        indices.Push((unsigned short)(z * row + x + 2 * skip));
-                    }
-                }
-            }
-
-            // Build the west edge
-            if (j & STITCH_WEST)
-            {
-                int x = 0;
-                for (int z = 0; z < patchSize_; z += skip * 2)
-                {
-                    if (z > 0 || (j & STITCH_SOUTH) == 0)
-                    {
-                        indices.Push((unsigned short)(z * row + x));
-                        indices.Push((unsigned short)((z + skip) * row + x + skip));
-                        indices.Push((unsigned short)(z * row + x + skip));
-                    }
-                    indices.Push((unsigned short)((z + 2 * skip) * row + x));
-                    indices.Push((unsigned short)((z + skip) * row + x + skip));
-                    indices.Push((unsigned short)(z * row + x));
-                    if (z < patchSize_ - skip * 2 || (j & STITCH_NORTH) == 0)
-                    {
-                        indices.Push((unsigned short)((z + 2 * skip) * row + x));
-                        indices.Push((unsigned short)((z + 2 * skip) * row + x + skip));
-                        indices.Push((unsigned short)((z + skip) * row + x + skip));
-                    }
-                }
-            }
-
-            // Build the east edge
-            if (j & STITCH_EAST)
-            {
-                int x = patchSize_ - skip;
-                for (int z = 0; z < patchSize_; z += skip * 2)
-                {
-                    if (z > 0 || (j & STITCH_SOUTH) == 0)
-                    {
-                        indices.Push((unsigned short)(z * row + x));
-                        indices.Push((unsigned short)((z + skip) * row + x));
-                        indices.Push((unsigned short)(z * row + x + skip));
-                    }
-                    indices.Push((unsigned short)((z + skip) * row + x));
-                    indices.Push((unsigned short)((z + 2 * skip) * row + x + skip));
-                    indices.Push((unsigned short)(z * row + x + skip));
-                    if (z < patchSize_ - skip * 2 || (j & STITCH_NORTH) == 0)
-                    {
-                        indices.Push((unsigned short)((z + skip) * row + x));
-                        indices.Push((unsigned short)((z + 2 * skip) * row + x));
-                        indices.Push((unsigned short)((z + 2 * skip) * row + x + skip));
-                    }
-                }
-            }
-
-            drawRanges_.Push(MakePair(indexStart, indices.Size() - indexStart));
-        }
-    }
-
-    indexBuffer_->SetSize(indices.Size(), false);
-    indexBuffer_->SetData(&indices[0]);
-}
-
-float Terrain::GetRawHeight(int x, int z) const
-{
-    if (!heightData_)
-        return 0.0f;
-
-    x = Clamp(x, 0, numVertices_.x_ - 1);
-    z = Clamp(z, 0, numVertices_.y_ - 1);
-    return heightData_[z * numVertices_.x_ + x];
-}
-
-float Terrain::GetSourceHeight(int x, int z) const
-{
-    if (!sourceHeightData_)
-        return 0.0f;
-
-    x = Clamp(x, 0, numVertices_.x_ - 1);
-    z = Clamp(z, 0, numVertices_.y_ - 1);
-    return sourceHeightData_[z * numVertices_.x_ + x];
-}
-
-float Terrain::GetLodHeight(int x, int z, unsigned lodLevel) const
-{
-    unsigned offset = (unsigned)(1 << lodLevel);
-    float divisor = (float)offset;
-    float xFrac = (float)(x % offset) / divisor;
-    float zFrac = (float)(z % offset) / divisor;
-    float h1, h2, h3;
-
-    if (xFrac + zFrac >= 1.0f)
-    {
-        h1 = GetRawHeight(x + offset, z + offset);
-        h2 = GetRawHeight(x, z + offset);
-        h3 = GetRawHeight(x + offset, z);
-        xFrac = 1.0f - xFrac;
-        zFrac = 1.0f - zFrac;
-    }
-    else
-    {
-        h1 = GetRawHeight(x, z);
-        h2 = GetRawHeight(x + offset, z);
-        h3 = GetRawHeight(x, z + offset);
-    }
-
-    return h1 * (1.0f - xFrac - zFrac) + h2 * xFrac + h3 * zFrac;
-}
-
-Vector3 Terrain::GetRawNormal(int x, int z) const
-{
-    float baseHeight = GetRawHeight(x, z);
-    float nSlope = GetRawHeight(x, z - 1) - baseHeight;
-    float neSlope = GetRawHeight(x + 1, z - 1) - baseHeight;
-    float eSlope = GetRawHeight(x + 1, z) - baseHeight;
-    float seSlope = GetRawHeight(x + 1, z + 1) - baseHeight;
-    float sSlope = GetRawHeight(x, z + 1) - baseHeight;
-    float swSlope = GetRawHeight(x - 1, z + 1) - baseHeight;
-    float wSlope = GetRawHeight(x - 1, z) - baseHeight;
-    float nwSlope = GetRawHeight(x - 1, z - 1) - baseHeight;
-    float up = 0.5f * (spacing_.x_ + spacing_.z_);
-
-    return (Vector3(0.0f, up, nSlope) +
-            Vector3(-neSlope, up, neSlope) +
-            Vector3(-eSlope, up, 0.0f) +
-            Vector3(-seSlope, up, -seSlope) +
-            Vector3(0.0f, up, -sSlope) +
-            Vector3(swSlope, up, -swSlope) +
-            Vector3(wSlope, up, 0.0f) +
-            Vector3(nwSlope, up, nwSlope)).Normalized();
-}
-
-void Terrain::CalculateLodErrors(TerrainPatch* patch)
-{
-    ATOMIC_PROFILE(CalculateLodErrors);
-
-    const IntVector2& coords = patch->GetCoordinates();
-    PODVector<float>& lodErrors = patch->GetLodErrors();
-    lodErrors.Clear();
-    lodErrors.Reserve(numLodLevels_);
-
-    int xStart = coords.x_ * patchSize_;
-    int zStart = coords.y_ * patchSize_;
-    int xEnd = xStart + patchSize_;
-    int zEnd = zStart + patchSize_;
-
-    for (unsigned i = 0; i < numLodLevels_; ++i)
-    {
-        float maxError = 0.0f;
-        int divisor = 1 << i;
-
-        if (i > 0)
-        {
-            for (int z = zStart; z <= zEnd; ++z)
-            {
-                for (int x = xStart; x <= xEnd; ++x)
-                {
-                    if (x % divisor || z % divisor)
-                    {
-                        float error = Abs(GetLodHeight(x, z, i) - GetRawHeight(x, z));
-                        maxError = Max(error, maxError);
-                    }
-                }
-            }
-
-            // Set error to be at least same as (half vertex spacing x LOD) to prevent horizontal stretches getting too inaccurate
-            maxError = Max(maxError, 0.25f * (spacing_.x_ + spacing_.z_) * (float)(1 << i));
-        }
-
-        lodErrors.Push(maxError);
-    }
-}
-
-void Terrain::SetNeighbors(TerrainPatch* patch)
-{
-    const IntVector2& coords = patch->GetCoordinates();
-    patch->SetNeighbors(GetPatch(coords.x_, coords.y_ + 1), GetPatch(coords.x_, coords.y_ - 1),
-        GetPatch(coords.x_ - 1, coords.y_), GetPatch(coords.x_ + 1, coords.y_));
-}
-
-bool Terrain::SetHeightMapInternal(Image* image, bool recreateNow)
-{
-    if (image && image->IsCompressed())
-    {
-        ATOMIC_LOGERROR("Can not use a compressed image as a terrain heightmap");
-        return false;
-    }
-
-    // Unsubscribe from the reload event of previous image (if any), then subscribe to the new
-    if (heightMap_)
-        UnsubscribeFromEvent(heightMap_, E_RELOADFINISHED);
-    if (image)
-        SubscribeToEvent(image, E_RELOADFINISHED, ATOMIC_HANDLER(Terrain, HandleHeightMapReloadFinished));
-
-    heightMap_ = image;
-
-    if (recreateNow)
-        CreateGeometry();
-    else
-        recreateTerrain_ = true;
-
-    return true;
-}
-
-void Terrain::HandleHeightMapReloadFinished(StringHash eventType, VariantMap& eventData)
-{
-    CreateGeometry();
-}
-
-}

+ 0 - 279
Source/Atomic/Atomic3D/Terrain.h

@@ -1,279 +0,0 @@
-//
-// 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 "../Scene/Component.h"
-
-namespace Atomic
-{
-
-class Image;
-class IndexBuffer;
-class Material;
-class Node;
-class TerrainPatch;
-
-/// Heightmap terrain component.
-class ATOMIC_API Terrain : public Component
-{
-    ATOMIC_OBJECT(Terrain, Component);
-
-public:
-    /// Construct.
-    Terrain(Context* context);
-    /// Destruct.
-    ~Terrain();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-
-    /// Handle attribute write access.
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
-    /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
-    virtual void ApplyAttributes();
-    /// Handle enabled/disabled state change.
-    virtual void OnSetEnabled();
-
-    /// Set patch quads per side. Must be a power of two.
-    void SetPatchSize(int size);
-    /// Set vertex (XZ) and height (Y) spacing.
-    void SetSpacing(const Vector3& spacing);
-    /// Set maximum number of LOD levels for terrain patches. This can be between 1-4.
-    void SetMaxLodLevels(unsigned levels);
-    /// Set LOD level used for terrain patch occlusion. By default (M_MAX_UNSIGNED) the coarsest. Since the LOD level used needs to be fixed, using finer LOD levels may result in false positive occlusion in cases where the actual rendered geometry is coarser, so use with caution.
-    void SetOcclusionLodLevel(unsigned level);
-    /// Set smoothing of heightmap.
-    void SetSmoothing(bool enable);
-    /// Set heightmap image. Dimensions should be a power of two + 1. Uses 8-bit grayscale, or optionally red as MSB and green as LSB for 16-bit accuracy. Return true if successful.
-    bool SetHeightMap(Image* image);
-    /// Set material.
-    void SetMaterial(Material* material);
-    /// Set draw distance for patches.
-    void SetDrawDistance(float distance);
-    /// Set shadow draw distance for patches.
-    void SetShadowDistance(float distance);
-    /// Set LOD bias for patches. Affects which terrain LOD to display.
-    void SetLodBias(float bias);
-    /// Set view mask for patches. Is and'ed with camera's view mask to see if the object should be rendered.
-    void SetViewMask(unsigned mask);
-    /// Set light mask for patches. Is and'ed with light's and zone's light mask to see if the object should be lit.
-    void SetLightMask(unsigned mask);
-    /// Set shadow mask for patches. Is and'ed with light's light mask and zone's shadow mask to see if the object should be rendered to a shadow map.
-    void SetShadowMask(unsigned mask);
-    /// Set zone mask for patches. Is and'ed with zone's zone mask to see if the object should belong to the zone.
-    void SetZoneMask(unsigned mask);
-    /// Set maximum number of per-pixel lights for patches. Default 0 is unlimited.
-    void SetMaxLights(unsigned num);
-    /// Set shadowcaster flag for patches.
-    void SetCastShadows(bool enable);
-    /// Set occlusion flag for patches. Occlusion uses the coarsest LOD by default.
-    void SetOccluder(bool enable);
-    /// Set occludee flag for patches.
-    void SetOccludee(bool enable);
-    /// Apply changes from the heightmap image.
-    void ApplyHeightMap();
-
-    /// Return patch quads per side.
-    int GetPatchSize() const { return patchSize_; }
-
-    /// Return vertex and height spacing.
-    const Vector3& GetSpacing() const { return spacing_; }
-
-    /// Return heightmap size in vertices.
-    const IntVector2& GetNumVertices() const { return numVertices_; }
-
-    /// Return heightmap size in patches.
-    const IntVector2& GetNumPatches() const { return numPatches_; }
-
-    /// Return maximum number of LOD levels for terrain patches. This can be between 1-4.
-    unsigned GetMaxLodLevels() const { return maxLodLevels_; }
-    
-    /// Return LOD level used for occlusion.
-    unsigned GetOcclusionLodLevel() const { return occlusionLodLevel_; }
-    
-    /// Return whether smoothing is in use.
-    bool GetSmoothing() const { return smoothing_; }
-
-    /// Return heightmap image.
-    Image* GetHeightMap() const;
-    /// Return material.
-    Material* GetMaterial() const;
-    /// Return patch by index.
-    TerrainPatch* GetPatch(unsigned index) const;
-    /// Return patch by patch coordinates.
-    TerrainPatch* GetPatch(int x, int z) const;
-    /// Return height at world coordinates.
-    float GetHeight(const Vector3& worldPosition) const;
-    /// Return normal at world coordinates.
-    Vector3 GetNormal(const Vector3& worldPosition) const;
-    /// Convert world position to heightmap pixel position. Note that the internal height data representation is reversed vertically, but in the heightmap image north is at the top.
-    IntVector2 WorldToHeightMap(const Vector3& worldPosition) const;
-
-    /// Return raw height data.
-    SharedArrayPtr<float> GetHeightData() const { return heightData_; }
-
-    /// Return draw distance.
-    float GetDrawDistance() const { return drawDistance_; }
-
-    /// Return shadow draw distance.
-    float GetShadowDistance() const { return shadowDistance_; }
-
-    /// Return LOD bias.
-    float GetLodBias() const { return lodBias_; }
-
-    /// Return view mask.
-    unsigned GetViewMask() const { return viewMask_; }
-
-    /// Return light mask.
-    unsigned GetLightMask() const { return lightMask_; }
-
-    /// Return shadow mask.
-    unsigned GetShadowMask() const { return shadowMask_; }
-
-    /// Return zone mask.
-    unsigned GetZoneMask() const { return zoneMask_; }
-
-    /// Return maximum number of per-pixel lights.
-    unsigned GetMaxLights() const { return maxLights_; }
-
-    /// Return visible flag.
-    bool IsVisible() const { return visible_; }
-
-    /// Return shadowcaster flag.
-    bool GetCastShadows() const { return castShadows_; }
-
-    /// Return occluder flag.
-    bool IsOccluder() const { return occluder_; }
-
-    /// Return occludee flag.
-    bool IsOccludee() const { return occludee_; }
-
-    /// Regenerate patch geometry.
-    void CreatePatchGeometry(TerrainPatch* patch);
-    /// Update patch based on LOD and neighbor LOD.
-    void UpdatePatchLod(TerrainPatch* patch);
-    /// Set heightmap attribute.
-    void SetHeightMapAttr(const ResourceRef& value);
-    /// Set material attribute.
-    void SetMaterialAttr(const ResourceRef& value);
-    /// Set patch size attribute.
-    void SetPatchSizeAttr(int value);
-    /// Set max LOD levels attribute.
-    void SetMaxLodLevelsAttr(unsigned value);
-    /// Set occlusion LOD level attribute.
-    void SetOcclusionLodLevelAttr(unsigned value);
-    /// Return heightmap attribute.
-    ResourceRef GetHeightMapAttr() const;
-    /// Return material attribute.
-    ResourceRef GetMaterialAttr() const;
-
-private:
-    /// Regenerate terrain geometry.
-    void CreateGeometry();
-    /// Create index data shared by all patches.
-    void CreateIndexData();
-    /// Return an uninterpolated terrain height value, clamping to edges.
-    float GetRawHeight(int x, int z) const;
-    /// Return a source terrain height value, clamping to edges. The source data is used for smoothing.
-    float GetSourceHeight(int x, int z) const;
-    /// Return interpolated height for a specific LOD level.
-    float GetLodHeight(int x, int z, unsigned lodLevel) const;
-    /// Get slope-based terrain normal at position.
-    Vector3 GetRawNormal(int x, int z) const;
-    /// Calculate LOD errors for a patch.
-    void CalculateLodErrors(TerrainPatch* patch);
-    /// Set neighbors for a patch.
-    void SetNeighbors(TerrainPatch* patch);
-    /// Set heightmap image and optionally recreate the geometry immediately. Return true if successful.
-    bool SetHeightMapInternal(Image* image, bool recreateNow);
-    /// Handle heightmap image reload finished.
-    void HandleHeightMapReloadFinished(StringHash eventType, VariantMap& eventData);
-
-    /// Shared index buffer.
-    SharedPtr<IndexBuffer> indexBuffer_;
-    /// Heightmap image.
-    SharedPtr<Image> heightMap_;
-    /// Height data.
-    SharedArrayPtr<float> heightData_;
-    /// Source height data for smoothing.
-    SharedArrayPtr<float> sourceHeightData_;
-    /// Material.
-    SharedPtr<Material> material_;
-    /// Terrain patches.
-    Vector<WeakPtr<TerrainPatch> > patches_;
-    /// Draw ranges for different LODs and stitching combinations.
-    PODVector<Pair<unsigned, unsigned> > drawRanges_;
-    /// Vertex and height spacing.
-    Vector3 spacing_;
-    /// Vertex and height sacing at the time of last update.
-    Vector3 lastSpacing_;
-    /// Origin of patches on the XZ-plane.
-    Vector2 patchWorldOrigin_;
-    /// Size of a patch on the XZ-plane.
-    Vector2 patchWorldSize_;
-    /// Terrain size in vertices.
-    IntVector2 numVertices_;
-    /// Terrain size in vertices at the time of last update.
-    IntVector2 lastNumVertices_;
-    /// Terrain size in patches.
-    IntVector2 numPatches_;
-    /// Patch size, quads per side.
-    int patchSize_;
-    /// Patch size at the time of last update.
-    int lastPatchSize_;
-    /// Number of terrain LOD levels.
-    unsigned numLodLevels_;
-    /// Maximum number of LOD levels.
-    unsigned maxLodLevels_;
-    /// LOD level used for occlusion.
-    unsigned occlusionLodLevel_;
-    /// Smoothing enable flag.
-    bool smoothing_;
-    /// Visible flag.
-    bool visible_;
-    /// Shadowcaster flag.
-    bool castShadows_;
-    /// Occluder flag.
-    bool occluder_;
-    /// Occludee flag.
-    bool occludee_;
-    /// View mask.
-    unsigned viewMask_;
-    /// Light mask.
-    unsigned lightMask_;
-    /// Shadow mask.
-    unsigned shadowMask_;
-    /// Zone mask.
-    unsigned zoneMask_;
-    /// Draw distance.
-    float drawDistance_;
-    /// Shadow distance.
-    float shadowDistance_;
-    /// LOD bias.
-    float lodBias_;
-    /// Maximum lights.
-    unsigned maxLights_;
-    /// Terrain needs regeneration flag.
-    bool recreateTerrain_;
-};
-
-}

+ 0 - 294
Source/Atomic/Atomic3D/TerrainPatch.cpp

@@ -1,294 +0,0 @@
-//
-// 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 "../Core/Context.h"
-#include "../Graphics/Camera.h"
-#include "../Graphics/DebugRenderer.h"
-#include "../Graphics/Geometry.h"
-#include "../Graphics/IndexBuffer.h"
-#include "../Graphics/Material.h"
-#include "../Graphics/OcclusionBuffer.h"
-#include "../Graphics/OctreeQuery.h"
-#include "../Graphics/Terrain.h"
-#include "../Graphics/TerrainPatch.h"
-#include "../Graphics/VertexBuffer.h"
-#include "../IO/Log.h"
-#include "../Scene/Node.h"
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-static const float LOD_CONSTANT = 1.0f / 150.0f;
-
-extern const char* GEOMETRY_CATEGORY;
-
-TerrainPatch::TerrainPatch(Context* context) :
-    Drawable(context, DRAWABLE_GEOMETRY),
-    geometry_(new Geometry(context)),
-    maxLodGeometry_(new Geometry(context)),
-    occlusionGeometry_(new Geometry(context)),
-    vertexBuffer_(new VertexBuffer(context)),
-    coordinates_(IntVector2::ZERO),
-    lodLevel_(0)
-{
-    geometry_->SetVertexBuffer(0, vertexBuffer_);
-    maxLodGeometry_->SetVertexBuffer(0, vertexBuffer_);
-    occlusionGeometry_->SetVertexBuffer(0, vertexBuffer_);
-
-    batches_.Resize(1);
-    batches_[0].geometry_ = geometry_;
-    batches_[0].geometryType_ = GEOM_STATIC_NOINSTANCING;
-}
-
-TerrainPatch::~TerrainPatch()
-{
-}
-
-void TerrainPatch::RegisterObject(Context* context)
-{
-    context->RegisterFactory<TerrainPatch>();
-}
-
-void TerrainPatch::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
-{
-    RayQueryLevel level = query.level_;
-
-    switch (level)
-    {
-    case RAY_AABB:
-        Drawable::ProcessRayQuery(query, results);
-        break;
-
-    case RAY_OBB:
-    case RAY_TRIANGLE:
-        {
-            Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-            Ray localRay = query.ray_.Transformed(inverse);
-            float distance = localRay.HitDistance(boundingBox_);
-            Vector3 normal = -query.ray_.direction_;
-
-            if (level == RAY_TRIANGLE && distance < query.maxDistance_)
-            {
-                Vector3 geometryNormal;
-                distance = geometry_->GetHitDistance(localRay, &geometryNormal);
-                normal = (node_->GetWorldTransform() * Vector4(geometryNormal, 0.0f)).Normalized();
-            }
-
-            if (distance < query.maxDistance_)
-            {
-                RayQueryResult result;
-                result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
-                result.normal_ = normal;
-                result.distance_ = distance;
-                result.drawable_ = this;
-                result.node_ = node_;
-                result.subObject_ = M_MAX_UNSIGNED;
-                results.Push(result);
-            }
-        }
-        break;
-
-    case RAY_TRIANGLE_UV:
-        ATOMIC_LOGWARNING("RAY_TRIANGLE_UV query level is not supported for TerrainPatch component");
-        break;
-    }
-}
-
-void TerrainPatch::UpdateBatches(const FrameInfo& frame)
-{
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-    distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center());
-
-    float scale = worldTransform.Scale().DotProduct(DOT_SCALE);
-    lodDistance_ = frame.camera_->GetLodDistance(distance_, scale, lodBias_);
-
-    batches_[0].distance_ = distance_;
-    batches_[0].worldTransform_ = &worldTransform;
-
-    unsigned newLodLevel = 0;
-    for (unsigned i = 0; i < lodErrors_.Size(); ++i)
-    {
-        if (lodErrors_[i] / lodDistance_ > LOD_CONSTANT)
-            break;
-        else
-            newLodLevel = i;
-    }
-
-    lodLevel_ = GetCorrectedLodLevel(newLodLevel);
-}
-
-void TerrainPatch::UpdateGeometry(const FrameInfo& frame)
-{
-    if (vertexBuffer_->IsDataLost())
-    {
-        if (owner_)
-            owner_->CreatePatchGeometry(this);
-        else
-            vertexBuffer_->ClearDataLost();
-    }
-
-    if (owner_)
-        owner_->UpdatePatchLod(this);
-}
-
-UpdateGeometryType TerrainPatch::GetUpdateGeometryType()
-{
-    // Because there is a latency in starting worker thread updates, and the update of terrain patch LOD should not take
-    // much time, always update in the main thread
-    return UPDATE_MAIN_THREAD;
-}
-
-Geometry* TerrainPatch::GetLodGeometry(unsigned batchIndex, unsigned level)
-{
-    if (!level)
-        return maxLodGeometry_;
-    else
-        return geometry_;
-}
-
-unsigned TerrainPatch::GetNumOccluderTriangles()
-{
-    // Check that the material is suitable for occlusion (default material always is)
-    Material* mat = batches_[0].material_;
-    if (mat && !mat->GetOcclusion())
-        return 0;
-    else
-        return occlusionGeometry_->GetIndexCount() / 3;
-}
-
-bool TerrainPatch::DrawOcclusion(OcclusionBuffer* buffer)
-{
-    // Check that the material is suitable for occlusion (default material always is) and set culling mode
-    Material* material = batches_[0].material_;
-    if (material)
-    {
-        if (!material->GetOcclusion())
-            return true;
-        buffer->SetCullMode(material->GetCullMode());
-    }
-    else
-        buffer->SetCullMode(CULL_CCW);
-
-    const unsigned char* vertexData;
-    unsigned vertexSize;
-    const unsigned char* indexData;
-    unsigned indexSize;
-    const PODVector<VertexElement>* elements;
-
-    occlusionGeometry_->GetRawData(vertexData, vertexSize, indexData, indexSize, elements);
-    // Check for valid geometry data
-    if (!vertexData || !indexData || !elements || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0)
-        return false;
-
-    // Draw and check for running out of triangles
-    return buffer->AddTriangles(node_->GetWorldTransform(), vertexData, vertexSize, indexData, indexSize, occlusionGeometry_->GetIndexStart(),
-        occlusionGeometry_->GetIndexCount());
-}
-
-void TerrainPatch::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
-{
-    // Intentionally no operation
-}
-
-void TerrainPatch::SetOwner(Terrain* terrain)
-{
-    owner_ = terrain;
-}
-
-void TerrainPatch::SetNeighbors(TerrainPatch* north, TerrainPatch* south, TerrainPatch* west, TerrainPatch* east)
-{
-    north_ = north;
-    south_ = south;
-    west_ = west;
-    east_ = east;
-}
-
-void TerrainPatch::SetMaterial(Material* material)
-{
-    batches_[0].material_ = material;
-}
-
-void TerrainPatch::SetBoundingBox(const BoundingBox& box)
-{
-    boundingBox_ = box;
-    OnMarkedDirty(node_);
-}
-
-void TerrainPatch::SetCoordinates(const IntVector2& coordinates)
-{
-    coordinates_ = coordinates;
-}
-
-void TerrainPatch::ResetLod()
-{
-    lodLevel_ = 0;
-}
-
-Geometry* TerrainPatch::GetGeometry() const
-{
-    return geometry_;
-}
-
-Geometry* TerrainPatch::GetMaxLodGeometry() const
-{
-    return maxLodGeometry_;
-}
-
-Geometry* TerrainPatch::GetOcclusionGeometry() const
-{
-    return occlusionGeometry_;
-}
-
-VertexBuffer* TerrainPatch::GetVertexBuffer() const
-{
-    return vertexBuffer_;
-}
-
-Terrain* TerrainPatch::GetOwner() const
-{
-    return owner_;
-}
-
-void TerrainPatch::OnWorldBoundingBoxUpdate()
-{
-    worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
-}
-
-unsigned TerrainPatch::GetCorrectedLodLevel(unsigned lodLevel)
-{
-    if (north_)
-        lodLevel = Min(lodLevel, north_->GetLodLevel() + 1);
-    if (south_)
-        lodLevel = Min(lodLevel, south_->GetLodLevel() + 1);
-    if (west_)
-        lodLevel = Min(lodLevel, west_->GetLodLevel() + 1);
-    if (east_)
-        lodLevel = Min(lodLevel, east_->GetLodLevel() + 1);
-
-    return lodLevel;
-}
-
-}

+ 0 - 143
Source/Atomic/Atomic3D/TerrainPatch.h

@@ -1,143 +0,0 @@
-//
-// 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 "../Graphics/Drawable.h"
-
-namespace Atomic
-{
-
-class Geometry;
-class Terrain;
-class VertexBuffer;
-
-/// Individually rendered part of a heightmap terrain.
-class ATOMIC_API TerrainPatch : public Drawable
-{
-    ATOMIC_OBJECT(TerrainPatch, Drawable);
-
-public:
-    /// Construct.
-    TerrainPatch(Context* context);
-    /// Destruct.
-    ~TerrainPatch();
-    /// Register object factory.
-    static void RegisterObject(Context* context);
-
-    /// Process octree raycast. May be called from a worker thread.
-    virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
-    /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
-    virtual void UpdateBatches(const FrameInfo& frame);
-    /// Prepare geometry for rendering. Called from a worker thread if possible (no GPU update.)
-    virtual void UpdateGeometry(const FrameInfo& frame);
-    /// Return whether a geometry update is necessary, and if it can happen in a worker thread.
-    virtual UpdateGeometryType GetUpdateGeometryType();
-    /// Return the geometry for a specific LOD level.
-    virtual Geometry* GetLodGeometry(unsigned batchIndex, unsigned level);
-    /// Return number of occlusion geometry triangles.
-    virtual unsigned GetNumOccluderTriangles();
-    /// Draw to occlusion buffer. Return true if did not run out of triangles.
-    virtual bool DrawOcclusion(OcclusionBuffer* buffer);
-    /// Visualize the component as debug geometry.
-    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
-
-    /// Set owner terrain.
-    void SetOwner(Terrain* terrain);
-    /// Set neighbor patches.
-    void SetNeighbors(TerrainPatch* north, TerrainPatch* south, TerrainPatch* west, TerrainPatch* east);
-    /// Set material.
-    void SetMaterial(Material* material);
-    /// Set local-space bounding box.
-    void SetBoundingBox(const BoundingBox& box);
-    /// Set patch coordinates.
-    void SetCoordinates(const IntVector2& coordinates);
-    /// Reset to LOD level 0.
-    void ResetLod();
-
-    /// Return visible geometry.
-    Geometry* GetGeometry() const;
-    /// Return max LOD geometry. Used for decals.
-    Geometry* GetMaxLodGeometry() const;
-    /// Return geometry used for occlusion.
-    Geometry* GetOcclusionGeometry() const;
-    /// Return vertex buffer.
-    VertexBuffer* GetVertexBuffer() const;
-    /// Return owner terrain.
-    Terrain* GetOwner() const;
-
-    /// Return north neighbor patch.
-    TerrainPatch* GetNorthPatch() const { return north_; }
-
-    /// Return south neighbor patch.
-    TerrainPatch* GetSouthPatch() const { return south_; }
-
-    /// Return west neighbor patch.
-    TerrainPatch* GetWestPatch() const { return west_; }
-
-    /// Return east neighbor patch.
-    TerrainPatch* GetEastPatch() const { return east_; }
-
-    /// Return geometrical error array.
-    PODVector<float>& GetLodErrors() { return lodErrors_; }
-
-    /// Return patch coordinates.
-    const IntVector2& GetCoordinates() const { return coordinates_; }
-
-    /// Return current LOD level.
-    unsigned GetLodLevel() const { return lodLevel_; }
-
-protected:
-    /// Recalculate the world-space bounding box.
-    virtual void OnWorldBoundingBoxUpdate();
-
-private:
-    /// Return a corrected LOD level to ensure stitching can work correctly.
-    unsigned GetCorrectedLodLevel(unsigned lodLevel);
-
-    /// Geometry.
-    SharedPtr<Geometry> geometry_;
-    /// Geometry that is locked to the max LOD level. Used for decals.
-    SharedPtr<Geometry> maxLodGeometry_;
-    /// Geometry that is used for occlusion.
-    SharedPtr<Geometry> occlusionGeometry_;
-    /// Vertex buffer.
-    SharedPtr<VertexBuffer> vertexBuffer_;
-    /// Parent terrain.
-    WeakPtr<Terrain> owner_;
-    /// North neighbor patch.
-    WeakPtr<TerrainPatch> north_;
-    /// South neighbor patch.
-    WeakPtr<TerrainPatch> south_;
-    /// West neighbor patch.
-    WeakPtr<TerrainPatch> west_;
-    /// East neighbor patch.
-    WeakPtr<TerrainPatch> east_;
-    /// Geometrical error per LOD level.
-    PODVector<float> lodErrors_;
-    /// Patch coordinates in the terrain. (0,0) is the northwest corner.
-    IntVector2 coordinates_;
-    /// Current LOD level.
-    unsigned lodLevel_;
-};
-
-}