/*! \page pagehlapi High Level (Toolkit) API (NvBlastTk) Table of Contents \subpage tkintroduction \subpage tk_class_hierarchy \subpage tk_include_and_library \subpage framework_init \subpage tkasset_creation \subpage tkasset_instancing \subpage tkgroups \subpage damage_in_tk \subpage tkjoints \subpage tkevents \subpage tktypes
\section tkintroduction Introduction to NvBlastTk The high-level API, NvBlastTk (Tk stands for "toolkit"), is intended to be a more powerful library and a much more convenient entry point into the use of Blast&tm;. Like the low-level library, Tk is physics and graphics-agnostic. Whereas the low-level API is C-style, Tk uses a C++ API. Everything in Tk is in the namespace: \code Nv::Blast \endcode (the only exceptions are global-scope functions to create and access a framework singleton, see below). Every object in Tk is prefixed with 'Tk'. For example, the Tk framework interface is: \code Nv::Blast::TkFramework \endcode For the remainder of this page we will be in the Nv::Blast namespace, and will drop the explicit scope Nv::Blast:: from our names.

BlastTk adds: - An object class hierarchy (see \ref tk_class_hierarchy, below). - A global framework, TkFramework (a singleton). This keeps track of TkIdentifiable objects and allows the user to query them based upon either GUID or TkIdentifiable subclass type, and also provides a number of functions to create the various objects in BlastTk. - Processing groups with a task interface (see TkGroup). - Event dispatching for actor families (see TkFamily). - Intra-actor and inter-actor joint management (see TkJoint). Note, these "joints" only hold descriptor data, since physical objects are not handled by BlastTk.
\section tk_class_hierarchy NvBlastTk Class Hierarchy - There are two abstract interfaces, one of which deriving from the other: TkObject <- TkIdentifiable. - Lightweight objects are derived from TkObject. - Objects which use a GUID and class identification are derieved from TkIdentifiable. - TkAsset derives from TkIdentifiable. This is mostly a wrapper for NvBlastAsset, however it also stores extra data associated with the asset such as internal joint descriptors. - TkFamily derives from TkIdentifiable. One of these objects is made when a TkActor is instanced from a TkAsset. All actors that are created by splitting the family's original actor remain within the same family. Actor and joint events are dispatched from the TkFamily. - TkGroup derives from TkIdentifiable. Groups are processing units. The user may create as many groups as they please, and add or remove actors as they please from groups. The group provides a worker (TkGroupWorker) interface which allows the user to process multiple jobs in the group asynchoronously. These jobs, along with a call to TkGroup::endProcess(), perform the tasks of generating fracture commands, applying fracture commands, and actor splitting at the low-level. The user is informed of splitting through listeners given to TkFamily objects. - TkActor derives from TkObject. It is mostly a wrapper for NvBlastActor, but it also provides a number of damage functions to the user. - TkJoint derives from TkObject. TkAsset descriptors, cause internal TkJoint obejcts to be created within an actor (joining chunks within the same actor). Alternatively, the TkFramework provides a function which allows the user to create an external joint between any two different actors. As actors split, internal joints may become external. The user gets notification whenever joints become external, or when actors joined by joints change or are deleted, through listeners attached to the associated TkFamily objects.
\section tk_include_and_library Linking and Header Files To use the BlastTk library, the application need only inlclude the header NvBlastTk.h, found in the include/toolkit folder, and link against the appropriate version of the NvBlastTk library. Depending on the platform and configuration, various suffixes will be added to the library name. The general naming scheme is NvBlastTk(config)(arch).(ext) (config) is DEBUG, CHECKED, OR PROFILE for the corresponding configurations. For a release configuration there is no (config) suffix. (arch) is _x86 or _x64 for Windows 32- and 64-bit builds, respectively, and empty for non-Windows platforms. (ext) is .lib for static linking and .dll for dynamic linking on Windows. On XBoxOne it is .lib, and on PS4 it is .a.
\section framework_init Creating the TkFramework As a reminder, in this document we assume we are in the Nv::Blast namespace: \code using namespace Nv::Blast; \endcode In order to use NvBlastTk, one first has to create a TkFramework singleton. This simply requires a call to the global function NvBlastTkFrameworkCreate: \code TkFramework* framework = NvBlastTkFrameworkCreate(); \endcode The framework may be accessed via: \code TkFramework* framework = NvBlastTkFrameworkGet(); \endcode In the sections that follow, it is assumed that a framework has been created, and we have a pointer to it named 'framework' within scope. Finally, to release the framework, use \code framework->release(); \endcode This will release all assets, families, actors, joints, and groups.
\section tkasset_creation Creating a TkAsset The TkAsset object is a high-level wrapper for the low-level NvBlastAsset (see \ref assets). The descriptor used to create a TkAsset, a TkAssetDesc, is derived from NvBlastAssetDesc. The base fields should be filled in as described in (\ref assets). The new field is an optional array of flags to be associated with each bond in the base descriptor. Currently the only flag is "BondJointed," and if set will cause an "internal joint" to be created in actors (TkActor type) created from the asset. See (\ref tkjoints) for more on joints in BlastTk. \code TkAssetDesc desc; myFunctionToFillInLowLevelAssetFields(desc); // Fill in the low-level (NvBlastAssetDesc) fields as usual std::vector bondFlags(desc.bondCount, 0); // Clear all flags // Set BondJointed flags corresponding to joints selected by the user (assumes a myBondIsJointedFunction to make this selection) for (uint32_t i = 0; i < desc.bondCount; ++i) { if (myBondIsJointedFunction(i)) // User-authored { bondFlags[i] |= TkAssetDesc::BondJointed; } } TkAsset* asset = framework->createAsset(desc); // Create a new TkAsset \endcode The createAsset function used above creates a low-level NvBlastAsset from the base fields of the descriptor, and then adds internal joint descriptors based upon the bonds' centroids and attached chunks. An alternative method to create a TkAsset allows the user to pass in a pre-existing NvBlastAsset, and a list of joint descriptors. If the TkAsset is to have no internal joints, then the joint descriptors are not necessary and with an NvBlastAsset pointer llAsset, a TkAsset may be created simply by using \code TkAsset* asset = framework->createAsset(llAsset); \endcode By default, such a TkAsset will not "own" the llAsset. When the TkAsset is released, the llAsset memory will be untouched. You can pass ownership to the TkAsset using all of the default parameters of the createAsset function: \code TkAsset* asset = framework->createAsset(llAsset, nullptr, 0, true); \endcode The last parameter sets ownership. N.B.: in order for the TkAsset to own the underlying llAsset, and therefore release it when the TkAsset is released, the memory for the llAsset must be allocated using the allocator accessed through NvBlastGlobals (see \ref pageglobalsapi). If one wants to author internal joints in a TkAsset using this second createAsset method, one must pass in a valid array of joint descriptors of type TkAssetJointDesc. Each joint descriptor takes two positions and two node indices. The positions are the joint's attachment positions in asset space, and the nodes indices are those of the graph nodes that correspond to support chunks. These indices are not, in general, the same as the chunk indices. An example of initialization of the joint descriptors is given below. \code std::vector jointDescs(jointCount); // Assume jointCount = the number of joints to add jointDescs[0].nodeIndices[0] = 0; // Attach node 0 to node 1 jointDescs[0].nodeIndices[1] = 1; jointDescs[0].attachPoistions[0] = physx::PxVec3( 1.0f, 2.0f, 3.0f ); // Attachment positions are often the same within an asset, but they don't have to be jointDescs[0].attachPoistions[1] = physx::PxVec3( 1.0f, 2.0f, 3.0f ); // ... etc. TkAsset* asset = framework->createAsset(llAsset, jointDescs.data(), jointDescs.size()); \endcode The code above assumes you know the support graph nodes to which you'd like to attach joints. Often, the user only knows the corresponding chunk indices. Fortunately it's easy to map chunk indices to graph node indices. In order to get the map, use the low-level function \code const uint32_t map = NvBlastAssetGetChunkToGraphNodeMap(llAsset, logFn); \endcode This map is an array with an entry for every chunk index. To get the graph node index for a chunk indexed chunkIndex, use \code uint32_t nodeIndex = map[chunkIndex]; \endcode If the chunk indexed by chunkIndex does not correspond to a support chunk, then the mapped value will be UINT32_MAX, the invalid index. Otherwise, the mapped value will be a valid graph node index. Finally, to release a TkAsset, as with any TkObject-derived object, use the release() method: \code asset->release(); \endcode
\section tkasset_instancing Instancing a TkAsset: Creation of a TkActor and a TkFamily Whereas with the Blast&tm; low-level (\ref pagellapi), one must explicitly create a family (NvBlastFamily) from an asset (NvBlastAsset) before creating the first actor (NvBlastActor) in the family, NvBlastTk creates a TkFamily automatically when an unfractured TkActor is instanced from a TkAsset using the framework's createActor function. This family is accessible through the actor and any actor that is created from splitting it. The family is not released automatically when all actors within it have been released. The user must use the TkFamily's release() method (see TkObject base API) to do so. (Or wait until the framework is released.) If a family is released that contains actors, the actors within will be released as well. The TkFamily has a special role in NvBlastTk, holding user-supplied event listeners (\ref TkEventListener). All internal actor creation and destruction events are broadcast to listeners through split events (TkSplitEvent). These signal when a fracturing operation has destroyed an actor and created child actors from it. TkActor creation or release that occurs from an explicit API call do not produce events. For example when creating a first unfractured instance of an asset using createAsset, or when calling the release() method on a TkActor. TkJoint events are similarly broadcast to receivers (TkJointEvent). These signal when the actors which are joined by the joints change, so that the user may update a corresponding physical joint. They also signal when a joint no longer attaches actors and is therefore unreferenced. The user may invalidate or release the joint using the TkObject release() method when this occurs (more on joint ownership in \ref tkjoints). To create an unfractured TkActor instance from a TkAsset, one first fills in a descriptor (\ref TkActorDesc) and passes it to the framework's createActor function. As with the TkAssetDesc, the TkActorDesc is derived from its low-level counterpart, the NvBlastActorDesc. In addition the TkActorDesc holds a pointer to the TkAsset being instanced. An example of TkActor creation is given below, given a TkAsset pointer asset. \code TkActorDesc desc; // The TkActorDesc constructor sets sane default values for the base (NvBlastActorDesc) fields, giving uniform chunk and bond healths of 1.0. desc.asset = asset; // This field of TkActorDesc must be set to a valid asset pointer. TkActor* actor = framework->createActor(desc); \endcode The TkFamily created with the actor above may be accessed through the actor's getFamily field: \code TkFamily& family = actor->getFamily(); \endcode The returned value is a reference since a TkActor's family can never be NULL. Actors resulting from the split of a "parent" actor will always belong to the parent's family. For most applications, the user will need to create a listener object to pass to every family created, in order to keep their physics and graphics representations in sync with the splitting of the TkActor. For more on this, see \ref tkevents.
\section tkgroups Groups One important feature of NvBlastTk is the ability to multitask damage processing. The mechanism by which the toolkit does this is the group object, TkGroup. Groups are created at the request of the user; the user may create as many groups as he or she likes. Actors may be added or removed from groups in any way the user wishes, with the only constraint being that a given actor may belong to no more than one group. A group is a processing object, much like a scene in a physics simulation. Indeed, a natural pattern would be to associate one group per physics scene, and synchronize the group processing with scene simulation. Another pattern would be to subdivide the world into neighborhoods, and associate each neighborhood with a group. A distributed game could take advantage of this structure to similarly distribute computation. Group processing is performed by workers, which have a TkGroupWorker API exposed to the user. The number of workers may be set by the user, with the idea being that this should correspond to the number of threads available for group processing. Processing starts with a call to TkGroup::startProcess(). This creates a number of jobs which the user may assign to workers as they like, each worker potentially on its own thread. The jobs calculate the effects of all damage taken by the group's actors. After all jobs have been run, the user must call TkGroup::endProcess(). This will result in all events being fired off to listeners associated with families with actors in the group. A convenience function, TkGroup::process(), is provided which uses one worker to perform all jobs sequentially on the calling thread. This is useful shortcut to get BlastTk up and running quickly. A multithreaded group processing implementation is given by Nv::Blast::ExtGroupTaskManager (in NvBlastExtPxTask.h). This resides in \ref pageextphysx, because it uses physx::PxTask. Actors resulting from the split of a "parent" actor will be placed automatically into the group that the parent belonged to. This is similar to the assigment of families from a split, except that unlike families, the user then has the option to move the new actors to other groups, or no group at all. Also similar to families, groups are not automatically released when the last actor is removed from it. Unlike families, when a group is released, the actors which belong to the group are not released. They will, however, be removed from the group before the release is complete. A typical usage is outlined below. See \ref damage_in_tk for methods of applying damage to actors. \code // Create actors from descriptors desc1, desc2, ... etc., and attach a listener to each new family created TkActor* actor1 = framework->createActor(desc1); actor1->getFamily().addListener(gMyReceiver); // gMyReceiver is a TkEventListener-derived object. More on events in a subsequent section. TkActor* actor2 = framework->createActor(desc2); actor2->getFamily().addListener(gMyReceiver); TkActor* actor3 = framework->createActor(desc3); actor3->getFamily().addListener(gMyReceiver); // etc... // Let's create two groups. First, create a group descriptor. This may be used to create both groups. TkGroupDesc groupDesc; groupDesc.workerCount = 1; // this example processes groups on the calling thread only // Now create the groups TkGroup* group1 = framework->createGroup(groupDesc); TkGroup* group2 = framework->createGroup(groupDesc); // Add actor1 and actor2 to group1, and actor2 to group3... group1->addActor(actor1); group1->addActor(actor2); group2->addActor(actor3); // etc... // Now apply damage to all actors - *NOTE* damage is described in detail in the next section. // For now we will just assume a "myDamageFunction" to apply the damage. myDamageFunction(actor1); myDamageFunction(actor2); myDamageFunction(actor3); // etc... // Calling the groups' process functions will (synchronously) run all jobs to process damage taken by the contained actors. group1->process(); group2->process(); // When the groups are no longer needed, they may be released with the usual release method. group1->release(); group2->release(); \endcode
Multithreaded processing When distributing the jobs as mentioned above, every job must be processed exactly once (over all user tasks). The number of jobs processed per worker can range from a single job (resulting in a user task per job) to all jobs (like Nv::Blast::TkGroup::process() does). At any point in time, no more than the set workerCount amount of workers may have been acquired. Return the worker at the end of each task. \code Nv::Blast::TkGroupWorker* worker = group->acquireWorker(); // process some jobs group->returnWorker(worker); \endcode
\section damage_in_tk Applying Damage to Actors and Families Damage in NvBlastTk uses the same damage program scheme as the low-level SDK (see \ref splitting). One passes the program (NvBlastDamageProgram), damage descriptor (program-dependent), and material (also program-dependent) to a TkActor::damage function. Ultimately, the damage descriptor and material data are all parameters used by the damage program. The distinction is that the damage descriptor should describe properties of the thing doing the damage, while the material should describe properties of the actor (the thing being damaged). The interpretation of this data is entirely up to the program's functions, however. For convenience, the user may set a default material in the actor's family. This assumes, of course, that the material parameters for this default are compatible with the program being used to damage the family's actors. Examples of the three TkActor damage methods are given below.
\subsection multiple_damage Multiple Damage Descriptors using NvBlastProgramParams N.B. - with this method of damage, the lifetime of the NvBlastProgramParams must extend at least until the TkGroup::endProcess call for the actor. \code NvBlastDamageProgram program = { myGraphShaderFunction, // A function with the NvBlastGraphShaderFunction signature mySubgraphShaderFunction // A function with the NvBlastSubgraphShaderFunction signature }; // The example struct "RadialDamageDesc" is modeled after NvBlastExtRadialDamageDesc in the NvBlastExtShaders extension RadialDamageDesc damageDescs[2]; damageDescs[0].compressive = 10.0f; damageDescs[0].position[0] = 1.0f; damageDescs[0].position[1] = 2.0f; damageDescs[0].position[2] = 3.0f; damageDescs[0].minRadius = 0.0f; damageDescs[0].maxRadius = 1.0f; damageDescs[1].compressive = 100.0f; damageDescs[1].position[0] = 3.0f; damageDescs[1].position[1] = 4.0f; damageDescs[1].position[2] = 5.0f; damageDescs[1].minRadius = 0.0f; damageDescs[1].maxRadius = 5.0f; // The example material "Material" is modeled after NvBlastExtMaterial in the NvBlastExtShaders extension Material material; material.health = 10.0f; material.minDamageThreshold = 0.1f; material.maxDamageThreshold = 0.8f; // Set the damage params struct NvBlastProgramParams params = { damageDescs, 2, &material }; // Apply damage actor->damage(program, ¶ms); // params must be kept around until TkGroup::endProcess is called! \endcode
\subsection single_damage_desc_default_material Single Damage Descriptor with Default TkFamily Material This method of damage copies the damage descriptor into a buffer, so the user need not hold onto a copy after the damage function call. Only one damage descriptor may be passed in at once. To use this method, the user must first set a default material in the actor's family. For example: \code // The example material "Material" is modeled after NvBlastExtMaterial in the NvBlastExtShaders extension Material material; material.health = 10.0f; material.minDamageThreshold = 0.1f; material.maxDamageThreshold = 0.8f; // Set the default material used by the material-less TkActor::damage call actor->getFamily().setMaterial(&material); \endcode N.B. the lifetime of the material set must extend at least until the TkGroup::endProcess call for the actor. Then to apply damage, use: \code NvBlastDamageProgram program = { myGraphShaderFunction, // A function with the NvBlastGraphShaderFunction signature mySubgraphShaderFunction // A function with the NvBlastSubgraphShaderFunction signature }; // The example struct "RadialDamageDesc" is modeled after NvBlastExtRadialDamageDesc in the NvBlastExtShaders extension RadialDamageDesc damageDesc; damageDesc.compressive = 10.0f; damageDesc.position[0] = 1.0f; damageDesc.position[1] = 2.0f; damageDesc.position[2] = 3.0f; damageDesc.minRadius = 0.0f; damageDesc.maxRadius = 1.0f; // Apply damage actor->damage(program, &damageDesc, (uint32_t)sizeof(RadialDamageDesc)); \endcode
\subsection single_damage_desc_with_material Single Damage Descriptor with Specified Material This method is just like the one above, except that the user has the opportunity to override the material used during damage. N.B. - the lifetime of the material passed in must extend at least until the TkGroup::endProcess call for the actor. This call is just like the one above with an extra material parameter: \code actor->damage(program, &damageDesc, (uint32_t)sizeof(RadialDamageDesc), &material); \endcode
\section tkjoints Joints Joints in NvBlastTk are abstract representations of physical joints. When joints become active, change the actors they join, or become unreferenced (the actors they join disappear), the user will receive notification via a TkJointUpdateEvent (see \ref tkevents). Joints may be defined as a part of a TkAsset, in which case they are consisdered "internal" joints. (See \ref tkasset_creation.) Since the first instance of a TkAsset is a single TkActor, internal joints are defined between chunks within the same actor. Therefore they are not active (there is no point in joining two locations in a single rigid body). Upon splitting into multiple actors, however, an internal joint's chunks may now belong to two different TkActors. When this happens, the user will receive a TkJointUpdateEvent of subtype TkJointUpdateEvent::External. The event contains a pointer to the TkJoint, and from that the user has access to the information needed to create a physical joint between the rigid bodies that correspond to the joined TkActors. Joints may also be created externally at runtime, using the TkFramework::createJoint function. A joint created this way must be between two different TkActors. Because of this, the joint is immediately considered active, and so no TkJointUpdateEvent is generated from its creation. The user should create a physical joint to correspond to the joint returned by createJoint. An externally created joint of this type has another distinguishing characteristic: it may join an actor to "the world," or "Newtonial Reference Frame" (NRF). To do this, one TkFamily pointer in the joint descriptor is set to NULL. Examples are given below. \code TkJointDesc desc; desc.families[0] = &actor0->getFamily(); // Assume we have a valid actor0 pointer desc.chunkIndices[0] = 1; // This chunk *must* be a support chunk in the asset that created desc.families[0] desc.attachPositions[0] = physx::PxVec3(1.0f, 2.0f; 3.0f); // The attach position is in asset space desc.families[1] = &actor1->getFamily(); // Assume we have a valid actor1 pointer... note, actor0 and actor1 could have the same family desc.chunkIndices[1] = 10; // This chunk *must* be a support chunk in the asset that created desc.families[1] desc.attachPositions[1] = physx::PxVec3(4.0f, 5.0f; 6.0f); // The attach position is in asset space // Create the external joint from the descriptor, which joins actor0 and actor1 TkJoint* joint = framework->createJoint(desc); // Now join actor0 to the NRF // desc.families[0] already contains actor0's family desc.chunkIndices[0] = 2; // Again, this chunk must be a support chunk in the asset that created desc.families[0] desc.attachPositions[0] = physx::PxVec3(0.0f, 0.0f; 0.0f); // The attach position is in asset space desc.families[1] = nullptr; // Setting the family to NULL designates the world (NRF) // The value of desc.chunkIndices[1] is not used, since desc.families[1] is NULL desc.attachPositions[1] = physx::PxVec3(0.0f, 0.0f, 10.0f); // Attach position in the world // Create the external joint which joins actor0 to the world TkJoint* jointNRF = framework->createJoint(desc); \endcode
\subsection releasing_joints Releasing Joints TkJoints are not released by Blast&tm;, except when the TkFramework is released. Otherwise, the user is responsible for releasing TkJoints after they become unreferenced. This is facilitated by the Unreferenced subtype of the TkJointUpdateEvent. After receiving this event for joint, the user may choose to release, using the typical TkObject::release() method. \code joint->release(); \endcode Note, this method can be called at any time, even before the joint is unreferenced. When called, it will remove its references to its attached actors first, causing the joint to then become unreferenced. For example, if the user wishes to break a physical joint in their simulation, they can then release the corresponding TkJoint. It should be mentioned, however, that joints created with an asset are allocated differently from external joints created using TkFramework::createJoint. Internal joints created from the joint descriptors in a TkAsset are block allocated with every TkFamily that instances the asset. Calling the release() method on those joints will remove any remaining references to them (as mentioned above), but will not perform any deallocation. Only when the TkFamily itself is released will the internal joint memory for that family be released. This is true even if the internal joints become "external" from actor splitting. Joints that become external are still associated with a single family and their memory still resides with that family. On the other hand, joints that start out life external by way of the TkFramework::createJoint function have a separate allocation, and do not have memory tied to any TkFamily (even if both actors joined are in the same family). Releasing a family holding one of the actors in such a "purely external" joint will trigger a TkJointUpdateEvent of subtype Unreferenced, however, signalling that the joint is ready for user release.
\section tkevents Events NvBlastTk uses events to communicate the results of actor splitting, joint updates from actor splitting, and fracture event buffers that can be used to synchronize fracturing between multiple clients. Events are broadcast to listeners which implement the TkEventListener interface. Listeners are held by TkFamily objects. During a TkGroup::endProcess call (see \ref tkgroups), relevant events are broadcast to the listeners in the families associated with the actors in the group. A typical user's receiver implementation might take on the form shown below. \code class MyActorAndJointListener : public TkEventListener { // TkEventListener interface void receive(const TkEvent* events, uint32_t eventCount) override { // Events are batched into an event buffer. Loop over all events: for (uint32_t i = 0; i < eventCount; ++i) { const TkEvent& event = events[i]; // See TkEvent documentation for event types switch (event.type) { case TkSplitEvent::EVENT_TYPE: // A TkActor has split into smaller actors { const TkSplitEvent* splitEvent = event.getPayload(); // Split event payload // The parent actor may no longer be valid. Instead, we receive the information it held // which we need to update our app's representation (e.g. removal of the corresponding physics actor) myRemoveActorFunction(splitEvent->parentData.family, splitEvent->parentData.index, splitEvent->parentData.userData); // The split event contains an array of "child" actors that came from the parent. These are valid // TkActor pointers and may be used to create physics and graphics representations in our application for (uint32_t j = 0; j < splitEvent->numChildren; ++j) { myCreateActorFunction(splitEvent->children[j]); } } break; case TkJointUpdateEvent::EVENT_TYPE: { const TkJointUpdateEvent* jointEvent = event.getPayload(); // Joint update event payload // Joint events have three subtypes, see which one we have switch (jointEvent->subtype) { case TkJointUpdateEvent::External: myCreateJointFunction(jointEvent->joint); // An internal joint has been "exposed" (now joins two different actors). Create a physics joint. break; case TkJointUpdateEvent::Changed: myUpdatejointFunction(jointEvent->joint); // A joint's actors have changed, so we need to update its corresponding physics joint. break; case TkJointUpdateEvent::Unreferenced: myDestroyJointFunction(jointEvent->joint); // This joint is no longer referenced, so we may delete the corresponding physics joint. break; } } // Unhandled: case TkFractureCommands::EVENT_TYPE: case TkFractureEvents::EVENT_TYPE: default: break; } } } }; \endcode Whenever a new TkActor is created by the user (via TkFramework::createActor, see \ref tkasset_instancing), its newly-made family should be given whatever listeners the user wishes to attach. For example, \code TkActor* actor = framework->createActor(actorDesc); actor->getFamily().addListener(myListener); // myListener is an object which implements TkEventListener (see MyActorAndJointListener above, for example) \endcode Listeners may also be removed from families at any time.
\section tktypes Object and Type Identification NvBlastTk objects that are derived from TkIdentifiable (TkAsset, TkFamily, and TkGroup) support an object and class (type) identification system. The TkIdentifiable interfaces setID and getID allow the user to set and access an NvBlastID for each object. The NvBlastID is a 128-bit identifier. TkIdentifiable objects are tracked by the TkFramework, which may be used to look up an object by its NvBlastID. Upon creation, TkIdentifiable objects are given a GUID, a unique NvBlastID. The user is welcome to change the object's guid at any time, with the restriction that the GUID cannot be all zero bytes. With an object's GUID, one may look up the object using the TkFramework function findObjectByID: \code TkIdentifiable* object = framework->findObjectByID(id); // id = an NvBlastID GUID \endcode If the object is found, a non-NULL pointer will be returned. TkIdentifiable-derived classes also have a class identification system, the TkType interface. From an individual object one may use the TkIdentifiable interface getType to access the class's TkType interface. Alternatively, one may use the TkFramework getType function with TkTypeIndex::Enum argument. For example, to get the TkType interface for the TkAsset class, use \code const TkType* assetType = framework->getType(TkTypeIndex::Asset); \endcode The type interface may be used: - to access class-specific object lists in the framework, - identify the class of a TkIdentifiable obtained through ID lookup or deserialization, or - to obtain the class's name and format version number. For example, to access a list of all families: \code // Get the TkFamily type interface const TkType* familyType = framework->getType(TkTypeIndex::Family); // Get the family count to allocate a buffer const uint32_t familyCount = framework->getObjectCount(familyType); std::vector families(familyCount); // Write the families to the buffer const uint32_t familiesFound = framework->getObjects(families.data(), familyCount, familyType); \endcode In the above code, the values of familyCount and familiesFound should be equal. An alternative usage of TkFramework::getObjects allows the user to write to a (potentially) smaller buffer, iteratively. For example: \code uint32_t familiesFound; uint32_t totalFamilyCount = 0; do { // Write to a fixed-size buffer TkIdentifiable* familyBuffer[16]; familiesFound = framework->getObjects(familyBuffer, 16, familyType, totalFamilyCount); totalFamilyCount += familiesFound; // Process the families found so far myProcessFamiliesFunction(familyBuffer, familiesFound); } while (familiesFound == 16); \endcode To use the type interface to identify a class, perhaps after serialization or lookup by ID, one may do something like: \code \\ Assume we have a TkIdentifiable pointer called "object" // Get the type interfaces of interest const TkType* assetType = framework->getType(TkTypeIndex::Asset); const TkType* familyType = framework->getType(TkTypeIndex::Family); if (object->getType() == *assetType) { TkAsset* asset = static_cast(object); // Process the object as a TkAsset } if (object->getType() == *familyType) else { TkFamily* family = static_cast(object); // Process the object as a TkFamily } \endcode A TkIdentifiable-derived class may be queried for its name using the TkType interface, using TkType::getName(). This function returns a const char pointer to a string. Finally, one may query the class for its current format version number using TkType::getVersion().
*/