aboutsummaryrefslogtreecommitdiff
path: root/NvBlast/sdk/toolkit/source
diff options
context:
space:
mode:
authorBryan Galdrikian <[email protected]>2017-02-21 12:07:59 -0800
committerBryan Galdrikian <[email protected]>2017-02-21 12:07:59 -0800
commit446ce137c6823ba9eff273bdafdaf266287c7c98 (patch)
treed20aab3e2ed08d7b3ca71c2f40db6a93ea00c459 /NvBlast/sdk/toolkit/source
downloadblast-1.0.0-beta.tar.xz
blast-1.0.0-beta.zip
first commitv1.0.0-beta
Diffstat (limited to 'NvBlast/sdk/toolkit/source')
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.cpp434
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.h375
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkAllocator.cpp22
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkAllocator.h49
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkArray.h41
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.cpp337
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.h162
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkCommon.h110
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h231
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp815
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.h245
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.cpp613
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.h225
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkGUID.h135
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.cpp592
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.h174
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkHashMap.h34
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkHashSet.h34
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.cpp183
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.h146
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.cpp263
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.h444
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkTypeImpl.h174
23 files changed, 5838 insertions, 0 deletions
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.cpp
new file mode 100644
index 0000000..028e0f1
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.cpp
@@ -0,0 +1,434 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#include "NvBlastPreprocessor.h"
+
+#include "NvBlastTkFrameworkImpl.h"
+#include "NvBlastTkActorImpl.h"
+#include "NvBlastTkGroupImpl.h"
+#include "NvBlastTkAssetImpl.h"
+#include "NvBlastTkFamilyImpl.h"
+#include "NvBlastTkJointImpl.h"
+
+#include "NvBlast.h"
+#include "NvBlastAssert.h"
+#include "NvBlastMemory.h"
+
+#include "Px.h"
+#include "PxFileBuf.h"
+#include "PxAllocatorCallback.h"
+#include "PxTransform.h"
+
+using namespace physx::general_PxIOStream2;
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+TkActorImpl* TkActorImpl::create(const TkActorDesc& desc)
+{
+ const TkAssetImpl* asset = static_cast<const TkAssetImpl*>(desc.asset);
+
+ TkFamilyImpl* family = TkFamilyImpl::create(asset);
+
+ NvBlastFamily* familyLL = family->getFamilyLLInternal();
+ TkArray<char>::type scratch((uint32_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(familyLL, TkFrameworkImpl::get()->log));
+ NvBlastActor* actorLL = NvBlastFamilyCreateFirstActor(familyLL, &desc, scratch.begin(), TkFrameworkImpl::get()->log);
+ if (actorLL == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkActorImpl::create: low-level actor could not be created.");
+ return nullptr;
+ }
+
+ TkActorImpl* actor = family->addActor(actorLL);
+
+ // Add internal joints
+ if (actor != nullptr)
+ {
+ const uint32_t internalJointCount = asset->getJointDescCountInternal();
+ const TkAssetJointDesc* jointDescs = asset->getJointDescsInternal();
+ const NvBlastSupportGraph graph = asset->getGraph();
+ TkJointImpl* joints = family->getInternalJoints();
+ for (uint32_t jointNum = 0; jointNum < internalJointCount; ++jointNum)
+ {
+ const TkAssetJointDesc& assetJointDesc = jointDescs[jointNum];
+ NVBLAST_ASSERT(assetJointDesc.nodeIndices[0] < graph.nodeCount && assetJointDesc.nodeIndices[1] < graph.nodeCount);
+ TkJointDesc jointDesc;
+ jointDesc.families[0] = jointDesc.families[1] = family;
+ jointDesc.chunkIndices[0] = graph.chunkIndices[assetJointDesc.nodeIndices[0]];
+ jointDesc.chunkIndices[1] = graph.chunkIndices[assetJointDesc.nodeIndices[1]];
+ jointDesc.attachPositions[0] = assetJointDesc.attachPositions[0];
+ jointDesc.attachPositions[1] = assetJointDesc.attachPositions[1];
+ TkJointImpl* joint = new (joints + jointNum) TkJointImpl(jointDesc, family);
+ actor->addJoint(joint->m_links[0]);
+ }
+ }
+
+ return actor;
+}
+
+
+//////// Member functions ////////
+
+TkActorImpl::TkActorImpl()
+ : m_actorLL(nullptr)
+ , m_family(nullptr)
+ , m_group(nullptr)
+ , m_groupJobIndex(invalidIndex<uint32_t>())
+ , m_flags(0)
+ , m_jointCount(0)
+{
+#if NV_PROFILE
+ NvBlastTimersReset(&m_timers);
+#endif
+}
+
+
+TkActorImpl::~TkActorImpl()
+{
+}
+
+
+void TkActorImpl::release()
+{
+ // Disassoaciate all joints
+
+ // Copy joint array for safety against implementation of joint->setActor
+ TkJointImpl** joints = reinterpret_cast<TkJointImpl**>(NvBlastAlloca(sizeof(TkJointImpl*)*getJointCountInternal()));
+ TkJointImpl** stop = joints + getJointCountInternal();
+ TkJointImpl** jointHandle = joints;
+ for (JointIt j(*this); (bool)j; ++j)
+ {
+ *jointHandle++ = *j;
+ }
+ jointHandle = joints;
+ while (jointHandle < stop)
+ {
+ NVBLAST_ASSERT(*jointHandle != nullptr);
+ NVBLAST_ASSERT((*jointHandle)->getDataInternal().actors[0] == this || (*jointHandle)->getDataInternal().actors[1] == this);
+ (*jointHandle++)->setActors(nullptr, nullptr);
+ }
+ NVBLAST_ASSERT(getJointCountInternal() == 0);
+
+ if (m_group != nullptr)
+ {
+ m_group->removeActor(*this);
+ }
+
+ if (m_actorLL != nullptr)
+ {
+ NvBlastActorDeactivate(m_actorLL, TkFrameworkImpl::get()->log);
+ }
+
+ if (m_family != nullptr)
+ {
+ m_family->removeActor(this);
+
+ // Make sure we dispatch any remaining events when this family is emptied, since it will no longer be done by any group
+ if (m_family->getActorCountInternal() == 0)
+ {
+ m_family->getQueue().dispatch();
+ }
+ }
+}
+
+
+const NvBlastActor* TkActorImpl::getActorLL() const
+{
+ return m_actorLL;
+}
+
+
+TkFamily& TkActorImpl::getFamily() const
+{
+ return getFamilyImpl();
+}
+
+
+uint32_t TkActorImpl::getIndex() const
+{
+ return getIndexInternal();
+}
+
+
+TkGroup* TkActorImpl::getGroup() const
+{
+ return getGroupImpl();
+}
+
+
+TkGroup* TkActorImpl::removeFromGroup()
+{
+ if (m_group == nullptr)
+ {
+ NVBLASTTK_LOG_WARNING("TkActorImpl::removeFromGroup: actor not in a group.");
+ return nullptr;
+ }
+
+ if (m_group->isProcessing())
+ {
+ NVBLASTTK_LOG_ERROR("TkActorImpl::removeFromGroup: cannot alter Group while processing.");
+ return nullptr;
+ }
+
+ TkGroup* group = m_group;
+
+ return m_group->removeActor(*this) ? group : nullptr;
+}
+
+
+NvBlastFamily* TkActorImpl::getFamilyLL() const
+{
+ return m_family->getFamilyLLInternal();
+}
+
+
+const TkAsset* TkActorImpl::getAsset() const
+{
+ return m_family->getAssetImpl();
+}
+
+
+uint32_t TkActorImpl::getVisibleChunkCount() const
+{
+ return NvBlastActorGetVisibleChunkCount(m_actorLL, TkFrameworkImpl::get()->log);
+}
+
+
+uint32_t TkActorImpl::getVisibleChunkIndices(uint32_t* visibleChunkIndices, uint32_t visibleChunkIndicesSize) const
+{
+ return NvBlastActorGetVisibleChunkIndices(visibleChunkIndices, visibleChunkIndicesSize, m_actorLL, TkFrameworkImpl::get()->log);
+}
+
+
+uint32_t TkActorImpl::getGraphNodeCount() const
+{
+ return NvBlastActorGetGraphNodeCount(m_actorLL, TkFrameworkImpl::get()->log);
+}
+
+
+uint32_t TkActorImpl::getGraphNodeIndices(uint32_t* graphNodeIndices, uint32_t graphNodeIndicesSize) const
+{
+ return NvBlastActorGetGraphNodeIndices(graphNodeIndices, graphNodeIndicesSize, m_actorLL, TkFrameworkImpl::get()->log);
+}
+
+
+const float* TkActorImpl::getBondHealths() const
+{
+ return NvBlastActorGetBondHealths(m_actorLL, TkFrameworkImpl::get()->log);
+}
+
+
+uint32_t TkActorImpl::getSplitMaxActorCount() const
+{
+ return NvBlastActorGetMaxActorCountForSplit(m_actorLL, TkFrameworkImpl::get()->log);
+}
+
+
+bool TkActorImpl::isDamaged() const
+{
+ NVBLAST_ASSERT(!m_flags.isSet(TkActorFlag::DAMAGED) || (m_flags.isSet(TkActorFlag::DAMAGED) && m_flags.isSet(TkActorFlag::PENDING)));
+ return m_flags.isSet(TkActorFlag::DAMAGED);
+}
+
+
+void TkActorImpl::markAsDamaged()
+{
+ m_flags |= TkActorFlag::DAMAGED;
+ makePending();
+}
+
+
+void TkActorImpl::makePending()
+{
+ if (m_group != nullptr && !isPending())
+ {
+ m_group->enqueue(this);
+ }
+
+ m_flags |= TkActorFlag::PENDING;
+}
+
+
+TkActorImpl::operator Nv::Blast::TkActorData() const
+{
+ TkActorData data = { m_family, userData, getIndex() };
+ return data;
+}
+
+
+void TkActorImpl::damage(const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams)
+{
+ PERF_SCOPE_L("TkActor::damage");
+
+ if (m_group == nullptr)
+ {
+ NVBLASTTK_LOG_WARNING("TkActor::damage: actor is not in a group, cannot fracture.");
+ return;
+ }
+
+ if (m_group->isProcessing())
+ {
+ NVBLASTTK_LOG_WARNING("TkActor::damage: group is being processed, cannot fracture this actor.");
+ return;
+ }
+
+ if (NvBlastActorCanFracture(m_actorLL, TkFrameworkImpl::get()->log))
+ {
+ m_damageBuffer.pushBack(DamageData(program, programParams));
+ makePending();
+ }
+}
+
+
+void TkActorImpl::damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize)
+{
+ damage(program, damageDesc, descSize, m_family->getMaterial());
+}
+
+
+void TkActorImpl::damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize, const void* material)
+{
+ PERF_SCOPE_L("TkActor::damage");
+
+ if (m_group == nullptr)
+ {
+ NVBLASTTK_LOG_WARNING("TkActor::damage: actor is not in a group, cannot fracture.");
+ return;
+ }
+
+ if (m_group->isProcessing())
+ {
+ NVBLASTTK_LOG_WARNING("TkActor::damage: group is being processed, cannot fracture this actor.");
+ return;
+ }
+
+ if (NvBlastActorCanFracture(m_actorLL, TkFrameworkImpl::get()->log))
+ {
+ bool appended = false;
+ for (auto& damageData : m_damageBuffer)
+ {
+ if (damageData.tryAppend(program, material, damageDesc, descSize))
+ {
+ appended = true;
+ break;
+ }
+ }
+
+ if (!appended)
+ {
+ m_damageBuffer.pushBack(DamageData(program, material, damageDesc, descSize));
+ }
+
+ makePending();
+ }
+}
+
+
+void TkActorImpl::generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) const
+{
+ PERF_SCOPE_L("TkActor::generateFracture");
+
+ if (m_group && m_group->isProcessing())
+ {
+ NVBLASTTK_LOG_WARNING("TkActor::generateFracture: group is being processed, cannot fracture this actor.");
+ return;
+ }
+
+ // const context, must make m_timers mutable otherwise
+ NvBlastActorGenerateFracture(commands, m_actorLL, program, programParams, TkFrameworkImpl::get()->log, const_cast<NvBlastTimers*>(&m_timers));
+}
+
+
+void TkActorImpl::applyFracture(NvBlastFractureBuffers* eventBuffers, const NvBlastFractureBuffers* commands)
+{
+ PERF_SCOPE_L("TkActor::applyFracture");
+
+ if (m_group && m_group->isProcessing())
+ {
+ NVBLASTTK_LOG_WARNING("TkActor::applyFracture: group is being processed, cannot fracture this actor.");
+ return;
+ }
+
+ NvBlastActorApplyFracture(eventBuffers, m_actorLL, commands, TkFrameworkImpl::get()->log, &m_timers);
+
+ if (commands->chunkFractureCount > 0 || commands->bondFractureCount > 0)
+ {
+ markAsDamaged();
+
+ TkFractureCommands* fevt = getFamilyImpl().getQueue().allocData<TkFractureCommands>();
+ fevt->tkActorData = *this;
+ fevt->buffers = *commands;
+ getFamilyImpl().getQueue().addEvent(fevt);
+ getFamilyImpl().getQueue().dispatch();
+ }
+}
+
+
+uint32_t TkActorImpl::getJointCount() const
+{
+ return getJointCountInternal();
+}
+
+
+uint32_t TkActorImpl::getJoints(TkJoint** joints, uint32_t jointsSize) const
+{
+ uint32_t jointsWritten = 0;
+
+ for (JointIt j(*this); (bool)j && jointsWritten < jointsSize; ++j)
+ {
+ joints[jointsWritten++] = *j;
+ }
+
+ return jointsWritten;
+}
+
+
+//////// TkActorImpl::DamageData methods ////////
+
+static bool operator==(const NvBlastDamageProgram& lhs, const NvBlastDamageProgram& rhs)
+{
+ return lhs.graphShaderFunction == rhs.graphShaderFunction && lhs.subgraphShaderFunction == rhs.subgraphShaderFunction;
+}
+
+
+TkActorImpl::DamageData::DamageData(const NvBlastDamageProgram& program, const NvBlastProgramParams* params)
+ : m_program(program), m_programParams(params), m_damageDescCount(0)
+{
+}
+
+
+TkActorImpl::DamageData::DamageData(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize)
+ : m_program(program), m_material(material), m_damageDescs((char*)desc, (char*)desc + descSize), m_damageDescCount(1)
+{
+}
+
+
+bool TkActorImpl::DamageData::tryAppend(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize)
+{
+ if (getType() == Buffered && m_program == program && m_material == material)
+ {
+ const uint32_t currentDescSize = m_damageDescs.size() / m_damageDescCount;
+ if (descSize == currentDescSize)
+ {
+ const uint32_t s = m_damageDescs.size();
+ m_damageDescs.resizeUninitialized(s + static_cast<uint32_t>(descSize));
+ memcpy(m_damageDescs.begin() + s, desc, descSize);
+ m_damageDescCount++;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.h
new file mode 100644
index 0000000..4d65660
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.h
@@ -0,0 +1,375 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKACTORIMPL_H
+#define NVBLASTTKACTORIMPL_H
+
+
+#include "NvBlastTkCommon.h"
+
+#include "NvBlastAssert.h"
+#include "NvBlastDLink.h"
+#include "NvBlastIteratorBase.h"
+
+#include "NvBlastTkJointImpl.h"
+
+#include "NvBlast.h"
+
+#include "NvBlastTkActor.h"
+
+#include "PxFlags.h"
+
+namespace Nv
+{
+namespace Blast
+{
+
+// Forward declarations:
+class TkGroupImpl;
+class TkFamilyImpl;
+class TkAssetImpl;
+class TkJointImpl;
+
+
+/**
+Struct-enum for actor status flags, used in TkGroup processing.
+*/
+struct TkActorFlag
+{
+ enum Enum
+ {
+ DAMAGED = (1 << 0), //!< The actor had fractures applied successfully and will take the split step.
+ PENDING = (1 << 1), //!< The actor will be processed when its group executes, used to update job queues when moving group.
+ };
+};
+
+
+/**
+Implementation of TkActor.
+*/
+class TkActorImpl : public TkActor
+{
+public:
+ TkActorImpl();
+ ~TkActorImpl();
+
+ // Begin TkActor
+ virtual const NvBlastActor* getActorLL() const override;
+
+ virtual TkFamily& getFamily() const override;
+
+ virtual uint32_t getIndex() const override;
+
+ virtual TkGroup* getGroup() const override;
+
+ virtual TkGroup* removeFromGroup() override;
+
+ virtual const TkAsset* getAsset() const override;
+
+ virtual uint32_t getVisibleChunkCount() const override;
+
+ virtual uint32_t getVisibleChunkIndices(uint32_t* visibleChunkIndices, uint32_t visibleChunkIndicesSize) const override;
+
+ virtual uint32_t getGraphNodeCount() const override;
+
+ virtual uint32_t getGraphNodeIndices(uint32_t* graphNodeIndices, uint32_t graphNodeIndicesSize) const override;
+
+ virtual const float* getBondHealths() const override;
+
+ virtual uint32_t getSplitMaxActorCount() const override;
+
+ virtual void damage(const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) override;
+ virtual void damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize) override;
+ virtual void damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize, const void* material) override;
+
+ virtual bool isPending() const override;
+
+ virtual void generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) const override;
+
+ virtual void applyFracture(NvBlastFractureBuffers* eventBuffers, const NvBlastFractureBuffers* commands) override;
+
+ virtual uint32_t getJointCount() const override;
+
+ virtual uint32_t getJoints(TkJoint** joints, uint32_t jointsSize) const override;
+ // End TkActor
+
+ // Begin TkObject
+ virtual void release() override;
+ // End TkObject
+
+
+ // Public methods
+
+ /**
+ Factory create method.
+
+ \param[in] desc Actor descriptor set by the user.
+
+ \return a pointer to a new TkActorImpl object if successful, NULL otherwise.
+ */
+ static TkActorImpl* create(const TkActorDesc& desc);
+
+ /**
+ TkActorImpl objects are created in an array within a TkFamilyImpl. Actors may become
+ 'inactive' without their memory being freed. If inactive, the actor should be treated as if
+ it has been released.
+
+ \return the active status of this TkActorImpl.
+ */
+ bool isActive() const;
+
+ /**
+ Utility to return the low-level family to which the low-level actor belongs.
+
+ \return a pointer to the NvBlastFamily to which the low-level actor belongs.
+ */
+ NvBlastFamily* getFamilyLL() const;
+
+ /**
+ Utility to access the TkFamily to which this actor belongs.
+
+ \return a reference to the TkFamilyImpl to which this TkActorImpl belongs.
+ */
+ TkFamilyImpl& getFamilyImpl() const;
+
+ /**
+ \return the index of this actor with its TkFamilyImpl.
+ */
+ uint32_t getIndexInternal() const;
+
+ /**
+ Access to the group to which this actor belongs, if any.
+
+ \return a pointer to the TkGroupImpl to which this TkActorImpl belongs, if any. If this actor is not in a group, this function returns NULL.
+ */
+ TkGroupImpl* getGroupImpl() const;
+
+ /**
+ Access to the low-level actor associated with this TkActorImpl.
+
+ \return a pointer to the NvBlastActor associated with this TkActorImpl. If this actor is inactive (see isActive), this function returns NULL.
+ */
+ NvBlastActor* getActorLLInternal() const;
+
+ /**
+ \return the number of TkJointImpl objects that reference this actor.
+ */
+ uint32_t getJointCountInternal() const;
+
+ /**
+ Joint iterator. Usage:
+
+ Given a TkActorImpl a,
+
+ for (TkActorImpl::JointIt i(a); (bool)i; ++i)
+ {
+ TkJointImpl* joint = (TkJointImpl*)i;
+ // ...
+ }
+ */
+ class JointIt : public DList::It
+ {
+ public:
+ /** Constructed from an actor. */
+ JointIt(const TkActorImpl& actor, Direction dir = Forward);
+
+ /** Current joint. */
+ TkJointImpl* operator * () const;
+ };
+
+ /**
+ Implicit converter to TkActorData for events.
+ */
+ operator Nv::Blast::TkActorData() const;
+
+private:
+ /**
+ Used to buffer damage for deferred fracture generation. Unifies 2 different ways to pass and store damage data.
+ */
+ struct DamageData
+ {
+ DamageData(const NvBlastDamageProgram& program, const NvBlastProgramParams* params);
+ DamageData(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize);
+
+ bool tryAppend(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize);
+ void generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastActor* actorLL, NvBlastTimers* timers) const;
+
+ enum Type
+ {
+ Plain,
+ Buffered
+ };
+
+ Type getType() const;
+
+ NvBlastDamageProgram m_program;
+ union
+ {
+ const void* m_material; //!< for Buffered type
+ const NvBlastProgramParams* m_programParams; //!< for Plain type
+ };
+ TkArray<char>::type m_damageDescs;
+ uint32_t m_damageDescCount;
+ };
+
+
+ /**
+ Functions to raise or check 'damaged' state: this actor will take the split step.
+ 'damaged' actors automatically become 'pending' also.
+ */
+ void markAsDamaged();
+ bool isDamaged() const;
+
+ /**
+ Raise actor to 'pending' state: this actor will be processed when its group executes next.
+ Enqueues the actor in its group's job list if a group is set. Otherwise the group will enqueue the actor when it is added.
+ */
+ void makePending();
+
+ /**
+ Functions to add or remove an internal reference to a joint. (Joints and actors mutually reference each other.)
+ */
+ void addJoint(TkJointLink& jointLink);
+ void removeJoint(TkJointLink& jointLink);
+
+
+ // Data
+
+ NvBlastActor* m_actorLL; //!< The low-level actor associated with this actor
+ TkFamilyImpl* m_family; //!< The TkFamilyImpl to which this actor belongs
+ TkGroupImpl* m_group; //!< The TkGroupImpl (if any) to which this actor belongs
+ uint32_t m_groupJobIndex; //!< The index of this actor's job within its group's job list
+ physx::PxFlags<TkActorFlag::Enum, char> m_flags; //!< Status flags for this actor
+ TkArray<DamageData>::type m_damageBuffer; //!< Buffered damage input
+ uint32_t m_jointCount; //!< The number of joints referenced in m_jointList
+ DList m_jointList; //!< A doubly-linked list of joint references
+
+//#if NV_PROFILE
+ NvBlastTimers m_timers; //!< If profiling, each actor stores timing data
+//#endif
+
+ friend class TkWorker; // m_damageBuffer and m_flags
+ friend class TkGroupImpl;
+ friend class TkFamilyImpl;
+ friend class TkJointImpl;
+ friend class TkFrameworkImpl;
+};
+
+
+//////// TkActorImpl inline methods ////////
+
+NV_INLINE TkFamilyImpl& TkActorImpl::getFamilyImpl() const
+{
+ NVBLAST_ASSERT(m_family != nullptr);
+
+ return *m_family;
+}
+
+
+NV_INLINE uint32_t TkActorImpl::getIndexInternal() const
+{
+ NVBLAST_ASSERT(isActive());
+ return NvBlastActorGetIndex(m_actorLL, TkFrameworkImpl::get()->log);
+}
+
+
+NV_INLINE NvBlastActor* TkActorImpl::getActorLLInternal() const
+{
+ return m_actorLL;
+}
+
+
+NV_INLINE uint32_t TkActorImpl::getJointCountInternal() const
+{
+ return m_jointCount;
+}
+
+
+NV_INLINE TkGroupImpl* TkActorImpl::getGroupImpl() const
+{
+ return m_group;
+}
+
+
+NV_INLINE bool TkActorImpl::isActive() const
+{
+ return m_actorLL != nullptr;
+}
+
+
+NV_INLINE bool TkActorImpl::isPending() const
+{
+ return m_flags.isSet(TkActorFlag::PENDING);
+}
+
+
+NV_INLINE void TkActorImpl::addJoint(TkJointLink& jointLink)
+{
+ NVBLAST_ASSERT(m_jointList.isSolitary(jointLink));
+
+ m_jointList.insertHead(jointLink);
+ ++m_jointCount;
+}
+
+
+NV_INLINE void TkActorImpl::removeJoint(TkJointLink& jointLink)
+{
+ NVBLAST_ASSERT(!m_jointList.isSolitary(jointLink));
+ NVBLAST_ASSERT(m_jointCount > 0);
+ if (m_jointCount > 0)
+ {
+ --m_jointCount;
+ m_jointList.remove(jointLink);
+ }
+}
+
+
+//////// TkActorImpl::DamageData inline methods ////////
+
+NV_INLINE TkActorImpl::DamageData::Type TkActorImpl::DamageData::getType() const
+{
+ return m_damageDescCount > 0 ? Buffered : Plain;
+}
+
+
+NV_INLINE void TkActorImpl::DamageData::generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastActor* actorLL, NvBlastTimers* timers) const
+{
+ if (getType() == Plain)
+ {
+ NvBlastActorGenerateFracture(commandBuffers, actorLL, m_program, m_programParams, TkFrameworkImpl::get()->log, timers);
+ }
+ else
+ {
+ const NvBlastProgramParams programParams = {
+ m_damageDescs.begin(),
+ m_damageDescCount,
+ m_material,
+ };
+ NvBlastActorGenerateFracture(commandBuffers, actorLL, m_program, &programParams, TkFrameworkImpl::get()->log, timers);
+ }
+}
+
+
+//////// TkActorImpl::JointIt methods ////////
+
+NV_INLINE TkActorImpl::JointIt::JointIt(const TkActorImpl& actor, Direction dir) : DList::It(actor.m_jointList, dir) {}
+
+
+NV_INLINE TkJointImpl* TkActorImpl::JointIt::operator * () const
+{
+ const DLink* link = (const DLink*)(*this);
+ return reinterpret_cast<const TkJointLink*>(link)->m_joint;
+}
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTTKACTORIMPL_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.cpp
new file mode 100644
index 0000000..b1c2c65
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.cpp
@@ -0,0 +1,22 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#include "NvBlastTkAllocator.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+physx::PxAllocatorCallback* TkAllocator::s_allocatorCallback = nullptr;
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.h b/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.h
new file mode 100644
index 0000000..abc7b16
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.h
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKALLOCATOR_H
+#define NVBLASTTKALLOCATOR_H
+
+#include "PxAllocatorCallback.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+/**
+An allocator which can be used in PxShared containers.
+*/
+class TkAllocator
+{
+public:
+ TkAllocator(const char* = 0)
+ {
+ }
+
+ void* allocate(size_t size, const char* file, int line)
+ {
+ return s_allocatorCallback->allocate(size, nullptr, file, line);
+ }
+
+ void deallocate(void* ptr)
+ {
+ return s_allocatorCallback->deallocate(ptr);
+ }
+
+ static physx::PxAllocatorCallback* s_allocatorCallback;
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // #ifndef NVBLASTTKALLOCATOR_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkArray.h b/NvBlast/sdk/toolkit/source/NvBlastTkArray.h
new file mode 100644
index 0000000..c07dc11
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkArray.h
@@ -0,0 +1,41 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKARRAY_H
+#define NVBLASTTKARRAY_H
+
+
+#include "NvBlastTkAllocator.h"
+#include "PsInlineArray.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+template <class T>
+struct TkArray
+{
+ typedef physx::shdfnd::Array<T, TkAllocator> type;
+};
+
+
+template <class T, uint32_t N>
+struct TkInlineArray
+{
+ typedef physx::shdfnd::InlineArray<T, N, TkAllocator> type;
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // #ifndef NVBLASTTKARRAY_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.cpp
new file mode 100644
index 0000000..577d46b
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.cpp
@@ -0,0 +1,337 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+
+#include "NvBlastTkFrameworkImpl.h"
+#include "NvBlastTkAssetImpl.h"
+#include "NvBlastTkFamilyImpl.h"
+
+#include "NvBlast.h"
+#include "NvBlastMemory.h"
+
+#include "Px.h"
+#include "PxFileBuf.h"
+#include "PxAllocatorCallback.h"
+
+
+using namespace physx::general_PxIOStream2;
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+//////// Static data ////////
+
+NVBLASTTK_DEFINE_TYPE_SERIALIZABLE(Asset);
+
+
+//////// Member functions ////////
+
+TkAssetImpl::TkAssetImpl()
+ : m_assetLL(nullptr), m_ownsAsset(false)
+{
+}
+
+
+TkAssetImpl::TkAssetImpl(const NvBlastID& id)
+ : TkAssetType(id), m_assetLL(nullptr), m_ownsAsset(false)
+{
+}
+
+
+TkAssetImpl::~TkAssetImpl()
+{
+ if (m_assetLL != nullptr && m_ownsAsset)
+ {
+ TkFrameworkImpl::get()->free(m_assetLL);
+ }
+}
+
+
+const NvBlastAsset* TkAssetImpl::getAssetLL() const
+{
+ return getAssetLLInternal();
+}
+
+
+uint32_t TkAssetImpl::getChunkCount() const
+{
+ return NvBlastAssetGetChunkCount(m_assetLL, TkFrameworkImpl::get()->log);
+}
+
+
+uint32_t TkAssetImpl::getLeafChunkCount() const
+{
+ return NvBlastAssetGetLeafChunkCount(m_assetLL, TkFrameworkImpl::get()->log);
+}
+
+
+uint32_t TkAssetImpl::getBondCount() const
+{
+ return NvBlastAssetGetBondCount(m_assetLL, TkFrameworkImpl::get()->log);
+}
+
+
+const NvBlastChunk* TkAssetImpl::getChunks() const
+{
+ return NvBlastAssetGetChunks(m_assetLL, TkFrameworkImpl::get()->log);
+}
+
+
+const NvBlastBond* TkAssetImpl::getBonds() const
+{
+ return NvBlastAssetGetBonds(m_assetLL, TkFrameworkImpl::get()->log);
+}
+
+
+const NvBlastSupportGraph TkAssetImpl::getGraph() const
+{
+ return NvBlastAssetGetSupportGraph(m_assetLL, TkFrameworkImpl::get()->log);
+}
+
+
+uint32_t TkAssetImpl::getDataSize() const
+{
+ return NvBlastAssetGetSize(m_assetLL, TkFrameworkImpl::get()->log);
+}
+
+
+uint32_t TkAssetImpl::getJointDescCount() const
+{
+ return getJointDescCountInternal();
+}
+
+
+const TkAssetJointDesc* TkAssetImpl::getJointDescs() const
+{
+ return getJointDescsInternal();
+}
+
+
+void TkAssetImpl::release()
+{
+ const TkType& tkType = TkFamilyImpl::s_type;
+ const uint32_t num = TkFrameworkImpl::get()->getObjectCount(tkType);
+
+ if (num)
+ {
+ TkArray<TkIdentifiable*>::type dependents(num);
+ TkFrameworkImpl::get()->getObjects(dependents.begin(), dependents.size(), tkType);
+
+ for (TkObject* o : dependents)
+ {
+ TkFamilyImpl* f = static_cast<TkFamilyImpl*>(o);
+ if (f->getAssetImpl() == this)
+ {
+ f->release();
+ }
+ }
+ }
+
+ NVBLASTTK_DELETE(this, TkAssetImpl);
+}
+
+
+bool TkAssetImpl::serialize(PxFileBuf& stream) const
+{
+ TkFrameworkImpl::get()->serializeHeader(*this, stream);
+
+ // Asset data
+ const uint32_t assetSize = NvBlastAssetGetSize(m_assetLL, TkFrameworkImpl::get()->log);
+ stream.storeDword(assetSize);
+ stream.write(m_assetLL, assetSize);
+
+ // Joint descs
+ stream.storeDword((uint32_t)m_jointDescs.size());
+ for (uint32_t i = 0; i < m_jointDescs.size(); ++i)
+ {
+ const TkAssetJointDesc& jointDesc = m_jointDescs[i];
+ stream.storeDword(jointDesc.nodeIndices[0]);
+ stream.storeDword(jointDesc.nodeIndices[1]);
+ stream.storeFloat(jointDesc.attachPositions[0].x);
+ stream.storeFloat(jointDesc.attachPositions[0].y);
+ stream.storeFloat(jointDesc.attachPositions[0].z);
+ stream.storeFloat(jointDesc.attachPositions[1].x);
+ stream.storeFloat(jointDesc.attachPositions[1].y);
+ stream.storeFloat(jointDesc.attachPositions[1].z);
+ }
+
+ return true;
+}
+
+
+//////// Static functions ////////
+
+TkSerializable* TkAssetImpl::deserialize(PxFileBuf& stream, const NvBlastID& id)
+{
+ // Allocate
+ TkAssetImpl* asset = NVBLASTTK_NEW(TkAssetImpl)(id);
+ if (asset == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkAssetImpl::deserialize: asset allocation failed.");
+ return nullptr;
+ }
+
+ // Asset data
+ const uint32_t assetSize = stream.readDword();
+ asset->m_assetLL = static_cast<NvBlastAsset*>(TkFrameworkImpl::get()->alloc(assetSize));
+ asset->m_ownsAsset = true;
+ stream.read(asset->m_assetLL, assetSize);
+
+ // Joint descs
+ const uint32_t jointDescCount = stream.readDword();
+ asset->m_jointDescs.resize(jointDescCount);
+ for (uint32_t i = 0; i < asset->m_jointDescs.size(); ++i)
+ {
+ TkAssetJointDesc& jointDesc = asset->m_jointDescs[i];
+ jointDesc.nodeIndices[0] = stream.readDword();
+ jointDesc.nodeIndices[1] = stream.readDword();
+ jointDesc.attachPositions[0].x = stream.readFloat();
+ jointDesc.attachPositions[0].y = stream.readFloat();
+ jointDesc.attachPositions[0].z = stream.readFloat();
+ jointDesc.attachPositions[1].x = stream.readFloat();
+ jointDesc.attachPositions[1].y = stream.readFloat();
+ jointDesc.attachPositions[1].z = stream.readFloat();
+ }
+
+
+ if (asset->m_assetLL == nullptr)
+ {
+ asset->release();
+ asset = nullptr;
+ }
+
+ return asset;
+}
+
+
+TkAssetImpl* TkAssetImpl::create(const TkAssetDesc& desc)
+{
+ TkAssetImpl* asset = NVBLASTTK_NEW(TkAssetImpl);
+
+ TkArray<char>::type scratch((uint32_t)NvBlastGetRequiredScratchForCreateAsset(&desc, TkFrameworkImpl::get()->log));
+ void* mem = TkFrameworkImpl::get()->alloc(NvBlastGetAssetMemorySize(&desc, TkFrameworkImpl::get()->log));
+ asset->m_assetLL = NvBlastCreateAsset(mem, &desc, scratch.begin(), TkFrameworkImpl::get()->log);
+ if (asset->m_assetLL == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkAssetImpl::create: low-level asset could not be created.");
+ asset->release();
+ return nullptr;
+ }
+
+ if (desc.bondFlags != nullptr)
+ {
+ for (uint32_t bondN = 0; bondN < desc.bondCount; ++bondN)
+ {
+ if (0 != (desc.bondFlags[bondN] & TkAssetDesc::BondJointed))
+ {
+ const NvBlastBondDesc& bondDesc = desc.bondDescs[bondN];
+ const uint32_t c0 = bondDesc.chunkIndices[0];
+ const uint32_t c1 = bondDesc.chunkIndices[1];
+ if (c0 >= desc.chunkCount || c1 >= desc.chunkCount)
+ {
+ NVBLASTTK_LOG_WARNING("TkAssetImpl::create: joint flag set for badly described bond. No joint descriptor created.");
+ continue;
+ }
+
+ if (!asset->addJointDesc(c0, c1))
+ {
+ NVBLASTTK_LOG_WARNING("TkAssetImpl::create: no bond corresponds to the user-described bond indices. No joint descriptor created.");
+ }
+ }
+ }
+ }
+
+ asset->m_ownsAsset = true;
+// asset->setID(NvBlastAssetGetID(asset->m_assetLL, TkFrameworkImpl::get()->log)); // Keeping LL and Tk IDs distinct
+
+ return asset;
+}
+
+
+TkAssetImpl* TkAssetImpl::create(const NvBlastAsset* assetLL, Nv::Blast::TkAssetJointDesc* jointDescs, uint32_t jointDescCount, bool ownsAsset)
+{
+ TkAssetImpl* asset = NVBLASTTK_NEW(TkAssetImpl);
+
+ //NOTE: Why are we passing in a const NvBlastAsset* and then discarding the const?
+ asset->m_assetLL = const_cast<NvBlastAsset*>(assetLL);
+ if (asset->m_assetLL == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkAssetImpl::create: low-level asset could not be created.");
+ asset->release();
+ return nullptr;
+ }
+
+ asset->m_ownsAsset = ownsAsset;
+ asset->setID(NvBlastAssetGetID(asset->m_assetLL, TkFrameworkImpl::get()->log));
+
+ asset->m_jointDescs.resize(jointDescCount);
+ for (uint32_t i = 0; i < asset->m_jointDescs.size(); ++i)
+ {
+ asset->m_jointDescs[i] = jointDescs[i];
+ }
+
+ return asset;
+}
+
+bool TkAssetImpl::addJointDesc(uint32_t chunkIndex0, uint32_t chunkIndex1)
+{
+ if (m_assetLL == nullptr)
+ {
+ return false;
+ }
+
+ const uint32_t upperSupportChunkCount = NvBlastAssetGetFirstSubsupportChunkIndex(m_assetLL, TkFrameworkImpl::get()->log);
+ if (chunkIndex0 >= upperSupportChunkCount || chunkIndex1 >= upperSupportChunkCount)
+ {
+ return false;
+ }
+
+ const uint32_t* chunkToGraphNodeMap = NvBlastAssetGetChunkToGraphNodeMap(m_assetLL, TkFrameworkImpl::get()->log);
+ const uint32_t node0 = chunkToGraphNodeMap[chunkIndex0];
+ const uint32_t node1 = chunkToGraphNodeMap[chunkIndex1];
+ const NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(m_assetLL, TkFrameworkImpl::get()->log);
+ if (node0 >= graph.nodeCount && node1 >= graph.nodeCount)
+ {
+ return false;
+ }
+
+ // Find bond index
+ // Iterate through all neighbors of node0 chunk
+ uint32_t bondIndex = 0xFFFFFFFF;
+ for (uint32_t i = graph.adjacencyPartition[node0]; i < graph.adjacencyPartition[node0 + 1]; i++)
+ {
+ if (graph.adjacentNodeIndices[i] == node1)
+ {
+ bondIndex = graph.adjacentBondIndices[i];
+ break;
+ }
+ }
+
+ if (bondIndex >= NvBlastAssetGetBondCount(m_assetLL, TkFrameworkImpl::get()->log))
+ {
+ return false;
+ }
+
+ const NvBlastBond& bond = NvBlastAssetGetBonds(m_assetLL, TkFrameworkImpl::get()->log)[bondIndex];
+
+ TkAssetJointDesc jointDesc;
+ jointDesc.attachPositions[0] = jointDesc.attachPositions[1] = physx::PxVec3(bond.centroid[0], bond.centroid[1], bond.centroid[2]);
+ jointDesc.nodeIndices[0] = node0;
+ jointDesc.nodeIndices[1] = node1;
+ m_jointDescs.pushBack(jointDesc);
+
+ return true;
+}
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.h
new file mode 100644
index 0000000..ae68af8
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.h
@@ -0,0 +1,162 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKASSETIMPL_H
+#define NVBLASTTKASSETIMPL_H
+
+
+#include "NvBlastTkCommon.h"
+#include "NvBlastTkJoint.h"
+#include "NvBlastTkAsset.h"
+#include "NvBlastTkTypeImpl.h"
+#include "NvBlastTkArray.h"
+
+
+// Forward declarations
+struct NvBlastAsset;
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+/**
+Implementation of TkAsset
+*/
+NVBLASTTK_IMPL_DECLARE(Asset)
+{
+public:
+ /**
+ Enum which keeps track of the serialized data format.
+ */
+ enum Version
+ {
+ /** Initial version */
+ Initial,
+
+ // New formats must come before Count. They should be given descriptive names with more information in comments.
+
+ /** The number of serialized formats. */
+ Count,
+
+ /** The current version. This should always be Count-1 */
+ Current = Count - 1
+ };
+
+ TkAssetImpl();
+ TkAssetImpl(const NvBlastID& id);
+ ~TkAssetImpl();
+
+ NVBLASTTK_IMPL_DEFINE_SERIALIZABLE('A', 'S', 'S', 'T');
+
+ // Public methods
+
+ /**
+ Factory create method. This method creates a low-level asset and stores a reference to it.
+
+ \param[in] desc Asset descriptor set by the user.
+
+ \return a pointer to a new TkAssetImpl object if successful, NULL otherwise.
+ */
+ static TkAssetImpl* create(const TkAssetDesc& desc);
+
+ /**
+ Static method to create an asset from an existing low-level asset.
+
+ \param[in] assetLL A valid low-level asset passed in by the user.
+ \param[in] jointDescs Optional joint descriptors to add to the new asset.
+ \param[in] jointDescCount The number of joint descriptors in the jointDescs array. If non-zero, jointDescs cannot be NULL.
+ \param[in] ownsAsset Whether or not to let this TkAssetImpl object release the low-level NvBlastAsset memory upon its own release.
+
+ \return a pointer to a new TkAssetImpl object if successful, NULL otherwise.
+ */
+ static TkAssetImpl* create(const NvBlastAsset* assetLL, Nv::Blast::TkAssetJointDesc* jointDescs = nullptr, uint32_t jointDescCount = 0, bool ownsAsset = false);
+
+ /**
+ \return a pointer to the underlying low-level NvBlastAsset associated with this asset.
+ */
+ const NvBlastAsset* getAssetLLInternal() const;
+
+ /**
+ \return the number of internal joint descriptors stored with this asset.
+ */
+ uint32_t getJointDescCountInternal() const;
+
+ /**
+ \return the array of internal joint descriptors stored with this asset, with size given by getJointDescCountInternal().
+ */
+ const TkAssetJointDesc* getJointDescsInternal() const;
+
+ // Begin TkAsset
+ virtual const NvBlastAsset* getAssetLL() const override;
+
+ virtual uint32_t getChunkCount() const override;
+
+ virtual uint32_t getLeafChunkCount() const override;
+
+ virtual uint32_t getBondCount() const override;
+
+ virtual const NvBlastChunk* getChunks() const override;
+
+ virtual const NvBlastBond* getBonds() const override;
+
+ virtual const NvBlastSupportGraph getGraph() const override;
+
+ virtual uint32_t getDataSize() const override;
+
+ virtual uint32_t getJointDescCount() const override;
+
+ virtual const TkAssetJointDesc* getJointDescs() const override;
+ // End TkAsset
+
+private:
+ /**
+ Utility to add a joint descriptor between the indexed chunks. The two chunks
+ must be support chunks, and there must exist a bond between them. The joint's
+ attachment positions will be the bond centroid.
+
+ \param[in] chunkIndex0 The first chunk index.
+ \param[in] chunkIndex1 The second chunk index.
+
+ \return true iff successful.
+ */
+ bool addJointDesc(uint32_t chunkIndex0, uint32_t chunkIndex1);
+
+ NvBlastAsset* m_assetLL; //!< The underlying low-level asset.
+ TkArray<TkAssetJointDesc>::type m_jointDescs; //!< The array of internal joint descriptors.
+ bool m_ownsAsset; //!< Whether or not this asset should release its low-level asset upon its own release.
+};
+
+
+//////// TkAssetImpl inline methods ////////
+
+NV_INLINE const NvBlastAsset* TkAssetImpl::getAssetLLInternal() const
+{
+ return m_assetLL;
+}
+
+
+NV_INLINE uint32_t TkAssetImpl::getJointDescCountInternal() const
+{
+ return m_jointDescs.size();
+}
+
+
+NV_INLINE const TkAssetJointDesc* TkAssetImpl::getJointDescsInternal() const
+{
+ return m_jointDescs.begin();
+}
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTTKASSETIMPL_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkCommon.h b/NvBlast/sdk/toolkit/source/NvBlastTkCommon.h
new file mode 100644
index 0000000..edc1a91
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkCommon.h
@@ -0,0 +1,110 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKCOMMON_H
+#define NVBLASTTKCOMMON_H
+
+
+#include "NvPreprocessor.h"
+#include "NvBlastTkGUID.h"
+
+
+// Macro to load a uint32_t (or larger) with four characters
+#define NVBLASTTK_FOURCC(_a, _b, _c, _d) ( (uint32_t)(_a) | (uint32_t)(_b)<<8 | (uint32_t)(_c)<<16 | (uint32_t)(_d)<<24 )
+
+
+// Macro to define standard object classes. An intermediate class is defined which holds common implementations.
+#define NVBLASTTK_IMPL_DECLARE(_name) \
+class Tk##_name##Type : public Tk##_name \
+{ \
+public: \
+ /* Blank constructor generates a new NvBlastID and informs framework */ \
+ Tk##_name##Type() \
+ { \
+ memset(&m_ID, 0, sizeof(NvBlastID)); \
+ setID(TkGenerateGUID(this)); \
+ TkFrameworkImpl::get()->onCreate(*this); \
+ } \
+ \
+ /* This constructor takes an existing NvBlastID and informs framework */ \
+ Tk##_name##Type(const NvBlastID& id) \
+ { \
+ memset(&m_ID, 0, sizeof(NvBlastID)); \
+ setID(id); \
+ TkFrameworkImpl::get()->onCreate(*this); \
+ } \
+ \
+ /* Destructor informs framework */ \
+ ~Tk##_name##Type() { TkFrameworkImpl::get()->onDestroy(*this); } \
+ \
+ /* Begin TkIdentifiable */ \
+ virtual void setID(const NvBlastID& id) override \
+ { \
+ /* Inform framework of ID change */ \
+ TkFrameworkImpl::get()->onIDChange(*this, m_ID, id); \
+ m_ID = id; \
+ } \
+ virtual const NvBlastID& getID() const override { return getIDInternal(); } \
+ virtual const TkType& getType() const override { return s_type; } \
+ /* End TkIdentifiable */ \
+ \
+ /* Begin public API */ \
+ \
+ /* Inline method for internal access to NvBlastID */ \
+ const NvBlastID& getIDInternal() const { return m_ID; } \
+ \
+ /* End public API */ \
+ \
+ /* Static type information */ \
+ static TkTypeImpl s_type; \
+ \
+private: \
+ NvBlastID m_ID; /* NvBlastID for a TkIdentifiable object */ \
+}; \
+ \
+/* Derive object implementation from common implementation class above */ \
+class Tk##_name##Impl final : public Tk##_name##Type
+
+
+// Macro to declare standard object interfaces, enums, etc.
+#define NVBLASTTK_IMPL_DEFINE_IDENTIFIABLE(_id0, _id1, _id2, _id3) \
+ /* Begin TkObject */ \
+ virtual void release() override; \
+ /* End TkObject */ \
+ \
+ /* Enums */ \
+ \
+ /* Generate a ClassID enum used to identify this TkIdentifiable. */ \
+ enum { ClassID = NVBLASTTK_FOURCC(_id0, _id1, _id2, _id3) }
+
+
+// Macro to declare standard object interfaces, enums, etc (serializable version)
+#define NVBLASTTK_IMPL_DEFINE_SERIALIZABLE(_id0, _id1, _id2, _id3) \
+ NVBLASTTK_IMPL_DEFINE_IDENTIFIABLE(_id0, _id1, _id2, _id3); \
+ \
+ /* Begin TkSerializable */ \
+ virtual bool serialize(physx::general_PxIOStream2::PxFileBuf& stream) const override; \
+ /* End TkSerializable */ \
+ \
+ /* Static deserialization function, called by TkFrameworkImpl::deserialize after header data */ \
+ static TkSerializable* deserialize(physx::general_PxIOStream2::PxFileBuf& stream, const NvBlastID& id)
+
+
+// Macro to define class type data
+#define NVBLASTTK_DEFINE_TYPE_IDENTIFIABLE(_name) \
+ TkTypeImpl Tk##_name##Type::s_type("Tk" #_name, Tk##_name##Impl::ClassID, 0, nullptr)
+
+
+// Macro to define class type data (serializable version)
+#define NVBLASTTK_DEFINE_TYPE_SERIALIZABLE(_name) \
+ TkTypeImpl Tk##_name##Type::s_type("Tk" #_name, Tk##_name##Impl::ClassID, Tk##_name##Impl::Version::Current, Tk##_name##Impl::deserialize)
+
+
+#endif // ifndef NVBLASTTKCOMMON_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h b/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h
new file mode 100644
index 0000000..00a1a61
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h
@@ -0,0 +1,231 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKEVENTQUEUE_H
+#define NVBLASTTKEVENTQUEUE_H
+
+#include <algorithm>
+#include <vector>
+
+#include <mutex>
+#include <atomic>
+
+#include "PxAllocatorCallback.h"
+#include "NvBlastTkFrameworkImpl.h"
+#include "NvBlastAssert.h"
+
+
+namespace Nv {
+namespace Blast {
+
+/**
+A dispatcher queue providing preallocation and thread-safe insertions therein.
+
+Typical usage:
+- preallocate space for events and payload:
+ - reserveEvents, reserveData
+- enable asserts to detect undersized storage (allocations are not thread safe):
+ - protect(true)
+- get pointers to payload data and events to fill in, thread safe for preallocated memory:
+ - allocData, addEvent
+- back on main thread, ensure consistency:
+ - protect(false)
+
+- continue adding events and payload on main thread if necessary like above (allocations are safe here)
+eventually dispatch, or reset if dispatched by proxy
+*/
+class TkEventQueue
+{
+public:
+ TkEventQueue() : m_currentEvent(0), m_poolCapacity(0), m_pool(nullptr), m_allowAllocs(true) {}
+
+ /**
+ Peek events queue for dispatch.
+ Do not use in protected state.
+ */
+ operator const TkArray<TkEvent>::type&()
+ {
+ NVBLAST_ASSERT(m_allowAllocs);
+ NVBLAST_ASSERT(m_currentEvent == m_events.size());
+ return m_events;
+ }
+
+ /**
+ Debug help to catch (unwanted) allocations during task work.
+ Note that this will not actually avoid allocations, but assert in debug builds.
+
+ Set true before using in distributed environment.
+ Set false to return to single-thread mode.
+ */
+ void protect(bool enable)
+ {
+ // During parallel use, m_events.size() and m_currentEvent are allowed to diverge.
+ // This is fine because resizeUninitialized does not alter the stored data.
+ NVBLAST_ASSERT(m_currentEvent <= m_events.capacity());
+ m_events.resizeUninitialized(m_currentEvent);
+ m_allowAllocs = !enable;
+ }
+
+ /**
+ Restores initial state.
+ Data memory is currently not being reused. To be improved.
+ */
+ void reset()
+ {
+ m_events.clear();
+ m_currentEvent = 0;
+ for (void* mem : m_memory)
+ {
+ NVBLASTTK_FREE(mem);
+ }
+ m_memory.clear();
+ m_currentData = 0;
+ m_allowAllocs = true;
+ m_poolCapacity = 0;
+ m_pool = nullptr;
+ }
+
+ /**
+ Queue an event with a payload.
+ */
+ template<class T>
+ void addEvent(T* payload)
+ {
+ uint32_t index = m_currentEvent.fetch_add(1);
+
+ // Should not allocate in protected state.
+ NVBLAST_ASSERT(m_allowAllocs || m_currentEvent <= m_events.capacity());
+
+ m_events.resizeUninitialized(m_currentEvent);
+
+ // During parallel use, m_events.size() and m_currentEvent are allowed to diverge.
+ // Consistency is restored in protect().
+ NVBLAST_ASSERT(!m_allowAllocs || m_currentEvent == m_events.size());
+
+ TkEvent& evt = m_events[index];
+ evt.type = TkEvent::Type(T::EVENT_TYPE);
+ evt.payload = payload;
+ }
+
+ /**
+ Request storage for payload.
+ */
+ template<typename T>
+ T* allocData()
+ {
+ uint32_t index = m_currentData.fetch_add(sizeof(T));
+ if (m_currentData <= m_poolCapacity)
+ {
+ return reinterpret_cast<T*>(&m_pool[index]);
+ }
+ else
+ {
+ // Could do larger block allocation here.
+ reserveData(sizeof(T));
+ // Account for the requested size.
+ m_currentData = sizeof(T);
+ return reinterpret_cast<T*>(&m_pool[0]);
+ }
+ }
+
+ /**
+ Preallocate a memory block of size Bytes for payload data.
+ Note that this will inevitably allocate a new memory block.
+ Subsequent calls to allocData will use this memory piecewise.
+ */
+ void reserveData(size_t size)
+ {
+ NVBLAST_ASSERT(m_allowAllocs);
+ m_pool = reinterpret_cast<uint8_t*>(allocDataBySize(size));
+ m_poolCapacity = size;
+ m_currentData = 0;
+ }
+
+ /**
+ Preallocate space for events.
+ */
+ void reserveEvents(uint32_t n)
+ {
+ NVBLAST_ASSERT(m_allowAllocs);
+ m_events.reserve(m_events.size() + n);
+ }
+
+ /**
+ Add a listener to dispatch to.
+ */
+ void addListener(TkEventListener& l)
+ {
+ m_listeners.pushBack(&l);
+ }
+
+ /**
+ Remove a listener from dispatch list.
+ */
+ void removeListener(TkEventListener& l)
+ {
+ m_listeners.findAndReplaceWithLast(&l);
+ }
+
+ /**
+ Dispatch the stored events to the registered listeners.
+ After dispatch, all data is invalidated.
+ */
+ void dispatch()
+ {
+ dispatch(*this);
+ reset();
+ }
+
+ /**
+ Proxy function to dispatch events to this queue's listeners.
+ */
+ void dispatch(const TkArray<TkEvent>::type& events) const
+ {
+ if (events.size())
+ {
+ for (TkEventListener* l : m_listeners)
+ {
+ PERF_SCOPE_M("TkEventQueue::dispatch");
+ l->receive(events.begin(), events.size());
+ }
+ }
+ }
+
+private:
+ /**
+ Allocates and stores a block of size Bytes of payload data.
+ */
+ void* allocDataBySize(size_t size)
+ {
+ void* memory = nullptr;
+ if (size > 0)
+ {
+ memory = NVBLASTTK_ALLOC(size, "TkEventQueue Data");
+ m_memory.pushBack(memory);
+ }
+ return memory;
+ }
+
+
+ TkArray<TkEvent>::type m_events; //!< holds events
+ TkArray<void*>::type m_memory; //!< holds allocated data memory blocks
+ std::atomic<uint32_t> m_currentEvent; //!< reference index for event insertion
+ std::atomic<uint32_t> m_currentData; //!< reference index for data insertion
+ size_t m_poolCapacity; //!< size of the currently active memory block (m_pool)
+ uint8_t* m_pool; //!< the current memory block allocData() uses
+ bool m_allowAllocs; //!< assert guard
+ TkInlineArray<TkEventListener*,4>::type m_listeners; //!< objects to dispatch to
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTTKEVENTQUEUE_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp
new file mode 100644
index 0000000..33baafe
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp
@@ -0,0 +1,815 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#include "NvBlastTkFrameworkImpl.h"
+#include "NvBlastTkFamilyImpl.h"
+#include "NvBlastTkGroupImpl.h"
+#include "NvBlastTkAssetImpl.h"
+#include "NvBlastTkActorImpl.h"
+#include "NvBlastTkJointImpl.h"
+
+#include "Px.h"
+#include "PxFileBuf.h"
+#include "PxAllocatorCallback.h"
+
+#include "NvBlastIndexFns.h"
+#include "NvBlastMemory.h"
+
+using namespace physx::general_PxIOStream2;
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+//////// Static data ////////
+
+NVBLASTTK_DEFINE_TYPE_SERIALIZABLE(Family);
+
+
+//////// Member functions ////////
+
+TkFamilyImpl::TkFamilyImpl() : m_familyLL(nullptr), m_internalJointCount(0), m_asset(nullptr), m_material(nullptr)
+{
+}
+
+
+TkFamilyImpl::TkFamilyImpl(const NvBlastID& id) : TkFamilyType(id), m_familyLL(nullptr), m_internalJointCount(0), m_asset(nullptr), m_material(nullptr)
+{
+}
+
+
+TkFamilyImpl::~TkFamilyImpl()
+{
+ if (m_familyLL != nullptr)
+ {
+ uint32_t familyActorCount = NvBlastFamilyGetActorCount(m_familyLL, TkFrameworkImpl::get()->log);
+ if (familyActorCount != 0)
+ {
+ NVBLASTTK_LOG_WARNING("TkFamilyImpl::~TkFamilyImpl(): family actor count is not 0.");
+ }
+ TkFrameworkImpl::get()->free(m_familyLL);
+ }
+}
+
+
+void TkFamilyImpl::release()
+{
+ for (TkActorImpl& actor : m_actors)
+ {
+ if (actor.isActive())
+ {
+ actor.release();
+ }
+ }
+
+ m_actors.clear();
+
+ NVBLASTTK_DELETE(this, TkFamilyImpl);
+}
+
+
+const NvBlastFamily* TkFamilyImpl::getFamilyLL() const
+{
+ return m_familyLL;
+}
+
+
+TkActorImpl* TkFamilyImpl::addActor(NvBlastActor* actorLL)
+{
+ TkActorImpl* actor = getActorByActorLL(actorLL);
+ NVBLAST_ASSERT(actor);
+ actor->m_actorLL = actorLL;
+ actor->m_family = this;
+ return actor;
+}
+
+
+void TkFamilyImpl::removeActor(TkActorImpl* actor)
+{
+ NVBLAST_ASSERT(actor != nullptr && actor->m_family == this);
+ //actor->m_family = nullptr;
+ actor->m_actorLL = nullptr;
+}
+
+
+uint32_t TkFamilyImpl::getActorCount() const
+{
+ return getActorCountInternal();
+}
+
+
+uint32_t TkFamilyImpl::getActors(TkActor** buffer, uint32_t bufferSize, uint32_t indexStart /*= 0*/) const
+{
+ uint32_t actorCount = getActorCount();
+ if (actorCount <= indexStart)
+ {
+ NVBLASTTK_LOG_WARNING("TkFamilyImpl::getActors: indexStart beyond end of actor list.");
+ return 0;
+ }
+
+ actorCount -= indexStart;
+ if (actorCount > bufferSize)
+ {
+ actorCount = static_cast<uint32_t>(bufferSize);
+ }
+
+ uint32_t index = 0;
+ for (const TkActorImpl& actor : m_actors)
+ {
+ if (actor.isActive())
+ {
+ if (index >= indexStart)
+ {
+ if ((index - indexStart) >= actorCount)
+ {
+ break;
+ }
+ else
+ {
+ *buffer++ = const_cast<TkActorImpl*>(&actor);
+ }
+ }
+ index++;
+ }
+ }
+
+ return actorCount;
+}
+
+
+NV_INLINE bool areLLActorsEqual(const NvBlastActor* actor0, const NvBlastActor* actor1, TkArray<uint32_t>::type& scratch)
+{
+ if (NvBlastActorGetGraphNodeCount(actor0, TkFrameworkImpl::get()->log) != NvBlastActorGetGraphNodeCount(actor1, TkFrameworkImpl::get()->log))
+ {
+ return false;
+ }
+
+ const uint32_t chunkCount = NvBlastActorGetVisibleChunkCount(actor0, TkFrameworkImpl::get()->log);
+ if (chunkCount != NvBlastActorGetVisibleChunkCount(actor1, TkFrameworkImpl::get()->log))
+ {
+ return false;
+ }
+
+ scratch.resize(chunkCount * 2);
+ NvBlastActorGetVisibleChunkIndices(scratch.begin(), chunkCount, actor0, TkFrameworkImpl::get()->log);
+ NvBlastActorGetVisibleChunkIndices(scratch.begin() + chunkCount, chunkCount, actor1, TkFrameworkImpl::get()->log);
+ return memcmp(scratch.begin(), scratch.begin() + chunkCount, chunkCount * sizeof(uint32_t)) == 0;
+}
+
+
+void TkFamilyImpl::reinitialize(const NvBlastFamily* newFamily, TkGroup* group)
+{
+ NVBLAST_ASSERT(newFamily);
+#if NV_ENABLE_ASSERTS
+ NvBlastID id0 = NvBlastFamilyGetAssetID(m_familyLL, TkFrameworkImpl::get()->log);
+ NvBlastID id1 = NvBlastFamilyGetAssetID(newFamily, TkFrameworkImpl::get()->log);
+ NVBLAST_ASSERT(TkGUIDsEqual(&id0, &id1));
+#endif
+ NVBLAST_ASSERT(NvBlastFamilyGetSize(m_familyLL, TkFrameworkImpl::get()->log) == NvBlastFamilyGetSize(newFamily, TkFrameworkImpl::get()->log));
+
+ // alloc and init new family
+ const uint32_t blockSize = NvBlastFamilyGetSize(newFamily, TkFrameworkImpl::get()->log);
+ NvBlastFamily* newFamilyCopy = (NvBlastFamily*)TkFrameworkImpl::get()->alloc(blockSize);
+ memcpy(newFamilyCopy, newFamily, blockSize);
+ NvBlastFamilySetAsset(newFamilyCopy, m_asset->getAssetLL(), TkFrameworkImpl::get()->log);
+
+ // get actors from new family
+ TkArray<NvBlastActor*>::type newLLActors(NvBlastFamilyGetActorCount(newFamilyCopy, TkFrameworkImpl::get()->log));
+ uint32_t actorCount = NvBlastFamilyGetActors(newLLActors.begin(), newLLActors.size(), newFamilyCopy, TkFrameworkImpl::get()->log);
+
+ // reset actor families to nullptr (we use it as a flag later)
+ for (TkActorImpl& actor : m_actors)
+ {
+ if (actor.isActive())
+ {
+ actor.m_family = nullptr;
+ }
+ }
+
+ // prepare split event with new actors
+ auto newActorsSplitEvent = getQueue().allocData<TkSplitEvent>();
+ TkArray<TkActor*>::type children(actorCount);
+ children.resizeUninitialized(0);
+ newActorsSplitEvent->children = children.begin();
+
+ // scratch
+ TkArray<uint32_t>::type scratch(m_asset->getChunkCount());
+
+ for (uint32_t i = 0; i < actorCount; ++i)
+ {
+ NvBlastActor* newLLActor = newLLActors[i];
+ uint32_t actorIndex = NvBlastActorGetIndex(newLLActor, TkFrameworkImpl::get()->log);
+ TkActorImpl& tkActor = *getActorByIndex(actorIndex);
+
+ tkActor.m_family = this;
+
+ if (!tkActor.isActive() || !areLLActorsEqual(newLLActor, tkActor.m_actorLL, scratch))
+ {
+ if (tkActor.isActive())
+ {
+ auto removeSplitEvent = getQueue().allocData<TkSplitEvent>();
+ removeSplitEvent->parentData.family = this;
+ removeSplitEvent->numChildren = 0;
+ removeSplitEvent->parentData.userData = tkActor.userData;
+ removeSplitEvent->parentData.index = tkActor.getIndex();
+ getQueue().addEvent(removeSplitEvent);
+ }
+
+ tkActor.m_actorLL = newLLActor;
+
+ // switch groups
+ TkGroupImpl* prevGroup = tkActor.m_group;
+ if (prevGroup != group)
+ {
+ if (prevGroup)
+ {
+ prevGroup->removeActor(tkActor);
+ }
+ if (group)
+ {
+ group->addActor(tkActor);
+ }
+ }
+
+ children.pushBack(&tkActor);
+ }
+ else
+ {
+ tkActor.m_actorLL = newLLActor;
+ }
+ }
+
+ // if m_family is still nullptr for an active actor -> remove it. It doesn't exist in new family.
+ for (TkActorImpl& tkActor : m_actors)
+ {
+ if (tkActor.isActive() && tkActor.m_family == nullptr)
+ {
+ tkActor.m_family = this;
+ if (tkActor.m_group)
+ {
+ tkActor.m_group->removeActor(tkActor);
+ }
+
+ auto removeSplitEvent = getQueue().allocData<TkSplitEvent>();
+ removeSplitEvent->parentData.family = this;
+ removeSplitEvent->numChildren = 0;
+ removeSplitEvent->parentData.userData = tkActor.userData;
+ removeSplitEvent->parentData.index = tkActor.getIndex();
+ getQueue().addEvent(removeSplitEvent);
+
+ tkActor.m_actorLL = nullptr;
+ }
+ }
+
+ // add split event with all new actors
+ newActorsSplitEvent->parentData.family = this;
+ newActorsSplitEvent->parentData.userData = 0;
+ newActorsSplitEvent->parentData.index = invalidIndex<uint32_t>();
+ newActorsSplitEvent->numChildren = children.size();
+ if (newActorsSplitEvent->numChildren > 0)
+ {
+ getQueue().addEvent(newActorsSplitEvent);
+ }
+
+ // replace family
+ TkFrameworkImpl::get()->free(m_familyLL);
+ m_familyLL = newFamilyCopy;
+
+ // update joints
+ for (TkActorImpl& tkActor : m_actors)
+ {
+ if (!tkActor.m_jointList.isEmpty())
+ {
+ updateJoints(&tkActor);
+ }
+ }
+
+ getQueue().dispatch();
+}
+
+
+TkActorImpl* TkFamilyImpl::getActorByChunk(uint32_t chunk)
+{
+ if (chunk >= NvBlastAssetGetChunkCount(m_asset->getAssetLLInternal(), TkFrameworkImpl::get()->log))
+ {
+ NVBLASTTK_LOG_WARNING("TkFamilyImpl::getActorByChunk: invalid chunk index. Returning NULL.");
+ return nullptr;
+ }
+
+ NvBlastActor* actorLL = NvBlastFamilyGetChunkActor(m_familyLL, chunk, TkFrameworkImpl::get()->log);
+ return actorLL ? getActorByActorLL(actorLL) : nullptr;
+}
+
+
+void TkFamilyImpl::applyFractureInternal(const NvBlastFractureBuffers* commands)
+{
+ NvBlastSupportGraph graph = getAsset()->getGraph();
+
+ // apply bond fracture commands on relevant actors
+ {
+ TkActorImpl* currActor = nullptr;
+ NvBlastBondFractureData* bondFractures = commands->bondFractures;
+ uint32_t bondFracturesCount = 0;
+
+ auto applyFracture = [&]()
+ {
+ if (bondFracturesCount > 0)
+ {
+ if (currActor != nullptr && currActor->isActive())
+ {
+ NvBlastFractureBuffers newCommands;
+ newCommands.bondFractures = bondFractures;
+ newCommands.bondFractureCount = bondFracturesCount;
+ newCommands.chunkFractures = nullptr;
+ newCommands.chunkFractureCount = 0;
+ currActor->applyFracture(nullptr, &newCommands);
+ }
+
+ bondFractures += bondFracturesCount;
+ bondFracturesCount = 0;
+ }
+ };
+
+ for (uint32_t i = 0; i < commands->bondFractureCount; ++i, ++bondFracturesCount)
+ {
+ const NvBlastBondFractureData& command = commands->bondFractures[i];
+ uint32_t chunk0 = graph.chunkIndices[command.nodeIndex0];
+ uint32_t chunk1 = graph.chunkIndices[command.nodeIndex1];
+ TkActorImpl* actor0 = getActorByChunk(chunk0);
+ TkActorImpl* actor1 = getActorByChunk(chunk1);
+ if (actor0 != actor1)
+ {
+ // skipping this event, bond already broken
+ actor0 = nullptr;
+ }
+ if (actor0 != currActor)
+ {
+ applyFracture();
+ currActor = actor0;
+ }
+ }
+
+ if (bondFracturesCount > 0)
+ {
+ applyFracture();
+ }
+ }
+
+ // apply chunk fracture commands on relevant actors
+ {
+ TkActorImpl* currActor = nullptr;
+ NvBlastChunkFractureData* chunkFractures = commands->chunkFractures;
+ uint32_t chunkFracturesCount = 0;
+
+ auto applyFracture = [&]()
+ {
+ if (chunkFracturesCount > 0)
+ {
+ if (currActor != nullptr && currActor->isActive())
+ {
+ NvBlastFractureBuffers newCommands;
+ newCommands.bondFractures = nullptr;
+ newCommands.bondFractureCount = 0;
+ newCommands.chunkFractures = chunkFractures;
+ newCommands.chunkFractureCount = chunkFracturesCount;
+ currActor->applyFracture(nullptr, &newCommands);
+ }
+
+ chunkFractures += chunkFracturesCount;
+ chunkFracturesCount = 0;
+ }
+ };
+
+ for (uint32_t i = 0; i < commands->chunkFractureCount; ++i, ++chunkFracturesCount)
+ {
+ const NvBlastChunkFractureData& command = commands->chunkFractures[i];
+ TkActorImpl* actor = getActorByChunk(command.chunkIndex);
+ if (actor != currActor)
+ {
+ applyFracture();
+ currActor = actor;
+ }
+ }
+ if (chunkFracturesCount > 0)
+ {
+ applyFracture();
+ }
+ }
+}
+
+
+void TkFamilyImpl::updateJoints(TkActorImpl* actor, TkEventQueue* alternateQueue)
+{
+ // Copy joint array for safety against implementation of joint->setActor
+ TkJointImpl** joints = reinterpret_cast<TkJointImpl**>(NvBlastAlloca(sizeof(TkJointImpl*)*actor->getJointCountInternal()));
+ TkJointImpl** stop = joints + actor->getJointCountInternal();
+ TkJointImpl** jointHandle = joints;
+ for (TkActorImpl::JointIt j(*actor); (bool)j; ++j)
+ {
+ *jointHandle++ = *j;
+ }
+ jointHandle = joints;
+ while (jointHandle < stop)
+ {
+ TkJointImpl* joint = *jointHandle++;
+
+ const TkJointData& data = joint->getDataInternal();
+
+ TkActorImpl* actor0 = data.actors[0] != nullptr ?
+ static_cast<TkActorImpl&>(*data.actors[0]).getFamilyImpl().getActorByChunk(data.chunkIndices[0]) : nullptr;
+
+ TkActorImpl* actor1 = data.actors[1] != nullptr ?
+ static_cast<TkActorImpl&>(*data.actors[1]).getFamilyImpl().getActorByChunk(data.chunkIndices[1]) : nullptr;
+
+ joint->setActors(actor0, actor1, alternateQueue);
+ }
+}
+
+
+const TkAsset* TkFamilyImpl::getAsset() const
+{
+ return m_asset;
+}
+
+
+bool TkFamilyImpl::serialize(PxFileBuf& stream) const
+{
+ TkFrameworkImpl::get()->serializeHeader(*this, stream);
+
+ if (m_material != nullptr)
+ {
+ NVBLASTTK_LOG_WARNING("TkFamilyImpl::serialize(): Material pointer is not nullptr, it will be lost during serialization.");
+ }
+
+ NVBLASTTK_CHECK_ERROR(m_asset != nullptr, "TkFamilyImpl::serialize(): TkFamily asset is nullptr, can't be serialized.", return false);
+ NVBLASTTK_CHECK_ERROR(m_familyLL != nullptr, "TkFamilyImpl::serialize(): TkFamily family is nullptr, can't be serialized.", return false);
+
+ // Asset ID
+ const NvBlastID& assetID = m_asset->getID();
+ NVBLASTTK_CHECK_ERROR(!TkGUIDIsZero(&assetID), "TkFamilyImpl::serialize(): Associated asset doesn't have an ID set.", return false);
+ stream.write(&assetID, sizeof(NvBlastID));
+
+ // Family
+ const uint32_t familySize = NvBlastFamilyGetSize(m_familyLL, TkFrameworkImpl::get()->log);
+ stream.storeDword(familySize);
+ stream.write(m_familyLL, familySize);
+
+ //// Joints ////
+
+ // Internal joint data
+ stream.storeDword(m_internalJointCount);
+
+ // External joint family ID list
+ stream.storeDword(m_jointSets.size());
+ for (uint32_t i = 0; i < m_jointSets.size(); ++i)
+ {
+ const JointSet* jointSet = m_jointSets[i];
+ stream.write(&jointSet->m_familyID, sizeof(NvBlastID));
+ }
+
+ // Actor joint lists
+ TkJointImpl* internalJoints = getInternalJoints();
+ for (uint32_t actorNum = 0; actorNum < m_actors.size(); ++actorNum)
+ {
+ const TkActorImpl& actor = m_actors[actorNum];
+ if (!actor.isActive())
+ {
+ continue; // We may need a better way of iterating through active actors
+ }
+
+ stream.storeDword(actor.getJointCount());
+
+ for (TkActorImpl::JointIt j(actor); (bool)j; ++j)
+ {
+ TkJointImpl* joint = *j;
+
+ const TkJointData& jointData = joint->getDataInternal();
+ NVBLAST_ASSERT(jointData.actors[0] == &actor || jointData.actors[1] == &actor);
+
+ const uint32_t attachmentFlags = (uint32_t)(jointData.actors[0] == &actor) | (uint32_t)(jointData.actors[1] == &actor) << 1;
+ stream.storeDword(attachmentFlags);
+
+ const TkActorImpl* otherActor = static_cast<const TkActorImpl*>(jointData.actors[(attachmentFlags >> 1) ^ 1]);
+
+ if (joint->m_owner == this)
+ {
+ // Internal joint - write internal joint index
+ const uint32_t jointIndex = static_cast<uint32_t>(joint - internalJoints);
+ stream.storeDword(jointIndex);
+ if (otherActor != nullptr && otherActor->getIndexInternal() < actorNum) // No need to write the joint data, it has already been written
+ {
+ continue;
+ }
+ }
+ else
+ {
+ // External joint - write external family index and joint information
+ stream.storeDword(invalidIndex<uint32_t>()); // Denotes external joint
+
+ const FamilyIDMap::Entry* e = m_familyIDMap.find(getFamilyID(otherActor));
+ NVBLASTTK_CHECK_ERROR(e != nullptr, "TkFamilyImpl::deserialize(): Bad data - attached family's ID not recorded.", return false);
+
+ stream.storeDword(e->second); // Write family ID index
+ }
+
+ // Write joint data
+ for (int side = 0; side < 2; ++side)
+ {
+ stream.storeDword(jointData.chunkIndices[side]);
+ const physx::PxVec3& attachPosition = jointData.attachPositions[side];
+ stream.storeFloat(attachPosition.x); stream.storeFloat(attachPosition.y); stream.storeFloat(attachPosition.z);
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//////// Static functions ////////
+
+TkSerializable* TkFamilyImpl::deserialize(PxFileBuf& stream, const NvBlastID& id)
+{
+ // Asset resolve
+ NvBlastID assetID;
+ stream.read(&assetID, sizeof(NvBlastID));
+ TkIdentifiable* object = TkFrameworkImpl::get()->findObjectByIDInternal(assetID);
+ NVBLASTTK_CHECK_ERROR(object && object->getType() == TkAssetImpl::s_type, "TkFamilyImpl::deserialize: can't find asset with corresponding ID.", return nullptr);
+ TkAssetImpl* asset = static_cast<TkAssetImpl*>(object);
+
+ // Allocate
+ TkFamilyImpl* family = NVBLASTTK_NEW(TkFamilyImpl)(id);
+ NVBLASTTK_CHECK_ERROR(family != nullptr, "TkFamilyImpl::deserialize: family allocation failed.", return nullptr);
+
+ // associate with found asset
+ family->m_asset = asset;
+
+ // Family
+ const uint32_t familySize = stream.readDword();
+ family->m_familyLL = static_cast<NvBlastFamily*>(TkFrameworkImpl::get()->alloc(familySize));
+ stream.read(family->m_familyLL, familySize);
+
+ if (family->m_familyLL == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkFamilyImpl::deserialize: low-level family could not be created.");
+ family->release();
+ return nullptr;
+ }
+
+#if NV_ENABLE_ASSERTS && 0
+ NvBlastID id = NvBlastFamilyGetAssetID(family->m_familyLL, TkFrameworkImpl::get()->log);
+ NVBLAST_ASSERT(TkGUIDsEqual(&id, &assetID));
+#endif
+
+ // preallocate actors
+ uint32_t maxActorCount = NvBlastFamilyGetMaxActorCount(family->m_familyLL, TkFrameworkImpl::get()->log);
+ family->m_actors.resize(maxActorCount);
+
+ // get actors from family
+ TkArray<NvBlastActor*>::type newLLActors(NvBlastFamilyGetActorCount(family->m_familyLL, TkFrameworkImpl::get()->log));
+ uint32_t actorCount = NvBlastFamilyGetActors(newLLActors.begin(), newLLActors.size(), family->m_familyLL, TkFrameworkImpl::get()->log);
+
+ // fill actors
+ for (uint32_t i = 0; i < actorCount; ++i)
+ {
+ NvBlastActor* newLLActor = newLLActors[i];
+ uint32_t actorIndex = NvBlastActorGetIndex(newLLActor, TkFrameworkImpl::get()->log);
+ TkActorImpl& tkActor = *family->getActorByIndex(actorIndex);
+
+ tkActor.m_family = family;
+ tkActor.m_actorLL = newLLActor;
+ }
+
+ //// Create joints ////
+
+ // internal
+ family->m_internalJointCount = stream.readDword();
+ family->m_internalJointBuffer.resize(family->m_internalJointCount * sizeof(TkJointImpl), '\0');
+ TkJointImpl* internalJoints = family->getInternalJoints();
+
+ // external joint family ID list
+ const uint32_t jointSetCount = stream.readDword();
+ family->m_jointSets.resize(jointSetCount);
+ for (uint32_t i = 0; i < jointSetCount; ++i)
+ {
+ family->m_jointSets[i] = NVBLASTTK_NEW(JointSet);
+ stream.read(&family->m_jointSets[i]->m_familyID, sizeof(NvBlastID));
+ family->m_familyIDMap[family->m_jointSets[i]->m_familyID] = i;
+ }
+
+ // fill actor joint lists
+ for (uint32_t actorNum = 0; actorNum < family->m_actors.size(); ++actorNum)
+ {
+ TkActorImpl& actor = family->m_actors[actorNum];
+ if (!actor.isActive())
+ {
+ continue; // We may need a better way of iterating through active actors
+ }
+
+ // Read joint information
+ uint32_t jointCount = stream.readDword();
+ while (jointCount--)
+ {
+ const uint32_t attachmentFlags = stream.readDword();
+ const uint32_t jointIndex = stream.readDword();
+ if (!isInvalidIndex(jointIndex))
+ {
+ // Internal joint
+ TkJointImpl& joint = internalJoints[jointIndex];
+ TkJointData& jointData = joint.getDataWritable();
+
+ // Initialize joint if it has not been encountered yet
+ NVBLAST_ASSERT((joint.m_links[0].m_joint == nullptr) == (joint.m_links[1].m_joint == nullptr));
+ if (joint.m_links[0].m_joint == nullptr)
+ {
+ new (&joint) TkJointImpl;
+ joint.m_owner = family;
+ for (int side = 0; side < 2; ++side)
+ {
+ jointData.chunkIndices[side] = stream.readDword();
+ physx::PxVec3& attachPosition = jointData.attachPositions[side];
+ attachPosition.x = stream.readFloat(); attachPosition.y = stream.readFloat(); attachPosition.z = stream.readFloat();
+ }
+ }
+
+ if (attachmentFlags & 1)
+ {
+ jointData.actors[0] = &actor;
+ actor.addJoint(joint.m_links[0]);
+ }
+
+ if (attachmentFlags & 2)
+ {
+ jointData.actors[1] = &actor;
+ if (jointData.actors[0] != jointData.actors[1])
+ {
+ actor.addJoint(joint.m_links[1]);
+ }
+ }
+ }
+ else
+ {
+ // External joint
+ const uint32_t otherFamilyIndex = stream.readDword();
+ NVBLASTTK_CHECK_ERROR(otherFamilyIndex < family->m_jointSets.size(), "TkFamilyImpl::deserialize: family allocation failed.", return nullptr);
+ const NvBlastID& otherFamilyID = family->m_jointSets[otherFamilyIndex]->m_familyID;
+ TkFamilyImpl* otherFamily = static_cast<TkFamilyImpl*>(TkFrameworkImpl::get()->findObjectByIDInternal(otherFamilyID));
+
+ TkJointDesc jointDesc;
+ for (int side = 0; side < 2; ++side)
+ {
+ jointDesc.chunkIndices[side] = stream.readDword();
+ physx::PxVec3& attachPosition = jointDesc.attachPositions[side];
+ attachPosition.x = stream.readFloat(); attachPosition.y = stream.readFloat(); attachPosition.z = stream.readFloat();
+ }
+
+ NVBLASTTK_CHECK_ERROR(attachmentFlags != 3, "TkFamilyImpl::deserialize: both attached actors are the same in an external joint.", return nullptr);
+
+ const uint32_t attachmentIndex = attachmentFlags >> 1;
+
+ TkJointImpl** jointHandle = family->createExternalJointHandle(otherFamilyID, jointDesc.chunkIndices[attachmentIndex], jointDesc.chunkIndices[attachmentIndex ^ 1]);
+ NVBLASTTK_CHECK_ERROR(jointHandle != nullptr, "TkFamilyImpl::deserialize: joint handle could not be created.", return nullptr);
+
+ if (otherFamily == nullptr)
+ {
+ // Other family does not exist yet, we'll create the joint
+ jointDesc.families[attachmentIndex] = family;
+ jointDesc.families[attachmentIndex ^ 1] = nullptr;
+
+ TkJointImpl* joint = NVBLASTTK_NEW(TkJointImpl)(jointDesc, nullptr);
+ NVBLASTTK_CHECK_ERROR(joint != nullptr, "TkFamilyImpl::deserialize: joint createion failed.", return nullptr);
+
+ *jointHandle = joint;
+
+ actor.addJoint(joint->m_links[attachmentIndex]);
+ }
+ else
+ {
+ // Other family exists, and should have created the joint
+ TkJointImpl* joint = otherFamily->findExternalJoint(family, ExternalJointKey(jointDesc.chunkIndices[attachmentIndex ^ 1], jointDesc.chunkIndices[attachmentIndex]));
+ NVBLASTTK_CHECK_ERROR(joint != nullptr, "TkFamilyImpl::deserialize: other family should have created joint, but did not.", return nullptr);
+
+ *jointHandle = joint;
+
+ // Add the joint to its actor(s)
+ joint->getDataWritable().actors[attachmentIndex] = &actor;
+ actor.addJoint(joint->m_links[attachmentIndex]);
+ }
+ }
+ }
+ }
+
+ return family;
+}
+
+
+TkFamilyImpl* TkFamilyImpl::create(const TkAssetImpl* asset)
+{
+ TkFamilyImpl* family = NVBLASTTK_NEW(TkFamilyImpl);
+ family->m_asset = asset;
+ void* mem = TkFrameworkImpl::get()->alloc(NvBlastAssetGetFamilyMemorySize(asset->getAssetLL(), TkFrameworkImpl::get()->log));
+ family->m_familyLL = NvBlastAssetCreateFamily(mem, asset->getAssetLL(), TkFrameworkImpl::get()->log);
+ //family->addListener(*TkFrameworkImpl::get());
+
+ if (family->m_familyLL == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkFamilyImpl::create: low-level family could not be created.");
+ family->release();
+ return nullptr;
+ }
+
+ uint32_t maxActorCount = NvBlastFamilyGetMaxActorCount(family->m_familyLL, TkFrameworkImpl::get()->log);
+ family->m_actors.resize(maxActorCount);
+
+ family->m_internalJointBuffer.resize(asset->getJointDescCountInternal() * sizeof(TkJointImpl), 0);
+ family->m_internalJointCount = asset->getJointDescCountInternal();
+
+ return family;
+}
+
+
+TkJointImpl** TkFamilyImpl::createExternalJointHandle(const NvBlastID& otherFamilyID, uint32_t chunkIndex0, uint32_t chunkIndex1)
+{
+ JointSet* jointSet;
+ const FamilyIDMap::Entry* jointSetIndexEntry = m_familyIDMap.find(otherFamilyID);
+ uint32_t otherFamilyIndex;
+ if (jointSetIndexEntry != nullptr)
+ {
+ otherFamilyIndex = jointSetIndexEntry->second;
+ jointSet = m_jointSets[otherFamilyIndex];
+ }
+ else
+ {
+ jointSet = NVBLASTTK_NEW(JointSet);
+ NVBLASTTK_CHECK_ERROR(jointSet != nullptr, "TkFamilyImpl::addExternalJoint: failed to create joint set for other family ID.", return nullptr);
+ jointSet->m_familyID = otherFamilyID;
+ otherFamilyIndex = m_jointSets.size();
+ m_familyIDMap[otherFamilyID] = otherFamilyIndex;
+ m_jointSets.pushBack(jointSet);
+ }
+
+ const ExternalJointKey key(chunkIndex0, chunkIndex1);
+ const bool jointExists = jointSet->m_joints.find(key) != nullptr;
+ NVBLASTTK_CHECK_WARNING(!jointExists, "TkFamilyImpl::addExternalJoint: joint already added.", return nullptr);
+
+ return &jointSet->m_joints[key];
+}
+
+
+bool TkFamilyImpl::deleteExternalJointHandle(TkJointImpl*& joint, const NvBlastID& otherFamilyID, uint32_t chunkIndex0, uint32_t chunkIndex1)
+{
+ const FamilyIDMap::Entry* jointSetIndexEntry = m_familyIDMap.find(otherFamilyID);
+ if (jointSetIndexEntry != nullptr)
+ {
+ const uint32_t jointSetIndex = jointSetIndexEntry->second;
+ TkHashMap<ExternalJointKey, TkJointImpl*>::type::Entry e;
+ if (m_jointSets[jointSetIndex]->m_joints.erase(ExternalJointKey(chunkIndex0, chunkIndex1), e))
+ {
+ // Delete the joint set if it is empty
+ if (m_jointSets[jointSetIndex]->m_joints.size() == 0)
+ {
+ NVBLASTTK_DELETE(m_jointSets[jointSetIndex], JointSet);
+ m_jointSets.replaceWithLast(jointSetIndex);
+ m_familyIDMap.erase(otherFamilyID);
+ if (jointSetIndex < m_jointSets.size())
+ {
+ m_familyIDMap[m_jointSets[jointSetIndex]->m_familyID] = jointSetIndex;
+ }
+ }
+
+ // Return value that was stored
+ joint = e.second;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+TkJointImpl* TkFamilyImpl::findExternalJoint(const TkFamilyImpl* otherFamily, ExternalJointKey key) const
+{
+ const FamilyIDMap::Entry* jointSetIndexEntry = m_familyIDMap.find(getFamilyID(otherFamily));
+ if (jointSetIndexEntry != nullptr)
+ {
+ const TkHashMap<ExternalJointKey, TkJointImpl*>::type::Entry* e = m_jointSets[jointSetIndexEntry->second]->m_joints.find(key);
+ if (e != nullptr)
+ {
+ return e->second;
+ }
+ }
+
+ return nullptr;
+}
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.h
new file mode 100644
index 0000000..571ee75
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.h
@@ -0,0 +1,245 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKFAMILYIMPL_H
+#define NVBLASTTKFAMILYIMPL_H
+
+#include "NvBlastTkCommon.h"
+
+#include "NvBlastTkFamily.h"
+#include "NvBlastTkTypeImpl.h"
+#include "NvBlastTkActorImpl.h"
+
+#include "NvBlastTkEventQueue.h"
+#include "NvBlastTkHashSet.h"
+#include "NvBlastTkHashMap.h"
+
+#include "NvBlast.h"
+#include "NvBlastAssert.h"
+#include "NvBlastDLink.h"
+
+
+// Forward declarations
+struct NvBlastFamily;
+
+namespace Nv
+{
+namespace Blast
+{
+
+// Forward declarations
+class TkGroupImpl;
+class TkAssetImpl;
+
+
+NVBLASTTK_IMPL_DECLARE(Family)
+{
+public:
+ /**
+ Enum which keeps track of the serialized data format.
+ */
+ enum Version
+ {
+ /** Initial version */
+ Initial,
+
+ // New formats must come before Count. They should be given descriptive names with more information in comments.
+
+ /** The number of serialized formats. */
+ Count,
+
+ /** The current version. This should always be Count-1 */
+ Current = Count - 1
+ };
+
+ TkFamilyImpl();
+ TkFamilyImpl(const NvBlastID& id);
+ ~TkFamilyImpl();
+
+ NVBLASTTK_IMPL_DEFINE_SERIALIZABLE('A', 'C', 'T', 'F');
+
+ // Begin TkFamily
+ virtual const NvBlastFamily* getFamilyLL() const override;
+
+ virtual uint32_t getActorCount() const override;
+
+ virtual uint32_t getActors(TkActor** buffer, uint32_t bufferSize, uint32_t indexStart = 0) const override;
+
+ virtual void addListener(TkEventListener& l) override { m_queue.addListener(l); }
+
+ virtual void removeListener(TkEventListener& l) override { m_queue.removeListener(l); }
+
+ virtual void applyFracture(const NvBlastFractureBuffers* commands) override { applyFractureInternal(commands); }
+
+ virtual const TkAsset* getAsset() const override;
+
+ virtual void reinitialize(const NvBlastFamily* newFamily, TkGroup* group) override;
+
+ virtual const void* getMaterial() const override;
+
+ virtual void setMaterial(const void* material) override;
+ // End TkFamily
+
+ // Public methods
+ static TkFamilyImpl* create(const TkAssetImpl* asset);
+
+ const TkAssetImpl* getAssetImpl() const;
+
+ NvBlastFamily* getFamilyLLInternal() const;
+
+ uint32_t getActorCountInternal() const;
+
+ TkActorImpl* addActor(NvBlastActor* actorLL);
+
+ void applyFractureInternal(const NvBlastFractureBuffers* commands);
+
+ void removeActor(TkActorImpl* actorLL);
+
+ TkEventQueue& getQueue() { return m_queue; }
+
+ TkActorImpl* getActorByActorLL(const NvBlastActor* actorLL);
+
+ void updateJoints(TkActorImpl* actor, TkEventQueue* alternateQueue = nullptr);
+
+ TkArray<TkActorImpl>::type& getActorsInternal();
+
+ uint32_t getInternalJointCount() const;
+
+ TkJointImpl* getInternalJoints() const;
+
+ TkJointImpl** createExternalJointHandle(const NvBlastID& otherFamilyID, uint32_t chunkIndex0, uint32_t chunkIndex1);
+
+ bool deleteExternalJointHandle(TkJointImpl*& joint, const NvBlastID& otherFamilyID, uint32_t chunkIndex0, uint32_t chunkIndex1);
+
+ void releaseJoint(TkJointImpl& joint);
+
+ TkActorImpl* getActorByChunk(uint32_t chunkIndex);
+
+ typedef physx::shdfnd::Pair<uint32_t, uint32_t> ExternalJointKey; //!< The chunk indices within the TkFamily joined by the joint. This chunks will be a supports chunks.
+
+ TkJointImpl* findExternalJoint(const TkFamilyImpl* otherFamily, ExternalJointKey key) const;
+
+private:
+ TkActorImpl* getActorByIndex(uint32_t index);
+
+ struct JointSet
+ {
+ NvBlastID m_familyID;
+ TkHashMap<ExternalJointKey, TkJointImpl*>::type m_joints;
+ };
+
+ typedef TkHashMap<NvBlastID, uint32_t>::type FamilyIDMap;
+
+ NvBlastFamily* m_familyLL;
+ TkArray<TkActorImpl>::type m_actors;
+ uint32_t m_internalJointCount;
+ TkArray<uint8_t>::type m_internalJointBuffer;
+ TkArray<JointSet*>::type m_jointSets;
+ FamilyIDMap m_familyIDMap;
+ const TkAssetImpl* m_asset;
+ const void* m_material;
+
+ TkEventQueue m_queue;
+};
+
+
+//////// TkFamilyImpl inline methods ////////
+
+NV_INLINE const TkAssetImpl* TkFamilyImpl::getAssetImpl() const
+{
+ return m_asset;
+}
+
+
+NV_INLINE NvBlastFamily* TkFamilyImpl::getFamilyLLInternal() const
+{
+ return m_familyLL;
+}
+
+
+NV_INLINE uint32_t TkFamilyImpl::getActorCountInternal() const
+{
+ NVBLAST_ASSERT(m_familyLL != nullptr);
+
+ return NvBlastFamilyGetActorCount(m_familyLL, TkFrameworkImpl::get()->log);
+}
+
+
+NV_INLINE TkActorImpl* TkFamilyImpl::getActorByIndex(uint32_t index)
+{
+ NVBLAST_ASSERT(index < m_actors.size());
+ return &m_actors[index];
+}
+
+
+NV_INLINE TkActorImpl* TkFamilyImpl::getActorByActorLL(const NvBlastActor* actorLL)
+{
+ uint32_t index = NvBlastActorGetIndex(actorLL, TkFrameworkImpl::get()->log);
+ return getActorByIndex(index);
+}
+
+
+NV_INLINE const void* TkFamilyImpl::getMaterial() const
+{
+ return m_material;
+}
+
+
+NV_INLINE void TkFamilyImpl::setMaterial(const void* material)
+{
+ m_material = material;
+}
+
+
+NV_INLINE TkArray<TkActorImpl>::type& TkFamilyImpl::getActorsInternal()
+{
+ return m_actors;
+}
+
+
+NV_INLINE uint32_t TkFamilyImpl::getInternalJointCount() const
+{
+ return m_internalJointCount;
+}
+
+
+NV_INLINE TkJointImpl* TkFamilyImpl::getInternalJoints() const
+{
+ return const_cast<TkJointImpl*>(reinterpret_cast<const TkJointImpl*>(m_internalJointBuffer.begin()));
+}
+
+
+NV_INLINE void TkFamilyImpl::releaseJoint(TkJointImpl& joint)
+{
+ NVBLAST_ASSERT(joint.m_owner == this);
+ NVBLAST_ASSERT(&joint >= getInternalJoints() && &joint < getInternalJoints() + getInternalJointCount() * sizeof(TkJointImpl));
+
+ joint.~TkJointImpl();
+ joint.m_owner = nullptr;
+}
+
+
+//////// Inline global functions ////////
+
+NV_INLINE const NvBlastID& getFamilyID(const TkActor* actor)
+{
+ return actor != nullptr ? static_cast<const TkActorImpl*>(actor)->getFamilyImpl().getIDInternal() : *reinterpret_cast<const NvBlastID*>("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
+}
+
+NV_INLINE const NvBlastID& getFamilyID(const TkFamilyImpl* family)
+{
+ return family != nullptr ? family->getIDInternal() : *reinterpret_cast<const NvBlastID*>("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
+}
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTTKFAMILYIMPL_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.cpp
new file mode 100644
index 0000000..6201101
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.cpp
@@ -0,0 +1,613 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#include "NvBlastAssert.h"
+
+#include "NvBlastTkFrameworkImpl.h"
+#include "NvBlastTkAssetImpl.h"
+#include "NvBlastTkFamilyImpl.h"
+#include "NvBlastTkGroupImpl.h"
+#include "NvBlastTkActorImpl.h"
+#include "NvBlastTkJointImpl.h"
+#include "NvBlastTkTypeImpl.h"
+
+#include "PxAllocatorCallback.h"
+#include "PxErrorCallback.h"
+#include "PxFileBuf.h"
+
+#include <algorithm>
+
+
+using namespace physx;
+using namespace physx::shdfnd;
+using namespace physx::general_PxIOStream2;
+
+
+NV_INLINE bool operator < (const NvBlastID& id1, const NvBlastID& id2)
+{
+ return memcmp(&id1, &id2, sizeof(NvBlastID)) < 0;
+}
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+//////// Local definitions ////////
+
+// Map type ID to static type data
+#define NVBLASTTK_REGISTER_TYPE(_name) \
+ if (!Tk##_name##Impl::s_type.indexIsValid()) \
+ { \
+ Tk##_name##Impl::s_type.setIndex(TkTypeIndex::_name); \
+ } \
+ m_types[TkTypeIndex::_name] = &Tk##_name##Impl::s_type; \
+ m_typeIDToIndex[Tk##_name##Impl::s_type.getID()] = TkTypeIndex::_name
+
+
+#define NVBLASTTK_RELEASE_TYPE(_name) \
+ { \
+ TkTypeImpl& type = Tk##_name##Impl::s_type; \
+ auto& toRelease = m_objects[type.getIndex()]; \
+ for (TkObject* obj : toRelease) \
+ { \
+ obj->release(); \
+ } \
+ }
+
+
+//////// TkFrameworkImpl static variables ////////
+
+TkFrameworkImpl* TkFrameworkImpl::s_framework = nullptr;
+
+
+//////// TkFrameworkImpl static function ////////
+
+TkFrameworkImpl* TkFrameworkImpl::get()
+{
+ return s_framework;
+}
+
+
+bool TkFrameworkImpl::set(TkFrameworkImpl* framework)
+{
+ if (s_framework != nullptr)
+ {
+ if (framework != nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::set: framework already set. Pass NULL to this function to destroy framework.");
+ return false;
+ }
+
+ PxAllocatorCallback& allocator = s_framework->getAllocatorCallbackInternal();
+ s_framework->~TkFrameworkImpl();
+ allocator.deallocate(s_framework);
+ }
+
+ s_framework = framework;
+
+ return true;
+}
+
+
+void TkFrameworkImpl::log(int type, const char* msg, const char* file, int line)
+{
+ if (s_framework == nullptr)
+ {
+ return;
+ }
+
+ PxErrorCode::Enum pxErrorCode = PxErrorCode::eNO_ERROR;
+ switch (type)
+ {
+ case NvBlastMessage::Error: pxErrorCode = PxErrorCode::eINVALID_OPERATION; break;
+ case NvBlastMessage::Warning: pxErrorCode = PxErrorCode::eDEBUG_WARNING; break;
+ case NvBlastMessage::Info: pxErrorCode = PxErrorCode::eDEBUG_INFO; break;
+ case NvBlastMessage::Debug: pxErrorCode = PxErrorCode::eNO_ERROR; break;
+ }
+
+ s_framework->getErrorCallback().reportError(pxErrorCode, msg, file, line);
+}
+
+
+void* TkFrameworkImpl::alloc(size_t size)
+{
+ if (s_framework == nullptr)
+ {
+ return nullptr;
+ }
+
+ NV_COMPILE_TIME_ASSERT(Alignment > 0 && Alignment <= 256);
+
+ unsigned char* mem = reinterpret_cast<unsigned char*>(s_framework->m_allocatorCallback->allocate(size + (size_t)Alignment, "NvBlast", __FILE__, __LINE__));
+
+ const unsigned char offset = (unsigned char)((uintptr_t)Alignment - (uintptr_t)mem % (size_t)Alignment - 1);
+ mem += offset;
+ *mem++ = offset;
+
+ return mem;
+}
+
+
+void TkFrameworkImpl::free(void* mem)
+{
+ if (s_framework == nullptr)
+ {
+ return;
+ }
+
+ unsigned char* ptr = reinterpret_cast<unsigned char*>(mem);
+ const unsigned char offset = *--ptr;
+
+ return s_framework->m_allocatorCallback->deallocate(ptr - offset);
+}
+
+
+//////// TkFrameworkImpl methods ////////
+
+TkFrameworkImpl::TkFrameworkImpl(const TkFrameworkDesc& desc)
+ : TkFramework()
+ , m_errorCallback(desc.errorCallback)
+ , m_allocatorCallback(desc.allocatorCallback)
+{
+ // Static create() function should ensure these pointers are not NULL
+ NVBLAST_ASSERT(m_errorCallback != nullptr);
+ NVBLAST_ASSERT(m_allocatorCallback != nullptr);
+
+ // Register types
+ m_types.resize(TkTypeIndex::TypeCount);
+ m_objects.resize(TkTypeIndex::TypeCount);
+ NVBLASTTK_REGISTER_TYPE(Asset);
+ NVBLASTTK_REGISTER_TYPE(Family);
+ NVBLASTTK_REGISTER_TYPE(Group);
+}
+
+
+TkFrameworkImpl::~TkFrameworkImpl()
+{
+}
+
+
+void TkFrameworkImpl::release()
+{
+ // Special release of joints, which are not TkIdentifiable:
+ TkArray<TkJointImpl*>::type joints; // Since the EraseIterator is not exposed
+ joints.reserve(m_joints.size());
+ for (auto j = m_joints.getIterator(); !j.done(); ++j)
+ {
+ joints.pushBack(*j);
+ }
+ for (uint32_t i = 0; i < joints.size(); ++i)
+ {
+ joints[i]->release();
+ }
+ NVBLAST_ASSERT(m_joints.size() == 0);
+ joints.reset(); // Since we will be deleting the allocator
+
+ NVBLASTTK_RELEASE_TYPE(Group);
+ NVBLASTTK_RELEASE_TYPE(Asset);
+ set(nullptr);
+ Nv::Blast::TkAllocator::s_allocatorCallback = nullptr;
+}
+
+
+physx::PxErrorCallback& TkFrameworkImpl::getErrorCallback() const
+{
+ return getErrorCallbackInternal();
+}
+
+
+physx::PxAllocatorCallback& TkFrameworkImpl::getAllocatorCallback() const
+{
+ return getAllocatorCallbackInternal();
+}
+
+
+NvBlastLog TkFrameworkImpl::getLogFn() const
+{
+ return TkFrameworkImpl::log;
+}
+
+
+TkSerializable* TkFrameworkImpl::deserialize(PxFileBuf& stream)
+{
+ // Read framework ID
+ if (stream.readDword() != ClassID)
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::deserialize: stream does not contain a BlastTk object.");
+ return nullptr;
+ }
+
+ // Read object class ID and get class type data
+ const auto it = m_typeIDToIndex.find(stream.readDword());
+ if (it == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::deserialize: BlastTk object type unrecognized.");
+ return nullptr;
+ }
+
+ const uint32_t index = (*it).second;
+ NVBLAST_ASSERT(index < m_types.size());
+
+ const TkTypeImpl* type = m_types[index];
+
+ // Read object class version and ensure it's current
+ if (stream.readDword() != type->getVersionInternal())
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::deserialize: BlastTk object version does not equal the current version for the loaded type.");
+ return nullptr;
+ }
+
+ // Object ID
+ NvBlastID id;
+ stream.read(&id, sizeof(NvBlastID));
+
+ // Serializable user data
+ const uint32_t lsq = stream.readDword();
+ const uint32_t msq = stream.readDword();
+
+ // All checks out, deserialize
+ TkSerializable* object = type->getDeserializeFn()(stream, id);
+
+ // Set serializable user data if deserialization was successful
+ if (object != nullptr)
+ {
+ object->userIntData = static_cast<uint64_t>(msq) << 32 | static_cast<uint64_t>(lsq);
+ }
+
+ return object;
+}
+
+
+const TkType* TkFrameworkImpl::getType(TkTypeIndex::Enum typeIndex) const
+{
+ if (typeIndex < 0 || typeIndex >= TkTypeIndex::TypeCount)
+ {
+ NVBLASTTK_LOG_WARNING("TkFrameworkImpl::getType: invalid typeIndex.");
+ return nullptr;
+ }
+
+ return m_types[typeIndex];
+}
+
+
+TkIdentifiable* TkFrameworkImpl::findObjectByID(const NvBlastID& id) const
+{
+ TkIdentifiable* object = findObjectByIDInternal(id);
+
+ if (object == nullptr)
+ {
+ NVBLASTTK_LOG_WARNING("TkFrameworkImpl::findObjectByID: object not found.");
+ }
+
+ return object;
+}
+
+
+uint32_t TkFrameworkImpl::getObjectCount(const TkType& type) const
+{
+ const uint32_t index = static_cast<const TkTypeImpl&>(type).getIndex();
+
+ if (index >= m_objects.size())
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::getObjectCount: BlastTk object type unrecognized.");
+ return 0;
+
+ }
+
+ return m_objects[index].size();
+}
+
+
+uint32_t TkFrameworkImpl::getObjects(TkIdentifiable** buffer, uint32_t bufferSize, const TkType& type, uint32_t indexStart /* = 0 */) const
+{
+ const uint32_t index = static_cast<const TkTypeImpl&>(type).getIndex();
+
+ if (index >= m_objects.size())
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::getObjectCount: BlastTk object type unrecognized.");
+ return 0;
+ }
+
+ const auto& objectArray = m_objects[index];
+
+ uint32_t objectCount = objectArray.size();
+ if (objectCount <= indexStart)
+ {
+ NVBLASTTK_LOG_WARNING("TkFrameworkImpl::getObjects: indexStart beyond end of object list.");
+ return 0;
+ }
+
+ objectCount -= indexStart;
+ if (objectCount > bufferSize)
+ {
+ objectCount = bufferSize;
+ }
+
+ memcpy(buffer, objectArray.begin() + indexStart, objectCount * sizeof(TkObject*));
+
+ return objectCount;
+}
+
+
+bool TkFrameworkImpl::reorderAssetDescChunks(NvBlastChunkDesc* chunkDescs, uint32_t chunkCount, NvBlastBondDesc* bondDescs, uint32_t bondCount, uint32_t* chunkReorderMap /*= nullptr*/) const
+{
+ uint32_t* map = chunkReorderMap != nullptr ? chunkReorderMap : static_cast<uint32_t*>(NVBLASTTK_ALLOC(chunkCount * sizeof(uint32_t), "reorderAssetDescChunks:chunkReorderMap"));
+ void* scratch = NVBLASTTK_ALLOC(chunkCount * sizeof(NvBlastChunkDesc), "reorderAssetDescChunks:scratch");
+ const bool result = NvBlastReorderAssetDescChunks(chunkDescs, chunkCount, bondDescs, bondCount, map, scratch, log);
+ NVBLASTTK_FREE(scratch);
+ if (chunkReorderMap == nullptr)
+ {
+ NVBLASTTK_FREE(map);
+ }
+ return result;
+}
+
+
+bool TkFrameworkImpl::ensureAssetExactSupportCoverage(NvBlastChunkDesc* chunkDescs, uint32_t chunkCount) const
+{
+ void* scratch = NVBLASTTK_ALLOC(chunkCount, "ensureAssetExactSupportCoverage:scratch");
+ const bool result = NvBlastEnsureAssetExactSupportCoverage(chunkDescs, chunkCount, scratch, log);
+ NVBLASTTK_FREE(scratch);
+ return result;
+}
+
+
+TkAsset* TkFrameworkImpl::createAsset(const TkAssetDesc& desc)
+{
+ TkAssetImpl* asset = TkAssetImpl::create(desc);
+ if (asset == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::createAsset: failed to create asset.");
+ }
+
+ return asset;
+}
+
+
+TkAsset* TkFrameworkImpl::createAsset(const NvBlastAsset* assetLL, Nv::Blast::TkAssetJointDesc* jointDescs, uint32_t jointDescCount, bool ownsAsset)
+{
+ TkAssetImpl* asset = TkAssetImpl::create(assetLL, jointDescs, jointDescCount, ownsAsset);
+ if (asset == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::createAsset: failed to create asset.");
+ }
+
+ return asset;
+}
+
+
+TkGroup* TkFrameworkImpl::createGroup(const TkGroupDesc& desc)
+{
+ TkGroupImpl* group = TkGroupImpl::create(desc);
+ if (group == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::createGroup: failed to create group.");
+ }
+
+ return group;
+}
+
+
+TkActor* TkFrameworkImpl::createActor(const TkActorDesc& desc)
+{
+ TkActor* actor = TkActorImpl::create(desc);
+ if (actor == nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::createActor: failed to create actor.");
+ }
+
+ return actor;
+}
+
+
+TkJoint* TkFrameworkImpl::createJoint(const TkJointDesc& desc)
+{
+ TkJointImpl** handle0 = nullptr;
+ TkJointImpl** handle1 = nullptr;
+
+ TkFamilyImpl* family0 = static_cast<TkFamilyImpl*>(desc.families[0]);
+ TkFamilyImpl* family1 = static_cast<TkFamilyImpl*>(desc.families[1]);
+
+ NVBLASTTK_CHECK_ERROR(family0 != nullptr || family1 != nullptr, "TkFrameworkImpl::createJoint: at least one family in the TkJointDesc must be valid.", return nullptr);
+
+ NVBLASTTK_CHECK_ERROR(family0 == nullptr || desc.chunkIndices[0] < family0->getAssetImpl()->getChunkCount(), "TkFrameworkImpl::createJoint: desc.chunkIndices[0] is invalid.", return nullptr);
+ NVBLASTTK_CHECK_ERROR(family1 == nullptr || desc.chunkIndices[1] < family1->getAssetImpl()->getChunkCount(), "TkFrameworkImpl::createJoint: desc.chunkIndices[1] is invalid.", return nullptr);
+
+ const bool actorsAreTheSame = family0 == family1 && family0->getActorByChunk(desc.chunkIndices[0]) == family1->getActorByChunk(desc.chunkIndices[1]);
+ NVBLASTTK_CHECK_ERROR(!actorsAreTheSame, "TkFrameworkImpl::createJoint: the chunks listed in the TkJointDesc must be in different actors.", return nullptr);
+
+ if (family0 != nullptr)
+ {
+ const bool isSupportChunk = !isInvalidIndex(NvBlastAssetGetChunkToGraphNodeMap(family0->getAssetImpl()->getAssetLLInternal(), log)[desc.chunkIndices[0]]);
+ NVBLASTTK_CHECK_ERROR(isSupportChunk, "TkFrameworkImpl::createJoint: desc.chunkIndices[0] is not a support chunk in the asset for desc.families[0]. Joint not created.", return nullptr);
+ handle0 = family0->createExternalJointHandle(getFamilyID(family1), desc.chunkIndices[0], desc.chunkIndices[1]);
+ NVBLASTTK_CHECK_ERROR(handle0 != nullptr, "TkFrameworkImpl::createJoint: could not create joint handle in family[0]. Joint not created.", return nullptr);
+ }
+
+ if (family1 != nullptr)
+ {
+ const bool isSupportChunk = !isInvalidIndex(NvBlastAssetGetChunkToGraphNodeMap(family1->getAssetImpl()->getAssetLLInternal(), log)[desc.chunkIndices[1]]);
+ NVBLASTTK_CHECK_ERROR(isSupportChunk, "TkFrameworkImpl::createJoint: desc.chunkIndices[1] is not a support chunk in the asset for desc.families[1]. Joint not created.", return nullptr);
+ if (family1 != family0)
+ {
+ handle1 = family1->createExternalJointHandle(getFamilyID(family0), desc.chunkIndices[1], desc.chunkIndices[0]);
+ NVBLASTTK_CHECK_ERROR(handle1 != nullptr, "TkFrameworkImpl::createJoint: could not create joint handle in family[1]. Joint not created.", return nullptr);
+ }
+ }
+
+ TkJointImpl* joint = NVBLASTTK_NEW(TkJointImpl)(desc, nullptr);
+ NVBLASTTK_CHECK_ERROR(joint != nullptr, "TkFrameworkImpl::createJoint: failed to create joint.", return nullptr);
+
+ const TkJointData& jointData = joint->getDataInternal();
+
+ if (handle0 != nullptr)
+ {
+ *handle0 = joint;
+ static_cast<TkActorImpl*>(jointData.actors[0])->addJoint(joint->m_links[0]);
+ }
+
+ if (handle1 != nullptr)
+ {
+ *handle1 = joint;
+ if (jointData.actors[0] != jointData.actors[1])
+ {
+ static_cast<TkActorImpl*>(jointData.actors[1])->addJoint(joint->m_links[1]);
+ }
+ }
+
+ return joint;
+}
+
+
+bool TkFrameworkImpl::serializeHeader(const TkSerializable& object, PxFileBuf& stream)
+{
+ const TkTypeImpl& type = static_cast<const TkTypeImpl&>(object.getType());
+
+ // Tk framework identifier
+ stream.storeDword(ClassID);
+
+ // Object header
+ stream.storeDword(type.getID());
+ stream.storeDword(type.getVersionInternal());
+
+ // Object ID
+ stream.write(&object.getID(), sizeof(NvBlastID));
+
+ // Serializable user data
+ stream.storeDword(static_cast<uint32_t>(object.userIntData & 0xFFFFFFFF));
+ stream.storeDword(static_cast<uint32_t>(object.userIntData >> 32));
+
+ return true;
+}
+
+
+void TkFrameworkImpl::onCreate(TkIdentifiable& object)
+{
+ const TkTypeImpl& type = static_cast<const TkTypeImpl&>(object.getType());
+
+ const uint32_t index = type.getIndex();
+
+ if (index >= m_objects.size())
+ {
+ if (!isInvalidIndex(index))
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::addObject: object type unrecognized.");
+ }
+ return;
+ }
+
+ auto& objectArray = m_objects[index];
+ NVBLAST_ASSERT(objectArray.find(&object) == objectArray.end());
+ objectArray.pushBack(&object);
+}
+
+
+void TkFrameworkImpl::onDestroy(TkIdentifiable& object)
+{
+ // remove from id map if present
+ const auto id = object.getID();
+ if (!TkGUIDIsZero(&id))
+ {
+ m_IDToObject.erase(id);
+ }
+
+ // remove from object list
+ const TkTypeImpl& type = static_cast<const TkTypeImpl&>(object.getType());
+
+ const uint32_t index = type.getIndex();
+
+ if (index >= m_objects.size())
+ {
+ if (!isInvalidIndex(index))
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::removeObject: object type unrecognized.");
+ }
+ return;
+ }
+
+ auto& objectArray = m_objects[index];
+ objectArray.findAndReplaceWithLast(&object);
+}
+
+
+void TkFrameworkImpl::onCreate(TkJointImpl& joint)
+{
+ NVBLASTTK_CHECK_ERROR(m_joints.insert(&joint), "TkFrameworkImpl::onCreate: Joint already tracked.", return);
+}
+
+
+void TkFrameworkImpl::onDestroy(TkJointImpl& joint)
+{
+ NVBLASTTK_CHECK_ERROR(m_joints.erase(&joint), "TkFrameworkImpl::onDestroy: Joint not tracked.", return);
+}
+
+
+void TkFrameworkImpl::onIDChange(TkIdentifiable& object, const NvBlastID& IDPrev, const NvBlastID& IDCurr)
+{
+ if (!TkGUIDIsZero(&IDPrev))
+ {
+ if (!m_IDToObject.erase(IDPrev))
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::reportIDChanged: object with previous ID doesn't exist.");
+ }
+ }
+
+ if (!TkGUIDIsZero(&IDCurr))
+ {
+ auto& value = m_IDToObject[IDCurr];
+ if (value != nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkFrameworkImpl::reportIDChanged: object with new ID already exists.");
+ return;
+ }
+ value = &object;
+ }
+}
+
+} // namespace Blast
+} // namespace Nv
+
+
+//////// Global API implementation ////////
+
+Nv::Blast::TkFramework* NvBlastTkFrameworkCreate(const Nv::Blast::TkFrameworkDesc& desc)
+{
+ if (desc.errorCallback == nullptr)
+ {
+ return nullptr;
+ }
+
+ if (desc.allocatorCallback == nullptr)
+ {
+ desc.errorCallback->reportError(PxErrorCode::eINVALID_OPERATION, "TkFramework::create: NULL allocator given in descriptor.", __FILE__, __LINE__);
+ return nullptr;
+ }
+
+ if (Nv::Blast::TkFrameworkImpl::get() != nullptr)
+ {
+ desc.errorCallback->reportError(PxErrorCode::eINVALID_OPERATION, "TkFramework::create: framework already created. Use TkFramework::get() to access.", __FILE__, __LINE__);
+ return nullptr;
+ }
+
+ Nv::Blast::TkAllocator::s_allocatorCallback = desc.allocatorCallback;
+
+ Nv::Blast::TkFrameworkImpl* framework = new (desc.allocatorCallback->allocate(sizeof(Nv::Blast::TkFrameworkImpl), "TkFrameworkImpl", __FILE__, __LINE__)) Nv::Blast::TkFrameworkImpl(desc);
+ Nv::Blast::TkFrameworkImpl::set(framework);
+
+ return Nv::Blast::TkFrameworkImpl::get();
+}
+
+
+Nv::Blast::TkFramework* NvBlastTkFrameworkGet()
+{
+ return Nv::Blast::TkFrameworkImpl::get();
+}
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.h
new file mode 100644
index 0000000..ceeff06
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.h
@@ -0,0 +1,225 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKFRAMEWORKIMPL_H
+#define NVBLASTTKFRAMEWORKIMPL_H
+
+#include "NvBlastTkFramework.h"
+#include "NvBlastProfilerInternal.h"
+
+#include "NvBlastTkCommon.h"
+
+#include "NvBlastTkArray.h"
+#include "NvBlastTkHashMap.h"
+#include "NvBlastTkHashSet.h"
+
+
+//////// Log macros that use the TkFrameworkImpl::log function ////////
+
+#define NVBLASTTK_LOG_ERROR(_msg) NVBLAST_LOG_ERROR(TkFrameworkImpl::log, _msg)
+#define NVBLASTTK_LOG_WARNING(_msg) NVBLAST_LOG_WARNING(TkFrameworkImpl::log, _msg)
+#define NVBLASTTK_LOG_INFO(_msg) NVBLAST_LOG_INFO(TkFrameworkImpl::log, _msg)
+#define NVBLASTTK_LOG_DEBUG(_msg) NVBLAST_LOG_DEBUG(TkFrameworkImpl::log, _msg)
+
+#define NVBLASTTK_CHECK(_expr, _messageType, _msg, _onFail) \
+ { \
+ if(!(_expr)) \
+ { \
+ TkFrameworkImpl::log(_messageType, _msg, __FILE__, __LINE__); \
+ { _onFail; }; \
+ } \
+ }
+
+#define NVBLASTTK_CHECK_ERROR(_expr, _msg, _onFail) NVBLASTTK_CHECK(_expr, NvBlastMessage::Error, _msg, _onFail)
+#define NVBLASTTK_CHECK_WARNING(_expr, _msg, _onFail) NVBLASTTK_CHECK(_expr, NvBlastMessage::Warning, _msg, _onFail)
+#define NVBLASTTK_CHECK_INFO(_expr, _msg, _onFail) NVBLASTTK_CHECK(_expr, NvBlastMessage::Info, _msg, _onFail)
+#define NVBLASTTK_CHECK_DEBUG(_expr, _msg, _onFail) NVBLASTTK_CHECK(_expr, NvBlastMessage::Debug, _msg, _onFail)
+
+
+//////// Allocator macros ////////
+
+#define NVBLASTTK_ALLOC(_size, _name) TkFrameworkImpl::get()->getAllocatorCallbackInternal().allocate(_size, _name, __FILE__, __LINE__)
+#define NVBLASTTK_FREE(_mem) TkFrameworkImpl::get()->getAllocatorCallbackInternal().deallocate(_mem)
+
+#define NVBLASTTK_NEW(T) new (NVBLASTTK_ALLOC(sizeof(T), #T)) T
+#define NVBLASTTK_DELETE(obj, T) \
+ (obj)->~T(); \
+ NVBLASTTK_FREE(obj)
+
+
+
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+// Forward declarations
+class TkTypeImpl;
+class TkJointImpl;
+
+/**
+Implementation of TkFramework
+*/
+class TkFrameworkImpl : public TkFramework
+{
+public:
+ TkFrameworkImpl(const TkFrameworkDesc& desc);
+ ~TkFrameworkImpl();
+
+ // Begin TkFramework
+ virtual void release() override;
+
+ virtual physx::PxErrorCallback& getErrorCallback() const override;
+
+ virtual physx::PxAllocatorCallback& getAllocatorCallback() const override;
+
+ virtual NvBlastLog getLogFn() const override;
+
+ virtual TkSerializable* deserialize(physx::general_PxIOStream2::PxFileBuf& stream) override;
+
+ virtual const TkType* getType(TkTypeIndex::Enum typeIndex) const override;
+
+ virtual TkIdentifiable* findObjectByID(const NvBlastID& id) const override;
+
+ virtual uint32_t getObjectCount(const TkType& type) const override;
+
+ virtual uint32_t getObjects(TkIdentifiable** buffer, uint32_t bufferSize, const TkType& type, uint32_t indexStart = 0) const override;
+
+ virtual bool reorderAssetDescChunks(NvBlastChunkDesc* chunkDescs, uint32_t chunkCount, NvBlastBondDesc* bondDescs, uint32_t bondCount, uint32_t* chunkReorderMap = nullptr) const override;
+
+ virtual bool ensureAssetExactSupportCoverage(NvBlastChunkDesc* chunkDescs, uint32_t chunkCount) const override;
+
+ virtual TkAsset* createAsset(const TkAssetDesc& desc) override;
+
+ virtual TkAsset* createAsset(const NvBlastAsset* assetLL, Nv::Blast::TkAssetJointDesc* jointDescs = nullptr, uint32_t jointDescCount = 0, bool ownsAsset = false) override;
+
+ virtual TkGroup* createGroup(const TkGroupDesc& desc) override;
+
+ virtual TkActor* createActor(const TkActorDesc& desc) override;
+
+ virtual TkJoint* createJoint(const TkJointDesc& desc) override;
+ // End TkFramework
+
+ // Public methods
+ /**
+ Access to the error callback set by the user.
+ */
+ physx::PxErrorCallback& getErrorCallbackInternal() const;
+
+ /**
+ Access to the allocator callback set by the user.
+ */
+ physx::PxAllocatorCallback& getAllocatorCallbackInternal() const;
+
+ /**
+ To be called by any TkIdentifiable object when it is created, so the framework can track it.
+ */
+ void onCreate(TkIdentifiable& object);
+
+ /**
+ To be called by any TkIdentifiable object when it is deleted, so the framework can stop tracking it.
+ */
+ void onDestroy(TkIdentifiable& object);
+
+ /**
+ Special onCreate method for joints, since they are not TkIdentifiable.
+ */
+ void onCreate(TkJointImpl& joint);
+
+ /**
+ Special onDestroy method for joints, since they are not TkIdentifiable.
+ */
+ void onDestroy(TkJointImpl& joint);
+
+ /**
+ Must be called whenever a TkIdentifiable object's ID is changed, so that the framework can associate the new ID with it.
+ */
+ void onIDChange(TkIdentifiable& object, const NvBlastID& IDPrev, const NvBlastID& IDCurr);
+
+ /**
+ Internal (non-virtual) method to find a TkIdentifiable object based upon its NvBlastID.
+ */
+ TkIdentifiable* findObjectByIDInternal(const NvBlastID& id) const;
+
+ /**
+ Serialize a TkSerializable's standard header data, including its type ID, type version, object ID, and TkObject::userIntData.
+ */
+ bool serializeHeader(const TkSerializable& object, physx::general_PxIOStream2::PxFileBuf& stream);
+
+ // Access to singleton
+
+ /** Retrieve the global singleton. */
+ static TkFrameworkImpl* get();
+
+ /** Set the global singleton, if it's not already set, or set it to NULL. Returns true iff successful. */
+ static bool set(TkFrameworkImpl* framework);
+
+ // Blast LL context functions
+ static void log(int type, const char* msg, const char* file, int line); //!< A function with the NvBlastLog signature which can be used in NvBlast low-level function calls
+ static void* alloc(size_t size); //!< A function with the std::malloc signature which returns 16-byte aligned memory
+ static void free(void* mem); //!< A function with the std::free signature which can deallocate memory created by alloc
+
+private:
+ // Enums
+ enum { Alignment = 16 }; //!< Memory alignment used for allocations
+ enum { ClassID = NVBLASTTK_FOURCC('T', 'K', 'F', 'W') }; //!< TkFramework identifier token, used in serialization
+
+ // Static data
+ static TkFrameworkImpl* s_framework; //!< Global (singleton) object pointer
+
+ // Callbacks
+ physx::PxErrorCallback* m_errorCallback; //!< User-supplied error callback
+ physx::PxAllocatorCallback* m_allocatorCallback; //!< User-supplied allocator callback
+
+ // Types
+ TkInlineArray<const TkTypeImpl*, TkTypeIndex::TypeCount>::type m_types; //!< TkIdentifiable static type data
+ TkHashMap<uint32_t, uint32_t>::type m_typeIDToIndex; //!< Map to type data keyed by ClassID
+
+ // Objects and object names
+ TkHashMap<NvBlastID, TkIdentifiable*>::type m_IDToObject; //!< Map to all TkIdentifiable objects, keyed by NvBlastID
+ TkInlineArray<TkArray<TkIdentifiable*>::type, TkTypeIndex::TypeCount>::type m_objects; //!< Catalog of all TkIdentifiable objects, grouped by type. (Revisit implementation.)
+
+ // Track external joints (to do: make this a pool)
+ TkHashSet<TkJointImpl*>::type m_joints; //!< All internal joints
+};
+
+
+//////// TkFrameworkImpl inline methods ////////
+
+NV_INLINE physx::PxErrorCallback& TkFrameworkImpl::getErrorCallbackInternal() const
+{
+ return *m_errorCallback;
+}
+
+
+NV_INLINE physx::PxAllocatorCallback& TkFrameworkImpl::getAllocatorCallbackInternal() const
+{
+ return *m_allocatorCallback;
+}
+
+
+NV_INLINE TkIdentifiable* TkFrameworkImpl::findObjectByIDInternal(const NvBlastID& id) const
+{
+ const auto entry = m_IDToObject.find(id);
+ if (entry == nullptr)
+ {
+ return nullptr;
+ }
+
+ return entry->second;
+}
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTTKFRAMEWORKIMPL_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkGUID.h b/NvBlast/sdk/toolkit/source/NvBlastTkGUID.h
new file mode 100644
index 0000000..a770092
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkGUID.h
@@ -0,0 +1,135 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKGUID_H
+#define NVBLASTTKGUID_H
+
+#include "NvPreprocessor.h"
+
+#if NV_WINDOWS_FAMILY
+#include <rpc.h>
+#else
+//#include <uuid/uuid.h>
+#include "NvBlastTime.h"
+#endif
+
+#include "PsHash.h"
+
+namespace Nv
+{
+namespace Blast
+{
+
+#if NV_WINDOWS_FAMILY
+
+NV_INLINE NvBlastID TkGenerateGUID(void* ptr)
+{
+ NV_UNUSED(ptr);
+
+ NV_COMPILE_TIME_ASSERT(sizeof(UUID) == sizeof(NvBlastID));
+
+ NvBlastID guid;
+ UuidCreate(reinterpret_cast<UUID*>(&guid));
+
+ return guid;
+}
+
+#else
+
+NV_INLINE NvBlastID TkGenerateGUID(void* ptr)
+{
+// NV_COMPILE_TIME_ASSERT(sizeof(uuid_t) == sizeof(NvBlastID));
+ Time time;
+
+ NvBlastID guid;
+ // uuid_generate_random(reinterpret_cast<uuid_t&>(guid));
+
+ *reinterpret_cast<uint64_t*>(guid.data) = reinterpret_cast<uintptr_t>(ptr);
+ *reinterpret_cast<int64_t*>(guid.data + 8) = time.getLastTickCount();
+
+ return guid;
+}
+
+#endif
+
+
+/**
+Compares two NvBlastIDs.
+
+\param[in] id1 A pointer to the first id to compare.
+\param[in] id2 A pointer to the second id to compare.
+
+\return true iff ids are equal.
+*/
+NV_INLINE bool TkGUIDsEqual(const NvBlastID* id1, const NvBlastID* id2)
+{
+ return !memcmp(id1, id2, sizeof(NvBlastID));
+}
+
+
+/**
+Clears an NvBlastID (sets all of its fields to zero).
+
+\param[out] id A pointer to the ID to clear.
+*/
+NV_INLINE void TkGUIDReset(NvBlastID* id)
+{
+ memset(id, 0, sizeof(NvBlastID));
+}
+
+
+/**
+Tests an NvBlastID to determine if it's zeroed. After calling TkGUIDReset
+on an ID, passing it to this function will return a value of true.
+
+\param[in] id A pointer to the ID to test.
+*/
+NV_INLINE bool TkGUIDIsZero(const NvBlastID* id)
+{
+ return *reinterpret_cast<const uint64_t*>(&id->data[0]) == 0 && *reinterpret_cast<const uint64_t*>(&id->data[8]) == 0;
+}
+
+} // namespace Blast
+} // namespace Nv
+
+
+namespace physx
+{
+namespace shdfnd
+{
+
+// hash specialization for NvBlastID
+template <>
+struct Hash<NvBlastID>
+{
+ uint32_t operator()(const NvBlastID& k) const
+ {
+ // "DJB" string hash
+ uint32_t h = 5381;
+ for (uint32_t i = 0; i < sizeof(k.data) / sizeof(k.data[0]); ++i)
+ h = ((h << 5) + h) ^ uint32_t(k.data[i]);
+ return h;
+ }
+ bool equal(const NvBlastID& k0, const NvBlastID& k1) const
+ {
+ return Nv::Blast::TkGUIDsEqual(&k0, &k1);
+ }
+};
+
+} // namespace shdfnd
+} // namespace physx
+
+
+#endif // #ifndef NVBLASTTKGUID_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.cpp
new file mode 100644
index 0000000..d9a4b29
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.cpp
@@ -0,0 +1,592 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#include "NvPreprocessor.h"
+
+#include "NvBlastAssert.h"
+#include "NvBlast.h"
+
+#include "NvBlastTkFrameworkImpl.h"
+#include "NvBlastTkGroupImpl.h"
+#include "NvBlastTkActorImpl.h"
+#include "NvBlastTkFamilyImpl.h"
+#include "NvBlastTkAssetImpl.h"
+#include "NvBlastTkTaskImpl.h"
+
+#include "Px.h"
+#include "PxFileBuf.h"
+#include "PxAllocatorCallback.h"
+#include "task/PxCpuDispatcher.h"
+
+#undef max
+#undef min
+#include <algorithm>
+
+using namespace physx;
+using namespace physx::general_PxIOStream2;
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+//////// Static data ////////
+
+NVBLASTTK_DEFINE_TYPE_IDENTIFIABLE(Group);
+
+
+//////// Local (static) functions ////////
+
+static uint32_t getNumThreads(PxTaskManager* tm)
+{
+ if (tm == nullptr) return 0;
+ if (tm->getCpuDispatcher() == nullptr) return 0;
+ return tm->getCpuDispatcher()->getWorkerCount();
+}
+
+
+//////// Member functions ////////
+
+TkGroupImpl::TkGroupImpl() : m_actorCount(0), m_isProcessing(false), m_sync(0)
+{
+#if NV_PROFILE
+ memset(&m_stats, 0, sizeof(TkGroupStats));
+#endif
+}
+
+
+TkGroupImpl::~TkGroupImpl()
+{
+ NVBLAST_ASSERT(getActorCount() == 0);
+ NVBLAST_ASSERT(m_sharedMemory.size() == 0);
+}
+
+
+void TkGroupImpl::release()
+{
+ if (isProcessing())
+ {
+ // abort all processing?
+ NVBLASTTK_LOG_ERROR("TkGroup::release: cannot release Group while processing.");
+ NVBLAST_ALWAYS_ASSERT_MESSAGE("TkGroup::release: cannot release Group while processing.");
+ return;
+ }
+
+ for (auto it = m_sharedMemory.getIterator(); !it.done(); ++it)
+ {
+ TkFamilyImpl* family = it->first;
+ for (TkActorImpl& actor : family->getActorsInternal())
+ {
+ if (actor.m_group == this)
+ {
+ removeActorInternal(actor);
+ }
+ }
+ SharedMemory* mem = it->second;
+ mem->release();
+ NVBLASTTK_DELETE(mem, SharedMemory);
+ }
+ m_sharedMemory.clear();
+
+ m_bondTempDataBlock.release();
+ m_chunkTempDataBlock.release();
+ m_bondEventDataBlock.release();
+ m_chunkEventDataBlock.release();
+ m_splitScratchBlock.release();
+
+ NVBLASTTK_DELETE(this, TkGroupImpl);
+}
+
+
+void TkGroupImpl::addActorsInternal(TkActorImpl** actors, uint32_t numActors)
+{
+ for (uint32_t i = 0; i < numActors; i++)
+ {
+ addActorInternal(*actors[i]);
+ }
+}
+
+
+void TkGroupImpl::addActorInternal(TkActorImpl& tkActor)
+{
+ NVBLAST_ASSERT(tkActor.getGroup() == nullptr);
+ tkActor.m_group = this;
+ m_actorCount++;
+}
+
+
+bool TkGroupImpl::addActor(TkActor& actor)
+{
+ TkActorImpl& tkActor = static_cast<TkActorImpl&>(actor);
+ if (tkActor.getGroup() != nullptr)
+ {
+ NVBLASTTK_LOG_ERROR("TkGroup::addActor: actor already belongs to a Group. Remove from current group first.");
+ return false;
+ }
+
+ if (isProcessing())
+ {
+ NVBLASTTK_LOG_ERROR("TkGroup::addActor: cannot alter Group while processing.");
+ return false;
+ }
+
+ // mark the actor that it now belongs to this group
+ addActorInternal(tkActor);
+
+ // actors that were fractured already or have damage requested
+ // must be enqueued to be processed
+ if (tkActor.isPending())
+ {
+ enqueue(&tkActor);
+ }
+
+ TkFamilyImpl& family = tkActor.getFamilyImpl();
+ SharedMemory* mem = m_sharedMemory[&family];
+ if (mem == nullptr)
+ {
+ // the actor belongs to a family not involved in this group yet
+ // shared memory must be allocated and temporary buffers adjusted accordingly
+
+ PERF_ZONE_BEGIN("family memory");
+ mem = NVBLASTTK_NEW(SharedMemory);
+ mem->allocate(family);
+ m_sharedMemory[&family] = mem;
+ PERF_ZONE_END("family memory");
+
+ PERF_ZONE_BEGIN("group memory");
+
+ const uint32_t numThreads = getNumThreads(m_pxTaskManager);
+ // one worker always exists, even if it is the main thread (when numThreads is 0)
+ const uint32_t numWorkers = std::max(numThreads, (uint32_t)1);
+
+ // the number of threads could have changed, however this is unexpected and handled in process()
+
+
+ NvBlastLog theLog = TkFrameworkImpl::get()->log;
+
+ // this group's tasks will use one temporary buffer each, which is of max size of, for all families involved
+ const size_t requiredScratch = NvBlastActorGetRequiredScratchForSplit(tkActor.getActorLL(), theLog);
+ if (static_cast<size_t>(m_splitScratchBlock.numElementsPerBlock()) < requiredScratch)
+ {
+ m_splitScratchBlock.release();
+ m_splitScratchBlock.allocate(static_cast<uint32_t>(requiredScratch), numWorkers);
+ }
+
+ // generate and apply fracture may create an entry for each bond
+ const uint32_t bondCount = NvBlastAssetGetBondCount(tkActor.getAsset()->getAssetLL(), theLog);
+ if (m_bondTempDataBlock.numElementsPerBlock() < bondCount)
+ {
+ m_bondTempDataBlock.release();
+ m_bondTempDataBlock.allocate(bondCount, numWorkers);
+ m_bondEventDataBlock.release();
+ m_bondEventDataBlock.allocate(bondCount, numWorkers);
+ }
+
+ // apply fracture may create an entry for each lower-support chunk
+ const uint32_t graphNodeCount = NvBlastAssetGetSupportGraph(tkActor.getAsset()->getAssetLL(), theLog).nodeCount;
+ const uint32_t subsupportChunkCount
+ = NvBlastAssetGetChunkCount(tkActor.getAsset()->getAssetLL(), theLog)
+ - NvBlastAssetGetFirstSubsupportChunkIndex(tkActor.getAsset()->getAssetLL(), theLog);
+ const uint32_t chunkCount = graphNodeCount + subsupportChunkCount;
+ if (m_chunkTempDataBlock.numElementsPerBlock() < chunkCount)
+ {
+ m_chunkTempDataBlock.release();
+ m_chunkTempDataBlock.allocate(chunkCount, numWorkers);
+ m_chunkEventDataBlock.release();
+ m_chunkEventDataBlock.allocate(chunkCount, numWorkers);
+ }
+ PERF_ZONE_END("group memory");
+ }
+ mem->addReference();
+
+ return true;
+}
+
+
+uint32_t TkGroupImpl::getActors(TkActor** buffer, uint32_t bufferSize, uint32_t indexStart /* = 0 */) const
+{
+ PERF_SCOPE_L("TkGroup::getActors");
+
+ uint32_t actorCount = m_actorCount;
+ if (actorCount <= indexStart)
+ {
+ NVBLASTTK_LOG_WARNING("TkGroup::getActors: indexStart beyond end of actor list.");
+ return 0;
+ }
+
+ actorCount -= indexStart;
+ if (actorCount > bufferSize)
+ {
+ actorCount = bufferSize;
+ }
+
+ uint32_t index = 0;
+ bool done = false;
+ for (auto it = const_cast<TkGroupImpl*>(this)->m_sharedMemory.getIterator(); !it.done();++it)
+ {
+ TkFamilyImpl* fam = it->first;
+ for (TkActorImpl& actor : fam->getActorsInternal())
+ {
+ if (actor.m_group == this)
+ {
+ NVBLAST_ASSERT(actor.isActive());
+
+ if (index >= indexStart)
+ {
+ *buffer++ = &actor;
+ }
+
+ index++;
+ done = (index - indexStart) >= actorCount;
+ }
+ if (done) break;
+ }
+ if (done) break;
+ }
+
+ return actorCount;
+}
+
+
+void TkGroupImpl::removeActorInternal(TkActorImpl& tkActor)
+{
+ NVBLAST_ASSERT(tkActor.m_group == this);
+ tkActor.m_group = nullptr;
+ m_actorCount--;
+}
+
+
+void TkGroupImpl::releaseSharedMemory(TkFamilyImpl* fam, SharedMemory* mem)
+{
+ NVBLAST_ASSERT(mem != nullptr && m_sharedMemory[fam] == mem);
+ mem->release();
+ m_sharedMemory.erase(fam);
+ NVBLASTTK_DELETE(mem, SharedMemory);
+}
+
+
+bool TkGroupImpl::removeActor(TkActor& actor)
+{
+ TkActorImpl& tkActor = static_cast<TkActorImpl&>(actor);
+
+ if (tkActor.getGroup() != this)
+ {
+ NVBLASTTK_LOG_ERROR("TkGroup::removeActor: actor does not belong to this Group.");
+ return false;
+ }
+
+ if (isProcessing())
+ {
+ NVBLASTTK_LOG_ERROR("TkGroup::removeActor: cannot alter Group while processing.");
+ return false;
+ }
+
+ removeActorInternal(tkActor);
+
+ // pending actors must be removed from the job queue as well
+ if(tkActor.isPending())
+ {
+ uint32_t index = tkActor.m_groupJobIndex;
+ tkActor.m_groupJobIndex = invalidIndex<uint32_t>();
+ m_jobs.replaceWithLast(index);
+ if (index < m_jobs.size())
+ {
+ NVBLAST_ASSERT(m_jobs[index].m_tkActor->m_groupJobIndex == m_jobs.size());
+ NVBLAST_ASSERT(m_jobs[index].m_tkActor->isPending());
+ m_jobs[index].m_tkActor->m_groupJobIndex = index;
+ }
+ }
+
+ // if the actor is the last of its family in this group
+ // the group-family memory can be released
+ TkFamilyImpl* family = &tkActor.getFamilyImpl();
+ SharedMemory* mem = getSharedMemory(family);
+ if (mem->removeReference())
+ {
+ releaseSharedMemory(family, mem);
+ }
+
+ return true;
+}
+
+
+TkGroupImpl* TkGroupImpl::create(const TkGroupDesc& desc)
+{
+ if (desc.pxTaskManager == nullptr)
+ {
+ NVBLASTTK_LOG_WARNING("TkGroup::create: attempting to create a Group with a NULL pxTaskManager.");
+ }
+
+ TkGroupImpl* group = NVBLASTTK_NEW(TkGroupImpl);
+
+ group->m_pxTaskManager = desc.pxTaskManager;
+ group->m_initialNumThreads = getNumThreads(group->m_pxTaskManager);
+
+ return group;
+}
+
+
+bool TkGroupImpl::process()
+{
+ PERF_SCOPE_L("TkGroup::process");
+
+ if (!setProcessing(true))
+ {
+ NVBLASTTK_LOG_WARNING("TkGroup::process: Group is still processing, call TkGroup::sync first.");
+ return false;
+ }
+
+ if (m_jobs.size() > 0)
+ {
+ PERF_ZONE_BEGIN("task setup");
+
+ PERF_ZONE_BEGIN("task memory");
+ const uint32_t numThreads = getNumThreads(m_pxTaskManager);
+ // one worker always exists, even if it is the main thread (when numThreads is 0)
+ const uint32_t numWorkers = std::max(numThreads, (uint32_t)1);
+
+ if (numThreads != m_initialNumThreads)
+ {
+ NVBLASTTK_LOG_WARNING("TkGroup::process: number of threads has changed, memory is being reallocated.");
+ m_initialNumThreads = numThreads;
+
+ const uint32_t bondCount = m_bondTempDataBlock.numElementsPerBlock();
+ if (bondCount > 0)
+ {
+ m_bondTempDataBlock.release();
+ m_bondTempDataBlock.allocate(bondCount, numWorkers);
+ m_bondEventDataBlock.release();
+ m_bondEventDataBlock.allocate(bondCount, numWorkers);
+ }
+ const uint32_t chunkCount = m_chunkTempDataBlock.numElementsPerBlock();
+ m_chunkTempDataBlock.release();
+ m_chunkTempDataBlock.allocate(chunkCount, numWorkers);
+ m_chunkEventDataBlock.release();
+ m_chunkEventDataBlock.allocate(chunkCount, numWorkers);
+ const uint32_t scratchSize = m_splitScratchBlock.numElementsPerBlock();
+ m_splitScratchBlock.release();
+ m_splitScratchBlock.allocate(scratchSize, numWorkers);
+ }
+ PERF_ZONE_END("task memory");
+
+
+ PERF_ZONE_BEGIN("setup job queue");
+ for (const auto& job : m_jobs)
+ {
+ const TkActorImpl* a = job.m_tkActor;
+ SharedMemory* mem = getSharedMemory(&a->getFamilyImpl());
+
+ const uint32_t damageCount = a->m_damageBuffer.size();
+
+ // applyFracture'd actor do not necessarily have damage queued
+ NVBLAST_ASSERT(damageCount > 0 || a->m_flags.isSet(TkActorFlag::DAMAGED));
+
+ // no reason to be here without these
+ NVBLAST_ASSERT(a->m_flags.isSet(TkActorFlag::PENDING));
+ NVBLAST_ASSERT(a->m_group == this);
+
+ // collect the amount of event payload memory to preallocate for TkWorkers
+ mem->m_eventsMemory += damageCount * (sizeof(TkFractureCommands) + sizeof(TkFractureEvents)) + sizeof(TkSplitEvent);
+
+ // collect the amount of event entries to preallocate for TkWorkers
+ // (two TkFracture* events per damage plus one TkSplitEvent)
+ mem->m_eventsCount += 2 * damageCount + 1;
+ }
+ PERF_ZONE_END("setup job queue");
+
+ PERF_ZONE_BEGIN("memory protect");
+ for (auto it = m_sharedMemory.getIterator(); !it.done(); ++it)
+ {
+ // preallocate the event memory for TkWorkers
+ SharedMemory* mem = it->second;
+ mem->m_events.reserveData(mem->m_eventsMemory);
+ mem->m_events.reserveEvents(mem->m_eventsCount);
+
+ // these counters are not used anymore
+ // reset them immediately for next time
+ mem->m_eventsCount = 0;
+ mem->m_eventsMemory = 0;
+
+ // switch to parallel mode
+ mem->m_events.protect(true);
+ }
+ PERF_ZONE_END("memory protect");
+
+ PERF_ZONE_END("task setup");
+
+ // ready queue for the workers
+ const uint32_t numJobs = m_jobs.size();
+ m_jobQueue.init(m_jobs.begin(), numJobs);
+
+ // do not start more workers than there are jobs
+ const uint32_t workersToRun = std::min(numWorkers, numJobs);
+ m_workers.resize(workersToRun);
+ m_sync.setCount(workersToRun);
+
+ uint32_t workerId = 0;
+ if (numThreads > 0)
+ {
+ for (auto& task : m_workers)
+ {
+ PERF_SCOPE_M("task release");
+ task.m_id = workerId++;
+ task.m_group = this;
+ task.setContinuation(*m_pxTaskManager, nullptr);
+ // mind m_sync.setCount above, immediately removing reference would not work with a continuation task
+ task.removeReference();
+ }
+ }
+ else
+ {
+ // let this thread do the work
+ NVBLAST_ASSERT(m_workers.size() == 1);
+ for (auto& task : m_workers)
+ {
+ task.m_id = workerId++;
+ task.m_group = this;
+ task.run();
+ task.release();
+ }
+ }
+ }
+
+
+ return true;
+}
+
+
+bool TkGroupImpl::sync(bool block /*= true*/)
+{
+ if (!m_sync.isDone() && block)
+ {
+ PERF_SCOPE_L("TkGroupImpl::sync wait");
+ m_sync.wait();
+ }
+
+ if (isProcessing() && m_sync.isDone())
+ {
+ PERF_SCOPE_L("TkGroupImpl::sync finalize");
+
+ if (m_jobs.size() > 0)
+ {
+#if NV_PROFILE
+ PERF_ZONE_BEGIN("accumulate timers");
+ NvBlastTimers accumulated;
+ NvBlastTimersReset(&accumulated);
+ uint32_t jobCount = 0;
+ int64_t workerTime = 0;
+ for (TkWorker& worker : m_workers)
+ {
+ accumulated += worker.m_stats.timers;
+ jobCount += worker.m_stats.processedActorsCount;
+ workerTime += worker.m_stats.workerTime;
+ }
+ m_stats.timers = accumulated;
+ m_stats.processedActorsCount = jobCount;
+ m_stats.workerTime = workerTime;
+ PERF_ZONE_END("accumulate timers");
+#endif
+
+ PERF_ZONE_BEGIN("job update");
+ for (auto& j : m_jobs)
+ {
+ if (j.m_newActorsCount)
+ {
+ TkFamilyImpl* fam = &j.m_tkActor->getFamilyImpl();
+ SharedMemory* mem = getSharedMemory(fam);
+
+ // as LL is implemented, where newActorsCount the parent is always deleted
+ removeActorInternal(*j.m_tkActor);
+ mem->removeReference();
+ addActorsInternal(j.m_newActors, j.m_newActorsCount);
+ mem->addReference(j.m_newActorsCount);
+
+ // Update joints
+ mem->m_events.protect(false); // allow allocations again
+ fam->updateJoints(j.m_tkActor, &mem->m_events);
+ }
+
+ // virtually dequeue the actor
+ // the queue itself is cleared right after this loop
+ j.m_tkActor->m_flags.clear(TkActorFlag::PENDING);
+ j.m_tkActor->m_groupJobIndex = invalidIndex<uint32_t>();
+ j.m_tkActor->m_damageBuffer.clear();
+ }
+ m_jobs.clear();
+ PERF_ZONE_END("job update");
+
+ PERF_ZONE_BEGIN("event dispatch");
+ for (auto it = m_sharedMemory.getIterator(); !it.done(); ++it)
+ {
+ TkFamilyImpl* family = it->first;
+ SharedMemory* mem = it->second;
+
+ NVBLAST_ASSERT(family != nullptr);
+ NVBLAST_ASSERT(mem != nullptr && mem->isUsed());
+
+ // where no actor of a family has split,
+ // its group/family event queue has not been
+ // unprotected in the jobs loop above
+ mem->m_events.protect(false);
+
+ family->getQueue().dispatch(mem->m_events);
+
+ mem->m_events.reset();
+ mem->reset();
+ }
+ PERF_ZONE_END("event dispatch");
+
+ PERF_ZONE_BEGIN("event memory release");
+ for (auto& worker : m_workers)
+ {
+ worker.m_bondBuffer.clear();
+ worker.m_chunkBuffer.clear();
+ }
+ PERF_ZONE_END("event memory release");
+ }
+
+ bool success = setProcessing(false);
+ NVBLAST_ASSERT(success);
+ return success;
+ }
+
+ return false;
+}
+
+
+bool TkGroupImpl::setProcessing(bool value)
+{
+ bool expected = !value;
+ return m_isProcessing.compare_exchange_strong(expected, value);
+}
+
+
+void TkGroupImpl::enqueue(TkActorImpl* tkActor)
+{
+ NVBLAST_ASSERT(tkActor->getGroupImpl() != nullptr);
+ NVBLAST_ASSERT(tkActor->getGroupImpl() == this);
+ NVBLAST_ASSERT(isInvalidIndex(tkActor->m_groupJobIndex));
+ NVBLAST_ASSERT(isProcessing() == false);
+#if NV_DEBUG
+ for (TkWorkerJob& j : m_jobs)
+ {
+ NVBLAST_ASSERT(j.m_tkActor != tkActor);
+ }
+#endif
+
+ tkActor->m_groupJobIndex = m_jobs.size();
+ TkWorkerJob& j = m_jobs.insert();
+ j.m_tkActor = tkActor;
+}
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.h
new file mode 100644
index 0000000..db7e7c1
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.h
@@ -0,0 +1,174 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKGROUPIMPL_H
+#define NVBLASTTKGROUPIMPL_H
+
+
+#include "NvBlastTkTaskImpl.h"
+#include "NvBlastTkGroup.h"
+#include "NvBlastTkTypeImpl.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+class TkActorImpl;
+class TkFamilyImpl;
+
+NVBLASTTK_IMPL_DECLARE(Group)
+{
+ ~TkGroupImpl();
+
+public:
+ TkGroupImpl();
+
+ NVBLASTTK_IMPL_DEFINE_IDENTIFIABLE('G', 'R', 'P', '\0');
+
+ static TkGroupImpl* create(const TkGroupDesc& desc);
+
+ // Begin TkGroup
+ virtual bool addActor(TkActor& actor) override;
+
+ virtual uint32_t getActorCount() const override;
+
+ virtual uint32_t getActors(TkActor** buffer, uint32_t bufferSize, uint32_t indexStart = 0) const override;
+
+ virtual bool process() override;
+
+ virtual bool sync(bool block = true) override;
+
+ virtual void getStats(TkGroupStats& stats) const override;
+ // End TkGroup
+
+ // TkGroupImpl API
+
+ /**
+ Remove the actor from this group if the actor actually belongs to it and the group is not processing.
+
+ \param[in] actor The TkActor to remove.
+
+ \return true if removing succeeded, false otherwise
+ */
+ bool removeActor(TkActor& actor);
+
+ /**
+ Add the actor to this group's job queue.
+ It is the caller's responsibility to add an actor only once. This condition is checked in debug builds.
+ */
+ void enqueue(TkActorImpl* tkActor);
+
+ /**
+ Atomically check if this group is processing actors. @see setProcessing()
+
+ \return true between process() and sync() calls, false otherwise
+ */
+ bool isProcessing() const;
+
+private:
+ /**
+ Atomically set the processing state. This function checks for the current state
+ before changing it. @see isProcessing()
+
+ \param[in] value the value of the new state
+
+ \return true if the new state could be set, false otherwise
+ */
+ bool setProcessing(bool value);
+
+ /**
+ Get the group-family shared memory for the specified family. To be used when the memory is expected to already exist.
+ */
+ SharedMemory* getSharedMemory(TkFamilyImpl* family);
+ void releaseSharedMemory(TkFamilyImpl* fam, SharedMemory* mem);
+
+ // functions to add/remove actors _without_ group-family memory management
+ void addActorInternal(TkActorImpl& tkActor);
+ void addActorsInternal(TkActorImpl** actors, uint32_t numActors);
+ void removeActorInternal(TkActorImpl& tkActor);
+
+
+ physx::PxTaskManager* m_pxTaskManager; //!< the task manager used to dispatch workers
+ uint32_t m_initialNumThreads; //!< tracks the number of worker threads
+
+ uint32_t m_actorCount; //!< number of actors in this group
+
+ TkHashMap<TkFamilyImpl*, SharedMemory*>::type m_sharedMemory; //!< memory sharable by actors in the same family in this group
+
+ // it is assumed no more than the asset's number of bond and chunks fracture commands are produced
+ SharedBlock<NvBlastChunkFractureData> m_chunkTempDataBlock; //!< chunk data for damage/fracture
+ SharedBlock<NvBlastBondFractureData> m_bondTempDataBlock; //!< bond data for damage/fracture
+ SharedBlock<NvBlastChunkFractureData> m_chunkEventDataBlock; //!< initial memory block for event data
+ SharedBlock<NvBlastBondFractureData> m_bondEventDataBlock; //!< initial memory block for event data
+ SharedBlock<char> m_splitScratchBlock; //!< split scratch memory
+
+ std::atomic<bool> m_isProcessing; //!< true while workers are processing
+ TaskSync m_sync; //!< keeps track of finished workers
+
+ TkArray<TkWorker>::type m_workers; //!< this group's workers
+ TkAtomicJobQueue m_jobQueue; //!< shared job queue for workers
+
+ TkArray<TkWorkerJob>::type m_jobs; //!< this group's process jobs
+
+//#if NV_PROFILE
+ TkGroupStats m_stats; //!< accumulated group's worker stats
+//#endif
+
+ friend class TkWorker;
+};
+
+
+NV_INLINE bool TkGroupImpl::isProcessing() const
+{
+ return m_isProcessing.load();
+}
+
+
+NV_INLINE void TkGroupImpl::getStats(TkGroupStats& stats) const
+{
+#if NV_PROFILE
+ memcpy(&stats, &m_stats, sizeof(TkGroupStats));
+#else
+ NV_UNUSED(stats);
+#endif
+}
+
+
+NV_INLINE uint32_t TkGroupImpl::getActorCount() const
+{
+ return m_actorCount;
+}
+
+
+NV_INLINE SharedMemory* TkGroupImpl::getSharedMemory(TkFamilyImpl* family)
+{
+ SharedMemory* mem = m_sharedMemory[family];
+ NVBLAST_ASSERT(mem != nullptr);
+ return mem;
+}
+
+
+NV_FORCE_INLINE void operator +=(NvBlastTimers& lhs, const NvBlastTimers& rhs)
+{
+ lhs.material += rhs.material;
+ lhs.fracture += rhs.fracture;
+ lhs.island += rhs.fracture;
+ lhs.partition += rhs.partition;
+ lhs.visibility += rhs.visibility;
+}
+
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTTKGROUPIMPL_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkHashMap.h b/NvBlast/sdk/toolkit/source/NvBlastTkHashMap.h
new file mode 100644
index 0000000..14730c6
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkHashMap.h
@@ -0,0 +1,34 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKHASHMAP_H
+#define NVBLASTTKHASHMAP_H
+
+
+#include "NvBlastTkAllocator.h"
+#include "PsHashMap.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+template <class Key, class Value, class HashFn = physx::shdfnd::Hash<Key>>
+struct TkHashMap
+{
+ typedef physx::shdfnd::HashMap<Key, Value, HashFn, TkAllocator> type;
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // #ifndef NVBLASTTKHASHMAP_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkHashSet.h b/NvBlast/sdk/toolkit/source/NvBlastTkHashSet.h
new file mode 100644
index 0000000..4a3a3a0
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkHashSet.h
@@ -0,0 +1,34 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKHASHSET_H
+#define NVBLASTTKHASHSET_H
+
+
+#include "NvBlastTkAllocator.h"
+#include "PsHashSet.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+template <class Key, class HashFn = physx::shdfnd::Hash<Key>>
+struct TkHashSet
+{
+ typedef physx::shdfnd::HashSet<Key, HashFn, TkAllocator> type;
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // #ifndef NVBLASTTKHASHSET_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.cpp
new file mode 100644
index 0000000..46d6378
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.cpp
@@ -0,0 +1,183 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#include "NvBlastTkFrameworkImpl.h"
+#include "NvBlastTkJointImpl.h"
+#include "NvBlastTkActorImpl.h"
+#include "NvBlastTkAssetImpl.h"
+#include "NvBlastTkFamilyImpl.h"
+
+#include "Px.h"
+#include "PxFileBuf.h"
+#include "PxAllocatorCallback.h"
+
+using namespace physx::general_PxIOStream2;
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+//////// Member functions ////////
+
+TkJointImpl::TkJointImpl(const TkJointDesc& desc, TkFamilyImpl* owner) : m_owner(owner)
+{
+ userData = nullptr;
+
+ // Do not fire off a creation event. Creation events will only be fired when a family-internal joint is created.
+ NVBLAST_ASSERT(desc.families[0] != nullptr || desc.families[1] != nullptr);
+ NVBLAST_ASSERT(desc.families[0] == nullptr || desc.chunkIndices[0] < static_cast<TkFamilyImpl*>(desc.families[0])->getAssetImpl()->getChunkCount());
+ NVBLAST_ASSERT(desc.attachPositions[0].isFinite());
+ NVBLAST_ASSERT(desc.families[1] == nullptr || desc.chunkIndices[1] < static_cast<TkFamilyImpl*>(desc.families[1])->getAssetImpl()->getChunkCount());
+ NVBLAST_ASSERT(desc.attachPositions[1].isFinite());
+
+ for (int i = 0; i < 2; ++i)
+ {
+ m_data.actors[i] = desc.families[i] != nullptr ? static_cast<TkFamilyImpl*>(desc.families[i])->getActorByChunk(desc.chunkIndices[i]) : nullptr;
+ m_data.chunkIndices[i] = desc.chunkIndices[i];
+ m_data.attachPositions[i] = desc.attachPositions[i];
+ m_links[i].m_joint = this;
+ }
+
+ if (owner == nullptr)
+ {
+ TkFrameworkImpl::get()->onCreate(*this);
+ }
+}
+
+
+void TkJointImpl::release()
+{
+ removeReferencesInActors();
+
+ if (m_owner != nullptr)
+ {
+ // Internal joint
+ m_owner->releaseJoint(*this);
+ }
+ else
+ {
+ // External joint
+ removeReferencesInFamilies();
+ TkFrameworkImpl::get()->onDestroy(*this);
+ NVBLASTTK_DELETE(this, TkJointImpl);
+ }
+}
+
+
+void TkJointImpl::setActors(TkActorImpl* actor0, TkActorImpl* actor1, TkEventQueue* alternateQueue)
+{
+ NVBLAST_ASSERT(m_data.actors[0] != nullptr || m_data.actors[1] != nullptr);
+
+ const bool unreferenced = (actor0 == nullptr && m_data.actors[0] != nullptr) || (actor1 == nullptr && m_data.actors[1] != nullptr);
+
+ removeReferencesInActors();
+
+ if (!unreferenced)
+ {
+ if (actor0 != nullptr)
+ {
+ actor0->addJoint(m_links[0]);
+ }
+
+ if (actor1 != nullptr && actor1 != actor0) // If the actors are the same, we only need one joint reference
+ {
+ actor1->addJoint(m_links[1]);
+ }
+ }
+
+ // We do _not_ return if m_data.m_actors[0] == actor0 && m_data.m_actors[1] == actor1 since
+ // this leads to a bug. This function will only be called when an actor is split. It is
+ // possible that the two TkActors in a joint are the same as before, but in this case one
+ // of the actors will be the split actor. Since will be represented by a different
+ // physical actor, this case still needs to be reported in an event. Returning when neither
+ // TkActor has changed will prevent that, and lead to unwanted joint disconnection.
+
+ const uint32_t familyToUse = m_data.actors[0] != actor0 ? 0 : 1;
+
+ TkEventQueue* q = alternateQueue == nullptr ?
+ &static_cast<TkActorImpl*>(m_data.actors[familyToUse])->getFamilyImpl().getQueue()
+ : alternateQueue;
+
+ const bool jointWasInternal = m_data.actors[0] == m_data.actors[1];
+
+ if (unreferenced)
+ {
+ removeReferencesInFamilies();
+ actor0 = actor1 = nullptr; // Make both new actors NULL
+ }
+
+ if (!jointWasInternal || actor0 != actor1)
+ {
+ // The original actors were different, or they are now, signal a joint update
+ TkJointUpdateEvent* e = q->allocData<TkJointUpdateEvent>();
+ e->joint = this;
+ e->subtype = unreferenced ? TkJointUpdateEvent::Unreferenced : (jointWasInternal ? TkJointUpdateEvent::External : TkJointUpdateEvent::Changed);
+ m_data.actors[0] = actor0;
+ m_data.actors[1] = actor1;
+ q->addEvent(e);
+ }
+ else
+ if (jointWasInternal)
+ {
+ // The joint was originally created within the same actor and now it remains within the same actor.
+ m_data.actors[0] = m_data.actors[1] = actor0;
+ }
+}
+
+
+const TkJointData TkJointImpl::getData() const
+{
+ return getDataInternal();
+}
+
+
+void TkJointImpl::removeReferencesInActors()
+{
+ TkActorImpl* actor0 = static_cast<TkActorImpl*>(m_data.actors[0]);
+ TkActorImpl* actor1 = static_cast<TkActorImpl*>(m_data.actors[1]);
+
+ if (actor0 != nullptr)
+ {
+ actor0->removeJoint(m_links[0]);
+ }
+
+ if (actor1 != nullptr && actor1 != actor0) // If the actors are the same, we only had one joint reference
+ {
+ actor1->removeJoint(m_links[1]);
+ }
+}
+
+
+void TkJointImpl::removeReferencesInFamilies()
+{
+ if (m_owner != nullptr)
+ {
+ return; // Only concerned with external joints
+ }
+
+ NVBLAST_ASSERT(m_data.actors[0] != m_data.actors[1] || m_data.actors[0] == nullptr); // This is enforced by the initial assumption in TkFrameworkImpl::createJoint.
+
+ for (int i = 0; i < 2; ++i)
+ {
+ if (m_data.actors[i] != nullptr)
+ {
+ TkFamilyImpl& family = static_cast<TkActorImpl*>(m_data.actors[i])->getFamilyImpl();
+ TkJointImpl* joint = nullptr;
+ const bool found = family.deleteExternalJointHandle(joint, getFamilyID(m_data.actors[i ^ 1]), m_data.chunkIndices[i], m_data.chunkIndices[i ^ 1]);
+ NVBLAST_ASSERT((!found && m_data.actors[i ^ 1] == nullptr) || joint == this); // Might not be found if the actors in a family are in the process of being deleted
+ NV_UNUSED(found);
+ }
+ }
+}
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.h
new file mode 100644
index 0000000..ec57309
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.h
@@ -0,0 +1,146 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKJOINTIMPL_H
+#define NVBLASTTKJOINTIMPL_H
+
+
+#include "NvBlastTkJoint.h"
+#include "NvBlastTkCommon.h"
+#include "NvBlastIndexFns.h"
+
+#include "NvBlastAssert.h"
+#include "NvBlastDLink.h"
+
+#include <atomic>
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+// Forward declarations
+class TkActorImpl;
+class TkJointImpl;
+class TkFamilyImpl;
+class TkEventQueue;
+
+
+/**
+Double-sided link (DLink) which holds a reference back to a joint which contains it.
+*/
+struct TkJointLink : public DLink
+{
+ TkJointImpl* m_joint; //!< The joint containing this link.
+};
+
+
+/**
+Implementation of TkJoint.
+*/
+class TkJointImpl : public TkJoint
+{
+public:
+ /** Blank constructor only creates valid TkJointLinks (point back to this object) */
+ TkJointImpl();
+
+ /**
+ This constructor sets all internal data. If the joint is defined in an asset, the family
+ instanced from that asset will own this joint, and the 'owner' parameter is that family.
+ Otherwise, in the case where a joint is created from TkFramwork::createJoint, the joint
+ is not owned by a family and 'owner' will be NULL.
+ */
+ TkJointImpl(const TkJointDesc& desc, TkFamilyImpl* owner);
+
+ // Begin TkObject
+ virtual void release() override;
+ // End TkObject
+
+ // Begin TkJoint
+ virtual const TkJointData getData() const override;
+ // End TkJoint
+
+ // Public API
+
+ /**
+ Internal method to access a const reference to the joint data.
+
+ \return a const reference to the joint data.
+ */
+ const TkJointData& getDataInternal() const;
+
+ /**
+ Internal method to access a non-const reference to the joint data.
+
+ \return a non-const reference to the joint data.
+ */
+ TkJointData& getDataWritable();
+
+ /**
+ Set the actors that this joint attaches to. When the actors are different from the joint's current actors,
+ an event will be generated on one of the actors' families event queues to signal the change. Alternatively,
+ if alternateQueue is not NULL then it will be used to hold the event.
+
+ If a non-NULL attached actor becomes NULL, then this joint will detach its references to both actors (if
+ they exist) and send an event of subtype Unreferenced. This signals the user that the joint may be deleted.
+
+ \param[in] actor0 The new TkActor to replace the first attached actor.
+ \param[in] actor1 The new TkActor to replace the second attached actor.
+ \param[in] alternateQueue If not NULL, this queue will be used to hold events generated by this function.
+ */
+ void setActors(TkActorImpl* actor0, TkActorImpl* actor1, TkEventQueue* alternateQueue = nullptr);
+
+ /**
+ Ensures that any attached actors no longer refer to this joint.
+ */
+ void removeReferencesInActors();
+
+ /**
+ Ensures that any attached actors' families no longer refer to this joint. External joints (created using
+ TkFramework::createJoint) are referenced by the attached actors' families.
+ */
+ void removeReferencesInFamilies();
+
+private:
+ TkJointData m_data; //!< The data given to the user: attached actors, chunk indices, and actor-local attachment positions.
+ TkJointLink m_links[2]; //!< One link for each actor in m_data.m_actors. If m_data.m_actors[0] == m_data.m_actors[1], then only m_links[0] is used.
+ TkFamilyImpl* m_owner; //!< The owning family if this is an internal joint created during TkFramework::createActor() from a TkAssetDesc with joint flags.
+
+ friend class TkFrameworkImpl;
+ friend class TkFamilyImpl;
+ friend class TkActorImpl;
+};
+
+
+//////// TkJointImpl inline methods ////////
+
+NV_INLINE TkJointImpl::TkJointImpl()
+{
+ m_links[0].m_joint = m_links[1].m_joint = this;
+}
+
+
+NV_INLINE const TkJointData& TkJointImpl::getDataInternal() const
+{
+ return m_data;
+}
+
+
+NV_INLINE TkJointData& TkJointImpl::getDataWritable()
+{
+ return m_data;
+}
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTTKJOINTIMPL_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.cpp
new file mode 100644
index 0000000..3249928
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.cpp
@@ -0,0 +1,263 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#include "NvBlastTime.h"
+
+#include "NvBlastTkTaskImpl.h"
+#include "NvBlastTkFamilyImpl.h"
+#include "NvBlastTkAssetImpl.h"
+#include "NvBlastTkGroupImpl.h"
+
+
+using namespace Nv::Blast;
+
+
+void SharedMemory::allocate(TkFamilyImpl& tkFamily)
+{
+ NVBLAST_ASSERT(m_refCount == 0);
+ const NvBlastAsset* assetLL = tkFamily.getAsset()->getAssetLL();
+
+ // at most leafChunkCount actors can be created within a family
+ // tasks will grab their portion out of these memory blocks
+ uint32_t leafChunkCount = NvBlastAssetGetLeafChunkCount(assetLL, TkFrameworkImpl::get()->log);
+ m_newActorBuffers.allocate(2 * leafChunkCount); // GWD-167 workaround (2*)
+ m_newTkActorBuffers.allocate(leafChunkCount);
+}
+
+
+/**
+Creates a TkEvent::FractureCommand according to the input buffer for tkActor
+into events queue using the LocalBuffers to store the actual event data.
+*/
+NV_FORCE_INLINE void reportFractureCommands(
+ const NvBlastFractureBuffers& buffer,
+ LocalBuffer<NvBlastBondFractureData>& bondBuffer, LocalBuffer<NvBlastChunkFractureData>& chunkBuffer,
+ TkEventQueue& events, const TkActorImpl* tkActor)
+{
+
+ NvBlastBondFractureData* bdata = nullptr;
+ if (buffer.bondFractureCount > 0)
+ {
+ bdata = bondBuffer.allocate(buffer.bondFractureCount);
+ memcpy(bdata, buffer.bondFractures, sizeof(NvBlastBondFractureData)*buffer.bondFractureCount);
+ }
+
+ NvBlastChunkFractureData* cdata = nullptr;
+ if (buffer.chunkFractureCount > 0)
+ {
+ cdata = chunkBuffer.allocate(buffer.chunkFractureCount);
+ memcpy(cdata, buffer.chunkFractures, sizeof(NvBlastChunkFractureData)*buffer.chunkFractureCount);
+ }
+
+ TkFractureCommands* fevt = events.allocData<TkFractureCommands>();
+ fevt->tkActorData = *tkActor;
+ fevt->buffers = { buffer.bondFractureCount, buffer.chunkFractureCount, bdata, cdata };
+ events.addEvent(fevt);
+}
+
+
+/**
+Creates a TkEvent::FractureEvent according to the input buffer for tkActor
+into events queue using the LocalBuffers to store the actual event data.
+*/
+NV_FORCE_INLINE void reportFractureEvents(
+ const NvBlastFractureBuffers& buffer,
+ LocalBuffer<NvBlastBondFractureData>& bondBuffer, LocalBuffer<NvBlastChunkFractureData>& chunkBuffer,
+ TkEventQueue& events, const TkActorImpl* tkActor)
+{
+ uint32_t result[4] = { 0,0,0,0 };
+
+ NvBlastBondFractureData* bdata = nullptr;
+ if (buffer.bondFractureCount > 0)
+ {
+ bdata = bondBuffer.allocate(buffer.bondFractureCount);
+ for (uint32_t b = 0; b < buffer.bondFractureCount; ++b)
+ {
+ bdata[b] = buffer.bondFractures[b];
+ result[buffer.bondFractures[b].health > 0 ? 0 : 1]++;
+ }
+ }
+
+ NvBlastChunkFractureData* cdata = nullptr;
+ if (buffer.chunkFractureCount > 0)
+ {
+ cdata = chunkBuffer.allocate(buffer.chunkFractureCount);
+ for (uint32_t c = 0; c < buffer.chunkFractureCount; ++c)
+ {
+ cdata[c] = buffer.chunkFractures[c];
+ result[buffer.chunkFractures[c].health > 0 ? 2 : 3]++;
+ }
+ }
+
+ TkFractureEvents* fevt = events.allocData<TkFractureEvents>();
+ fevt->tkActorData = *tkActor;
+ fevt->buffers = { buffer.bondFractureCount, buffer.chunkFractureCount, bdata, cdata };
+ fevt->bondsDamaged = result[0];
+ fevt->bondsBroken = result[1];
+ fevt->chunksDamaged = result[2];
+ fevt->chunksBroken = result[3];
+ events.addEvent(fevt);
+}
+
+
+void TkWorker::run()
+{
+ PERF_SCOPE_L("TkWorker Task");
+
+ NvBlastTimers* timers = nullptr;
+
+#if NV_PROFILE
+ NvBlastTimers myTimers;
+ timers = &myTimers;
+ NvBlastTimersReset(timers);
+ uint32_t jobCount = 0;
+ Time workTime;
+#endif
+
+ // temporary memory used to generate and apply fractures
+ // it must fit for the largest family involved in the group that owns this worker
+ NvBlastBondFractureData* bondFractureData = m_group->m_bondTempDataBlock.getBlock(m_id);
+ uint32_t bondFractureCount = m_group->m_bondTempDataBlock.numElementsPerBlock();
+ NvBlastChunkFractureData* chunkFractureData = m_group->m_chunkTempDataBlock.getBlock(m_id);
+ uint32_t chunkFractureCount = m_group->m_chunkTempDataBlock.numElementsPerBlock();
+ const NvBlastFractureBuffers tempBuffer = { bondFractureCount, chunkFractureCount, bondFractureData, chunkFractureData };
+
+ // temporary memory used to split the actor
+ // large enough for the largest family involved
+ void* splitScratch = m_group->m_splitScratchBlock.getBlock(m_id);
+
+ // to avoid unnecessary allocations, preallocated memory exists to fit all chunks and bonds taking damage once
+ // where multiple damage occurs, more memory will be allocated on demand (this may thwart other threads doing the same)
+ m_bondBuffer.initialize(m_group->m_bondEventDataBlock.getBlock(m_id), m_group->m_bondEventDataBlock.numElementsPerBlock());
+ m_chunkBuffer.initialize(m_group->m_chunkEventDataBlock.getBlock(m_id), m_group->m_chunkEventDataBlock.numElementsPerBlock());
+
+ TkAtomicJobQueue& q = m_group->m_jobQueue;
+ TkWorkerJob* j;
+
+ while ((j = q.next()) != nullptr)
+ {
+ PERF_SCOPE_M("TkActor");
+
+ TkActorImpl* tkActor = j->m_tkActor;
+ const uint32_t tkActorIndex = tkActor->getIndex();
+ NvBlastActor* actorLL = tkActor->getActorLLInternal();
+ TkFamilyImpl& family = tkActor->getFamilyImpl();
+ SharedMemory* mem = m_group->getSharedMemory(&family);
+ TkEventQueue& events = mem->m_events;
+
+ NVBLAST_ASSERT(tkActor->getGroupImpl() == m_group);
+
+#if NV_PROFILE
+ *timers += tkActor->m_timers;
+ NvBlastTimersReset(&tkActor->m_timers);
+ jobCount++;
+#endif
+
+ // generate and apply fracture for all damage requested on this actor
+ // and queue events accordingly
+ for (const auto& damage : tkActor->m_damageBuffer)
+ {
+ NvBlastFractureBuffers commandBuffer = tempBuffer;
+
+ PERF_ZONE_BEGIN("Material");
+ damage.generateFracture(&commandBuffer, actorLL, timers);
+ PERF_ZONE_END("Material");
+
+ if (commandBuffer.chunkFractureCount > 0 || commandBuffer.bondFractureCount > 0)
+ {
+ PERF_SCOPE_M("Fill Command Events");
+ reportFractureCommands(commandBuffer, m_bondBuffer, m_chunkBuffer, events, tkActor);
+ }
+
+ NvBlastFractureBuffers eventBuffer = tempBuffer;
+
+ PERF_ZONE_BEGIN("Fracture");
+ NvBlastActorApplyFracture(&eventBuffer, actorLL, &commandBuffer, TkFrameworkImpl::get()->log, timers);
+ PERF_ZONE_END("Fracture");
+
+ if (eventBuffer.chunkFractureCount > 0 || eventBuffer.bondFractureCount > 0)
+ {
+ PERF_SCOPE_M("Fill Fracture Events");
+ tkActor->m_flags |= (TkActorFlag::DAMAGED);
+ reportFractureEvents(eventBuffer, m_bondBuffer, m_chunkBuffer, events, tkActor);
+ }
+ }
+
+
+ // split the actor, which could have been damaged directly though the TkActor's fracture functions
+ // i.e. it did not have damage queued for the above loop
+
+ NvBlastActorSplitEvent splitEvent = { nullptr, nullptr };
+ if (tkActor->isDamaged())
+ {
+ PERF_ZONE_BEGIN("Split Memory");
+ uint32_t maxActorCount = NvBlastActorGetMaxActorCountForSplit(actorLL, TkFrameworkImpl::get()->log);
+ splitEvent.newActors = mem->reserveNewActors(maxActorCount);
+ PERF_ZONE_END("Split Memory");
+ PERF_ZONE_BEGIN("Split");
+ j->m_newActorsCount = NvBlastActorSplit(&splitEvent, actorLL, maxActorCount, splitScratch, TkFrameworkImpl::get()->log, timers);
+ PERF_ZONE_END("Split");
+
+ tkActor->m_flags.clear(TkActorFlag::DAMAGED);
+ }
+ else
+ {
+ j->m_newActorsCount = 0;
+ }
+
+
+ // update the TkActor according to the LL split results and queue events accordingly
+ if (j->m_newActorsCount > 0)
+ {
+ NVBLAST_ASSERT(splitEvent.deletedActor == tkActor->getActorLL());
+
+ PERF_ZONE_BEGIN("memory new actors");
+
+ auto tkSplitEvent = events.allocData<TkSplitEvent>();
+
+ tkSplitEvent->children = mem->reserveNewTkActors(j->m_newActorsCount);
+ tkSplitEvent->numChildren = j->m_newActorsCount;
+
+ tkSplitEvent->parentData.family = &family;
+ tkSplitEvent->parentData.userData = tkActor->userData;
+ tkSplitEvent->parentData.index = tkActorIndex;
+ family.removeActor(tkActor);
+
+ PERF_ZONE_END("memory new actors");
+
+
+ PERF_ZONE_BEGIN("create new actors");
+ for (uint32_t i = 0; i < j->m_newActorsCount; ++i)
+ {
+ TkActorImpl* newActor = family.addActor(splitEvent.newActors[i]);
+ tkSplitEvent->children[i] = newActor;
+ }
+ j->m_newActors = reinterpret_cast<TkActorImpl**>(tkSplitEvent->children);
+ PERF_ZONE_END("create new actors");
+
+ PERF_ZONE_BEGIN("split event");
+ events.addEvent(tkSplitEvent);
+ PERF_ZONE_END("split event");
+ }
+ }
+
+#if NV_PROFILE
+ PERF_ZONE_BEGIN("write timers");
+ m_stats.timers = *timers;
+ m_stats.processedActorsCount = jobCount;
+ m_stats.workerTime = workTime.getElapsedTicks();
+ PERF_ZONE_END("write timers");
+#endif
+}
+
+void TkWorker::release()
+{
+ m_group->m_sync.notify();
+}
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.h
new file mode 100644
index 0000000..75f92f8
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.h
@@ -0,0 +1,444 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKTASKIMPL_H
+#define NVBLASTTKTASKIMPL_H
+
+#include "NvBlast.h"
+
+#include "NvBlastTkFrameworkImpl.h"
+#include "NvBlastTkEventQueue.h"
+#include "NvBlastTkArray.h"
+
+#include <atomic>
+#include <mutex>
+#include <condition_variable>
+
+#include "task/PxTask.h"
+#include "NvBlastAssert.h"
+
+#include "NvBlastTkGroup.h" // TkGroupStats
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+class TkGroupImpl;
+class TkActorImpl;
+class TkFamilyImpl;
+
+
+/**
+Transient structure describing a job and its results.
+*/
+struct TkWorkerJob
+{
+ TkActorImpl* m_tkActor; //!< the actor to process
+ TkActorImpl** m_newActors; //!< list of child actors created by splitting
+ uint32_t m_newActorsCount; //!< the number of child actors created
+};
+
+
+/**
+Counting synchronization object for waiting on TkWorkers to finish.
+*/
+class TaskSync
+{
+public:
+ /**
+ Initializes with an expected number of notifications.
+ */
+ TaskSync(uint32_t count) : m_count(count) {}
+
+ /**
+ Blocks until the expected number of notifications happened.
+ */
+ void wait()
+ {
+ std::unique_lock<std::mutex> lk(m_mutex);
+ m_cv.wait(lk, [&]{ return m_count == 0; });
+ }
+
+ /**
+ Decrement the wait() count by one.
+ */
+ void notify()
+ {
+ PERF_SCOPE_H("TaskSync::notify");
+ std::unique_lock<std::mutex> lk(m_mutex);
+ m_count--;
+ if (m_count == 0)
+ {
+ lk.unlock();
+ m_cv.notify_one();
+ }
+ }
+
+ /**
+ Peek if notifications are pending.
+ */
+ bool isDone()
+ {
+ std::unique_lock<std::mutex> lk(m_mutex);
+ return m_count == 0;
+ }
+
+ /**
+ Sets the expected number of notifications for wait() to unblock.
+ */
+ void setCount(uint32_t count)
+ {
+ m_count = count;
+ }
+
+private:
+ std::mutex m_mutex;
+ std::condition_variable m_cv;
+ uint32_t m_count;
+};
+
+
+/**
+A list of equally sized memory blocks sharable between tasks.
+*/
+template<typename T>
+class SharedBlock
+{
+public:
+
+ SharedBlock() : m_numElementsPerBlock(0), m_numBlocks(0), m_buffer(nullptr) {}
+
+ /**
+ Allocates one large memory block of elementsPerBlock*numBlocks elements.
+ */
+ void allocate(uint32_t elementsPerBlock, uint32_t numBlocks)
+ {
+ NVBLAST_ASSERT(elementsPerBlock > 0 && numBlocks > 0);
+
+ m_buffer = reinterpret_cast<T*>(NVBLASTTK_ALLOC(elementsPerBlock*numBlocks*sizeof(T), "SharedBlock"));
+ m_numElementsPerBlock = elementsPerBlock;
+ m_numBlocks = numBlocks;
+ }
+
+ /**
+ Returns the pointer to the first element of a block of numElementsPerBlock() elements.
+ */
+ T* getBlock(uint32_t id)
+ {
+ NVBLAST_ASSERT(id < m_numBlocks || 0 == m_numElementsPerBlock);
+ return &m_buffer[id*m_numElementsPerBlock];
+ }
+
+ /**
+ The number of elements available per block.
+ */
+ uint32_t numElementsPerBlock() const
+ {
+ return m_numElementsPerBlock;
+ }
+
+ /**
+ Frees the whole memory block.
+ */
+ void release()
+ {
+ m_numBlocks = 0;
+ m_numElementsPerBlock = 0;
+ NVBLASTTK_FREE(m_buffer);
+ m_buffer = nullptr;
+ }
+
+private:
+ uint32_t m_numElementsPerBlock; //!< elements available in one block
+ uint32_t m_numBlocks; //!< number of virtual blocks available
+ T* m_buffer; //!< contiguous memory for all blocks
+};
+
+
+/**
+A preallocated, shared array from which can be allocated from in tasks.
+Intended to be used when the maximum amount of data (e.g. for a family)
+is known in advance. No further allocations take place on exhaustion.
+Exhaustion asserts in debug builds and overflows otherwise.
+*/
+template<typename T>
+class SharedBuffer
+{
+public:
+ SharedBuffer() : m_capacity(0), m_used(0), m_buffer(nullptr) {}
+
+ /**
+ Atomically gets a pointer to the first element of an array of n elements.
+ */
+ T* reserve(size_t n)
+ {
+ NVBLAST_ASSERT(m_used + n <= m_capacity);
+ size_t start = m_used.fetch_add(n);
+ return &m_buffer[start];
+ }
+
+ /**
+ Preallocates memory for capacity elements.
+ */
+ void allocate(size_t capacity)
+ {
+ NVBLAST_ASSERT(m_buffer == nullptr);
+ m_buffer = reinterpret_cast<T*>(NVBLASTTK_ALLOC(capacity*sizeof(T), "SplitMemory"));
+ m_capacity = capacity;
+ }
+
+ /**
+ Preserves the memory allocated but resets to reserve from the beginning of the array.
+ */
+ void reset()
+ {
+ m_used = 0;
+ }
+
+ /**
+ Frees the preallocated array.
+ */
+ void release()
+ {
+ NVBLAST_ASSERT(m_buffer != nullptr);
+ NVBLASTTK_FREE(m_buffer);
+ m_buffer = nullptr;
+ m_capacity = m_used = 0;
+ }
+
+private:
+ size_t m_capacity; //!< available elements in the buffer
+ std::atomic<size_t> m_used; //!< used elements in the buffer
+ T* m_buffer; //!< the memory containing T's
+};
+
+
+/**
+Allocates from a preallocated, externally owned memory block initialized with.
+When blocks run out of space, new ones are allocated and owned by this class.
+*/
+template<typename T>
+class LocalBuffer
+{
+public:
+ /**
+ Returns the pointer to the first element of an array of n elements.
+ Allocates a new block of memory when exhausted, its size being the larger of n and capacity set with initialize().
+ */
+ T* allocate(size_t n)
+ {
+ if (m_used + n > m_capacity)
+ {
+ allocateNewBlock(n > m_capacity ? n : m_capacity);
+ }
+
+ size_t index = m_used;
+ m_used += n;
+ return &m_currentBlock[index];
+ }
+
+ /**
+ Release the additionally allocated memory blocks.
+ The externally owned memory block remains untouched.
+ */
+ void clear()
+ {
+ for (void* block : m_memoryBlocks)
+ {
+ NVBLASTTK_FREE(block);
+ }
+ m_memoryBlocks.clear();
+ }
+
+ /**
+ Set the externally owned memory block to start allocating from,
+ with a size of capacity elements.
+ */
+ void initialize(T* block, size_t capacity)
+ {
+ m_currentBlock = block;
+ m_capacity = capacity;
+ m_used = 0;
+ }
+
+private:
+ /**
+ Allocates space for capacity elements.
+ */
+ void allocateNewBlock(size_t capacity)
+ {
+ PERF_SCOPE_L("Local Buffer allocation");
+ m_capacity = capacity;
+ m_currentBlock = static_cast<T*>(NVBLASTTK_ALLOC(capacity*sizeof(T), "Blast LocalBuffer"));
+ m_memoryBlocks.pushBack(m_currentBlock);
+ m_used = 0;
+ }
+
+ TkInlineArray<void*, 4>::type m_memoryBlocks; //!< storage for memory blocks
+ T* m_currentBlock; //!< memory block used to allocate from
+ size_t m_used; //!< elements used in current block
+ size_t m_capacity; //!< elements available in current block
+};
+
+
+/**
+Holds the memory used by TkWorker for each family in each group.
+*/
+class SharedMemory
+{
+public:
+ SharedMemory() : m_eventsMemory(0), m_eventsCount(0), m_refCount(0) {}
+
+ /**
+ Reserves n entries from preallocated memory.
+ */
+ NvBlastActor** reserveNewActors(size_t n)
+ {
+ return m_newActorBuffers.reserve(n);
+ }
+
+ /**
+ Reserves n entries from preallocated memory.
+ */
+ TkActor** reserveNewTkActors(size_t n)
+ {
+ return m_newTkActorBuffers.reserve(n);
+ }
+
+ /**
+ Allocates buffers to hold
+ */
+ void allocate(TkFamilyImpl&);
+
+ /**
+ Resets the internal buffers to reserve from their beginning.
+ Preserves the allocated memory.
+ */
+ void reset()
+ {
+ m_newActorBuffers.reset();
+ m_newTkActorBuffers.reset();
+ }
+
+ /**
+ Increments the reference count.
+ */
+ void addReference() { m_refCount++; }
+
+ /**
+ Increments the reference count by n.
+ */
+ void addReference(size_t n) { m_refCount += n; }
+
+ /**
+ Decrements the reference count.
+ Returns true if the count reached zero.
+ */
+ bool removeReference()
+ {
+ m_refCount--;
+ return !isUsed();
+ }
+
+ /**
+ Checks if the reference count is not zero.
+ */
+ bool isUsed()
+ {
+ return m_refCount > 0;
+ }
+
+ /**
+ Release the internal buffers' memory.
+ */
+ void release()
+ {
+ m_newActorBuffers.release();
+ m_newTkActorBuffers.release();
+ }
+
+ TkEventQueue m_events; //!< event queue shared across a group's actors of the same family
+ uint32_t m_eventsMemory; //!< expected memory size for event data
+ uint32_t m_eventsCount; //!< expected number of events
+
+private:
+ size_t m_refCount; //!< helper for usage and releasing memory
+
+ SharedBuffer<NvBlastActor*> m_newActorBuffers; //!< memory for splitting
+ SharedBuffer<TkActor*> m_newTkActorBuffers; //!< memory for split events
+};
+
+
+/**
+Shared job queue from which TkWorkers atomically fetch the next job.
+*/
+template <typename T>
+class TkAtomicQueue
+{
+public:
+ /**
+ Initialize for a new batch of jobs.
+ */
+ void init(TkWorkerJob* jobs, uint32_t numJobs)
+ {
+ m_jobs = jobs;
+ m_maxCount = numJobs;
+ m_current = 0;
+ }
+
+ /**
+ Fetch a pointer to the next job. Returns nullptr when exhausted.
+ */
+ T* next()
+ {
+ size_t index = m_current.fetch_add(1, std::memory_order_relaxed);
+ if (index < m_maxCount)
+ {
+ return &m_jobs[index];
+ }
+ return nullptr;
+ }
+
+private:
+ T* m_jobs; //!< the list of jobs
+ size_t m_maxCount; //!< number of jobs available in the list
+ std::atomic<size_t> m_current; //!< current job counter
+};
+
+typedef TkAtomicQueue<TkWorkerJob> TkAtomicJobQueue;
+
+
+/**
+Thread worker fracturing and splitting actors sequentially.
+The list of actual jobs is provided by the group owning this worker.
+*/
+class TkWorker : public physx::PxLightCpuTask
+{
+public:
+ void run();
+ void release();
+ const char* getName() const { return "TkWorker"; }
+
+ uint32_t m_id; //!< this worker's id
+ TkGroupImpl* m_group; //!< the group owning this worker
+
+ LocalBuffer<NvBlastChunkFractureData> m_chunkBuffer; //!< memory manager for chunk event data
+ LocalBuffer<NvBlastBondFractureData> m_bondBuffer; //!< memory manager for bonds event data
+
+#if NV_PROFILE
+ TkGroupStats m_stats;
+#endif
+};
+}
+}
+
+#endif // NVBLASTTKTASKIMPL_H
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkTypeImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkTypeImpl.h
new file mode 100644
index 0000000..a29c32f
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkTypeImpl.h
@@ -0,0 +1,174 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKTYPEIMPL_H
+#define NVBLASTTKTYPEIMPL_H
+
+
+#include "NvPreprocessor.h"
+
+#include "NvBlastTkType.h"
+
+
+// Forward declarations
+namespace physx
+{
+namespace general_PxIOStream2
+{
+class PxFileBuf;
+}
+}
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+// Forward declarations
+class TkSerializable;
+
+
+// Serialization function signature
+typedef TkSerializable* (*TkDeserializeFn)(physx::general_PxIOStream2::PxFileBuf&, const NvBlastID& id);
+
+
+/**
+Implementation of TkType, storing class information for TkIdentifiable-derived classes.
+*/
+class TkTypeImpl : public TkType
+{
+public:
+ TkTypeImpl(const char* typeName, uint32_t typeID, uint32_t version, TkDeserializeFn deserializeFn);
+
+ // Begin TkType
+ virtual const char* getName() const override { return getNameInternal(); }
+
+ virtual uint32_t getVersion() const override { return getVersionInternal(); }
+ // End TkType
+
+ // Public methods
+
+ /**
+ Access to the class name.
+
+ \return a C string pointer to the class name.
+ */
+ const char* getNameInternal() const;
+
+ /**
+ Access to the data format version for the class (used if it TkSerializable-derived).
+
+ \return the data format version.
+ */
+ uint32_t getVersionInternal() const;
+
+ /**
+ Access to a unique identifier for the class (set using the NVBLASTTK_IMPL_DEFINE_IDENTIFIABLE or NVBLASTTK_IMPL_DEFINE_SERIALIZABLE macro).
+
+ \return the class's unique identifier.
+ */
+ uint32_t getID() const;
+
+ /**
+ \return the class's deserialization function.
+ */
+ TkDeserializeFn getDeserializeFn() const;
+
+ /**
+ Access to a runtime-unique small index for the class.
+
+ \return the index for the class.
+ */
+ uint32_t getIndex() const;
+
+ /**
+ \return whether or not the index has been set (see setIndex) to a valid value.
+ */
+ bool indexIsValid() const;
+
+private:
+ enum { InvalidIndex = 0xFFFFFFFF };
+
+ /**
+ Sets the type index.
+
+ \param[in] index The index to set.
+ */
+ void setIndex(uint32_t index);
+
+ const char* m_name; //!< The name of the class, set by the constructor.
+ uint32_t m_ID; //!< The unique identifier for the class, set by the constructor.
+ uint32_t m_version; //!< The data format version for the class, set by the constructor.
+ TkDeserializeFn m_deserializeFn; //!< The class deserialization function, set by the constructor.
+ uint32_t m_index; //!< The index set for this class, set using setIndex().
+
+ friend class TkFrameworkImpl;
+};
+
+
+//////// TkTypeImpl inline methods ////////
+
+NV_INLINE TkTypeImpl::TkTypeImpl(const char* typeName, uint32_t typeID, uint32_t version, TkDeserializeFn deserializeFn)
+ : m_name(typeName)
+ , m_ID(typeID)
+ , m_version(version)
+ , m_deserializeFn(deserializeFn)
+ , m_index((uint32_t)InvalidIndex)
+{
+}
+
+
+NV_INLINE const char* TkTypeImpl::getNameInternal() const
+{
+ return m_name;
+}
+
+
+NV_INLINE uint32_t TkTypeImpl::getVersionInternal() const
+{
+ return m_version;
+}
+
+
+NV_INLINE uint32_t TkTypeImpl::getID() const
+{
+ return m_ID;
+}
+
+
+NV_INLINE TkDeserializeFn TkTypeImpl::getDeserializeFn() const
+{
+ return m_deserializeFn;
+}
+
+
+NV_INLINE uint32_t TkTypeImpl::getIndex() const
+{
+ return m_index;
+}
+
+
+NV_INLINE bool TkTypeImpl::indexIsValid() const
+{
+ return m_index != (uint32_t)InvalidIndex;
+}
+
+
+NV_INLINE void TkTypeImpl::setIndex(uint32_t index)
+{
+ m_index = index;
+}
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTTKTYPEIMPL_H