static void verifyIsTerminalOrTransform(const hkpShape* shape) { if (hkOneFixedMoppUtil::isTerminalConvexShape(shape)) { return; } hkpShapeType type = shape->getType(); if (type == HK_SHAPE_TRANSFORM || type == HK_SHAPE_CONVEX_TRANSFORM || type == HK_SHAPE_CONVEX_TRANSLATE ) { const hkpShape* childShape = HK_NULL; switch(type) { case HK_SHAPE_TRANSFORM: childShape = static_cast<const hkpTransformShape*>(shape)->getChildShape(); break; case HK_SHAPE_CONVEX_TRANSFORM: childShape = static_cast<const hkpConvexTransformShape*>(shape)->getChildShape(); break; case HK_SHAPE_CONVEX_TRANSLATE: childShape = static_cast<const hkpConvexTranslateShape*>(shape)->getChildShape(); break; default: break; } HK_ASSERT2(0xad7788dd, hkOneFixedMoppUtil::isTerminalConvexShape(childShape), "bad" ); return; } HK_ASSERT2(0xad6888dd, false, "bad"); }
SkeletonDemo::SkeletonDemo( hkDemoEnvironment* env ) : hkDefaultAnimationDemo(env) { // Load the data m_loader = new hkLoader(); // Convert the scene { hkString assetFile = hkAssetManagementUtil::getFilePath("Resources/Animation/HavokGirl/hkRig.hkx"); hkRootLevelContainer* container = m_loader->load( assetFile.cString() ); HK_ASSERT2(0x27343437, container != HK_NULL , "Could not load asset"); hkaAnimationContainer* ac = reinterpret_cast<hkaAnimationContainer*>( container->findObjectByType( hkaAnimationContainerClass.getName() )); HK_ASSERT2(0x27343435, ac && (ac->m_numSkeletons > 0), "No skeleton loaded"); m_skeleton = ac->m_skeletons[0]; } // // Setup the camera // { hkVector4 from( 3,3,1 ); hkVector4 to ( 0,0,0 ); hkVector4 up ( 0.0f, 0.0f, 1.0f ); setupDefaultCameras( env, from, to, up, 0.1f, 100 ); } setupGraphics(); }
DumpClassListDemo::DumpClassListDemo( hkDemoEnvironment* env) : hkDefaultDemo(env) { hkString filePath("Common/Api/Serialize/Versioning/DumpClassList/"); hkString fileName = hkString(hkVersionUtil::getCurrentVersion()).replace("-","").replace(".","")+"Classes"; const char registryVariableName[] = "CustomStaticRegistry"; // Dump all currently reflected runtime classes // Open a stream to write the cpp header file { hkOstream outfile( hkString(filePath + fileName + ".h").cString() ); HK_ASSERT2( 0x215d080f, outfile.isOk(), "Could not open '" << fileName.cString() << "h' for writing." ); // extern the runtime classes into the header file hkVersionUtil::generateCppExternClassList(outfile, fileName.replace(".","_").asUpperCase().cString(), classes, registryVariableName); } // Open a stream to write the cpp source file { hkOstream outfile( hkString(filePath + fileName + ".cpp").cString() ); HK_ASSERT2( 0x215d0810, outfile.isOk(), "Could not open '" << fileName.cString() << "cpp' for writing." ); // set demos pch file const char* pchfilename = "demos/Demos.h"; // dump the runtime classes into the cpp file hkVersionUtil::generateCppClassList(outfile, classes, pchfilename, registryVariableName); } }
hkResult DestructibleHierarchy::updateRigidBodyOfDestructibleHierarchy(DestructibleHierarchy* hierarchy, unsigned int nodeIdx, hkpRigidBody* newOwningBody) { Node* node = &hierarchy->m_nodes[nodeIdx]; while(node->m_body == HK_NULL) { HK_ASSERT2(0xad789aae, node->m_parentIdx != INVALID_NODE_INDEX, "could not find the parent hkpRigidBody."); node = &hierarchy->m_nodes[node->m_parentIdx]; } //!! update for Evos if (node->m_body) { HK_ASSERT2(0xad789aee, node->m_initialTransformOfHierarchy == HK_NULL, "this body was alredy reassigned to a different hkpRigidBody once.."); node->m_initialTransformOfHierarchy = new hkTransform(node->m_body->getTransform()); node->m_body->removeReference(); node->m_body = newOwningBody; node->m_body->addReference(); return HK_SUCCESS; } HK_ASSERT2(0xad768dda, false, "Could not find the rigid body a hierarchy"); return HK_FAILURE; }
void hkDefaultPhysicsDemo::loadAndAddRigidBodies( hkLoader* loader, const char* filename ) { //hkString assetFile = hkAssetManagementUtil::getFilePath("Resources/Physics/levels/test_level.hkx"); hkString assetFile = hkAssetManagementUtil::getFilePath(filename); //hkString assetFile = hkAssetManagementUtil::getFilePath("Resources/Physics/levels/onemesh_test_level.hkx"); hkRootLevelContainer* container = loader->load( assetFile.cString() ); HK_ASSERT2(0x27343437, container != HK_NULL , "Could not load asset"); hkxScene* scene = reinterpret_cast<hkxScene*>( container->findObjectByType( hkxSceneClass.getName() )); HK_ASSERT2(0x27343635, scene, "No scene loaded"); m_env->m_sceneConverter->convert( scene, hkgAssetConverter::CONVERT_ALL ); hkpPhysicsData* physics = reinterpret_cast<hkpPhysicsData*>( container->findObjectByType( hkpPhysicsDataClass.getName() )); HK_ASSERT2(0x27343635, physics, "No physics loaded"); // Physics if (physics) { const hkArray<hkpPhysicsSystem*>& psys = physics->getPhysicsSystems(); // Tie the two together for (int i=0; i<psys.getSize(); i++) { hkpPhysicsSystem* system = psys[i]; // Associate the display and physics (by name) if (scene) { hkDefaultPhysicsDemo::addPrecreatedDisplayObjectsByName( psys[i]->getRigidBodies(), scene ); } // add the lot to the world m_world->addPhysicsSystem(system); } } }
WindChimesDemo::WindChimesDemo(hkDemoEnvironment* env) : hkDefaultPhysicsDemo(env), m_loader(HK_NULL) { const WindChimesVariant& variant = g_variants[m_variantId]; // // Create the sound manager. // // // Set up the camera // { hkVector4 from(6.0f, 0.0f, 3.0f); hkVector4 to (0.0f, 0.0f, 0.0f); hkVector4 up (0.0f, 0.0f, 1.0f); setupDefaultCameras( env, from, to, up ); } hkString assetFile = hkAssetManagementUtil::getFilePath(variant.m_assetName); m_loader = new hkLoader(); hkRootLevelContainer* container = m_loader->load( assetFile.cString() ); HK_ASSERT2(0xaefe9356, container != HK_NULL , "Could not load asset"); hkpPhysicsData* physics = static_cast<hkpPhysicsData*>( container->findObjectByType( hkpPhysicsDataClass.getName() ) ); HK_ASSERT2(0x245982ae, physics != HK_NULL, "Could not find physics data in root level object" ); { m_world = new hkpWorld( *physics->getWorldCinfo() ); m_world->lock(); hkpAgentRegisterUtil::registerAllAgents(m_world->getCollisionDispatcher()); setupGraphics(); } m_world->addPhysicsSystem( physics->getPhysicsSystems()[0] ); // There are 5 pipes in the circular chimes asset, called Chime0, Chime1...etc. for( int i = 0; i < variant.m_numChimes; i++ ) { hkString chimeName; chimeName.printf( "Chime%d", i ); hkpRigidBody* chime = physics->findRigidBodyByName( chimeName.cString() ); HK_ASSERT(0x99862353, chime); new WindChimesCollisionListener( chime, i % variant.m_numDifferentChimeSizes ); } m_world->unlock(); }
// the original shapes should be intact.. hkpShape* hkFlattenShapeHierarchyUtil::flattenHierarchy(const hkpShape* shape) { hkArray<hkpExtendedMeshShape::TrianglesSubpart> triangleParts; hkArray<hkpConvexShape*> convexShapes; hkArray<hkpShape*> otherShapes; // Get all leaf shapes with their 'wrapping' transform shapes onto the list getLeafShapesFromShape(shape, hkTransform::getIdentity(), false, triangleParts, convexShapes, otherShapes); hkpShape* list = HK_NULL; hkArray<hkpShape*> allShapes; for (int s = 0; s < convexShapes.getSize(); s++) { allShapes.pushBack(static_cast<hkpShape*>(convexShapes[s])); } for (int s = 0; s < otherShapes.getSize(); s++) { allShapes.pushBack(otherShapes[s]); } HK_ASSERT2(0xad6788c6, allShapes.getSize(), "No shapes to process!."); if (allShapes.getSize() > 1) { list = new hkpListShape(allShapes.begin(), allShapes.getSize()); for (int s = 0; s < allShapes.getSize(); s++) { allShapes[s]->removeReference(); } } else { return allShapes[0]; } HK_ASSERT2(0xad789aaa, 0 == list->getUserData(), "Overwriting user data ?"); // WE'RE MARKING THIS BODY FOR INCLUSION IN THE FIXED UBER SHAPE LATER .. // -- only if the number of child shapes is greater than 20 // if (list->getNumChildShapes() > 20) { list->setUserData(0x0000beef); } return list; }
/// Initialize multi-threading sharedThreadData and create threads. vHavokCpuJobThreadPool::vHavokCpuJobThreadPool(const vHavokCpuJobThreadPoolCinfo& ci) { m_isRunning = false; m_threadName = ci.m_threadName; m_stackSize = ci.m_stackSize; m_sharedThreadData.m_localHavokStackSize = ci.m_havokStackSize; m_sharedThreadData.m_timerBufferAllocation = ci.m_timerBufferPerThreadAllocation; int numThreads = ci.m_numThreads; if (numThreads >= MAX_NUM_THREADS) { HK_WARN( 0xf0defd23, "You requested 6 or more threads, this is not supported by vHavokCpuJobThreadPool" ); numThreads = MAX_NUM_THREADS - 1; } m_sharedThreadData.m_numThreads = numThreads; m_sharedThreadData.m_OnWorkerThreadCreatedPtr = ci.m_OnWorkerThreadCreatedPtr; m_sharedThreadData.m_OnWorkerThreadFinishedPtr = ci.m_OnWorkerThreadFinishedPtr; #if defined(HK_PLATFORM_XBOX360) int numCores = 3; int numThreadsPerCore = 2; #elif defined(HK_PLATFORM_WIN32) hkHardwareInfo info; hkGetHardwareInfo( info ); int numCores = info.m_numThreads; //This reports actual cores - ignoring hyperthreading int numThreadsPerCore = 1; numCores /= numThreadsPerCore; #endif for (int i = 0; i < numThreads; i++ ) { WorkerThreadData& data = m_workerThreads[i]; data.m_sharedThreadData = &m_sharedThreadData; data.m_threadId = i + 1; // don't start with thread 0 (assume that is the calling thread) data.m_monitorStreamBegin = HK_NULL; data.m_monitorStreamEnd = HK_NULL; data.m_killThread = false; data.m_clearTimers = false; #if defined(HK_PLATFORM_XBOX360) || defined(HK_PLATFORM_WIN32) if (ci.m_hardwareThreadIds.getSize() > 0) { HK_ASSERT2( 0x975fe134, ci.m_hardwareThreadIds.getSize() >= numThreads, "If you initialize hardware thread ids, you must give an ID to all threads"); data.m_hardwareThreadId = ci.m_hardwareThreadIds[i]; } else { //X360: { 2,4,1,3,5, 0, 2,4,.. } int procGroup = (data.m_threadId % numCores) * numThreadsPerCore; data.m_hardwareThreadId = procGroup + (numThreadsPerCore > 1? ((data.m_threadId / numCores) % numThreadsPerCore) : 0 ); } #endif data.m_thread.startThread( &hkWorkerThreadFunc, &data, m_threadName, m_stackSize ); } hkReferencedObject::setLockMode( hkReferencedObject::LOCK_MODE_AUTO); }
void HK_CALL popDoubleConversionCheck() { g_checkDoubleStackIndex--; HK_ASSERT2(0x7511c51f, g_checkDoubleStackIndex >= 0, "Double check underflow"); g_checkDoubles = g_checkDoubleStack[g_checkDoubleStackIndex]; }
void DestructibleHierarchy::insertShapeKeysIntoDestructibleHierarchy(hkpRigidBody* fixedBody) { const hkpShape* shape = fixedBody->getCollidable()->getShape(); HK_ASSERT2(0xad6738dd, shape->getType() == HK_SHAPE_MOPP, "The fixed body must have a mopp."); const hkpMoppBvTreeShape* mopp = static_cast<const hkpMoppBvTreeShape*>(shape); hkArray<hkpShapeKey> shapeKeys; hkMoppFindAllVirtualMachine_getAllKeys(mopp->getMoppCode(), &shapeKeys ); // ultimately we want to skip looking through triangles here.. hkpShapeCollection::ShapeBuffer shapeBuffer; //hkpShape* childShape = reinterpret_cast<hkpShape*>(&shapeBuffer); for (int k = 0; k < shapeKeys.getSize(); k++) { hkpShapeKey shapeKey = shapeKeys[k]; const hkpShape* childShape = mopp->getShapeCollection()->getChildShape(shapeKey, shapeBuffer); hkUint16 lookupIdx = hkUint16((hkUlong(childShape->getUserData())>>16) & 0xffff); if (childShape->getUserData()) { int oyo = 0; oyo++; } if (lookupIdx) { DestructibleHierarchy* hierarchy = DestructibleHierarchy::getDestructibleHierarchy(lookupIdx); unsigned int nodeIdx = DestructibleHierarchy::getNodeIndex(lookupIdx); DestructibleHierarchy::Node* node = &hierarchy->m_nodes[nodeIdx]; node->m_shapeKey = shapeKey; } } }
hkpRigidBody* DestructibleHierarchy::buildRigidBody(const hkpShape* shape, DestructibleHierarchy* hierarchy, int nodeIdx) { hkpRigidBodyCinfo info; info.m_shape = shape; hkpMassProperties massProperties; hkpInertiaTensorComputer::computeShapeVolumeMassProperties(shape, 1000.0f, massProperties); info.m_centerOfMass = massProperties.m_centerOfMass; info.m_inertiaTensor.setMul(massProperties.m_volume, massProperties.m_inertiaTensor); info.m_mass = massProperties.m_volume * massProperties.m_mass; info.m_motionType = hkpMotion::MOTION_BOX_INERTIA; info.m_qualityType = HK_COLLIDABLE_QUALITY_DEBRIS; // Many broken off elements will have thin box inertia, so we limit their angular vel. //info.m_maxAngularVelocity = 5.0f; hkpRigidBody* body = new hkpRigidBody(info); if (nodeIdx == INVALID_NODE_INDEX) { nodeIdx = hierarchy->m_rootNodeIdx; } hkpRigidBody*& bodyPtr = hierarchy->m_nodes[nodeIdx].m_body; HK_ASSERT2(0xad7899dd, bodyPtr == HK_NULL, "Overwriting body pointer"); bodyPtr = body; body->addReference(); return body; }
void vHavokBehaviorComponent::InitVisionCharacter( VisBaseEntity_cl* entityOwner ) { m_entityOwner = entityOwner; m_isListeningToEvents = false; vHavokBehaviorModule* behaviorModule = vHavokBehaviorModule::GetInstance(); if( behaviorModule != HK_NULL ) { HK_ASSERT2(0x2f1c46e9, m_character == HK_NULL, "Character should be freed first." ); // Try to create a character. May fail if path/file settings are not specified m_character = behaviorModule->addCharacter( this ); if( m_character != HK_NULL ) { // Set up the animation config and create bone mapping UpdateAnimationAndBoneIndexList(); // Set character transform UpdateHavokTransformFromVision(); // Update the Physics UpdateBehaviorPhysics(); } } }
// // Creates a scaled version of the given hkpMoppBvTreeShape // Both the triangle data and hkpMoppCode are shared between the instances, resulting in very little memory overhead. // Also, since we use existing Havok classes, the resulting shapes can be simulated on the SPU without any additional work. // There are a few important things to note: // 1) If the world is serialized out after the scaled instance is created (e.g. with hkpHavokSnapshot), multiple copies of // the code will be stored. // 2) The scaled instance must not be used after the original is removed from memory. This is because the hkpMoppCode // isn't reference counted. // 3) Scaling is currently only supported for hkpExtendedMeshShape. If the hkpExtendedMeshShape has ShapesSubpart, these // will NOT be scaled correctly. // hkpMoppBvTreeShape* MoppInstancingDemo::createScaledMopp(const hkpMoppBvTreeShape* originalMopp, hkReal relativeScale) { const hkpShape* oldContainer = originalMopp->getShapeCollection(); HK_ASSERT2(0x530b238a, oldContainer->getType() == HK_SHAPE_EXTENDED_MESH, "This function only scales hkpExtendedMeshShapes"); const hkpExtendedMeshShape* oldMesh = static_cast<const hkpExtendedMeshShape*>(oldContainer); // Create an new mesh, and copy the mesh data // Note that the underlying vertex data isn't duplicated, only the pointer information, etc. hkpExtendedMeshShape* newMesh; { newMesh= new hkpExtendedMeshShape(oldMesh->getRadius(), oldMesh->getNumBitsForSubpartIndex()); // Adjust the mesh scaling for the copy. // We have to do this BEFORE we add the triangle subparts, or else the AABB won't be computed correctly hkVector4 scaleMult; scaleMult.setAll3(relativeScale); hkVector4 newMeshScale; newMeshScale.setMul4( scaleMult, oldMesh->getScaling() ); newMesh->setScaling(newMeshScale); for (int i=0; i<oldMesh->getNumTrianglesSubparts(); i++) { newMesh->addTrianglesSubpart(oldMesh->getTrianglesSubpartAt(i)); } HK_ASSERT2(0x401d4c68, oldMesh->getNumShapesSubparts() == 0, "Can't scale shape subparts!"); } // Create a new hkpMoppCode. The internal data points to the original data, so the memory overhead is negligible. const hkpMoppCode* oldCode = originalMopp->getMoppCode(); hkpMoppCode* newcode; { hkpMoppCode::CodeInfo newCodeInfo = oldCode->m_info; // This is the tricky part. Adjusting the offset in this way will ensure that the collisions are handled properly. hkVector4 codeMult; codeMult.set( relativeScale, relativeScale, relativeScale, 1.0f / relativeScale ); newCodeInfo.m_offset.mul4(codeMult); // Use the newly scaled hkpMoppCode::CodeInfo, and point to the old data newcode = new hkpMoppCode(newCodeInfo, oldCode->m_data.begin(), oldCode->m_data.getSize()); } hkpMoppBvTreeShape* newMopp = new hkpMoppBvTreeShape(newMesh, newcode); newMesh->removeReference(); newcode->removeReference(); return newMopp; }
void HK_CALL pushDoubleConversionCheck(hkBool enable) { g_checkDoubleStack[g_checkDoubleStackIndex] = g_checkDoubles; g_checkDoubleStackIndex++; HK_ASSERT2(0x6c9871d4, g_checkDoubleStackIndex < 255, "Double check overflow"); g_checkDoubles = enable; }
BridgeLand::BridgeLand(hkReal maxHeight, int numTiles, hkReal canyonWidthRatio, hkBool smoothBreak) : FlatLand(numTiles) { HK_ASSERT2(0xaf25fe34, canyonWidthRatio > 0.0f && canyonWidthRatio < 1.0f, "The canyon's width ratio must be between (and excluding) 0 and 1."); m_maxHeight = maxHeight; m_halfCanyonWidthRatio = canyonWidthRatio * 0.5f; m_smoothBreak = smoothBreak; }
hkUint16 DestructibleHierarchy::getLookupIndexOfNewDestructibleNode(DestructibleHierarchy* hierarchy, int nodeIdxInDestructibleHierarchy) { HK_ASSERT2(0xad678bad, s_nodeInfos, "Array not created."); if (s_nodeInfos->isEmpty()) { // dummy node, so that index of valid nodes in non-zero s_nodeInfos->expandOne(); } unsigned int lookupIdx = s_nodeInfos->getSize(); NodeInformation& info = s_nodeInfos->expandOne(); info.m_destructibleHierarchy = hierarchy; info.m_nodeIdx = nodeIdxInDestructibleHierarchy; HK_ASSERT2(0xad6288dd, s_nodeInfos->getSize() <= (1<<16), "Too many destructible nodes in the lookup table -- we only have 16 bits for the index into the table" ); return hkUint16(lookupIdx); }
void vHavokCpuJobThreadPool::removeThread() { HK_ASSERT2(0xad67bd89, !m_isRunning, "You can only add or remove working threads via calls from the master thread and not between processJobs() and waitForCompletion() calls. "); HK_ASSERT2(0xcede9735, m_sharedThreadData.m_numThreads > 0, "You cannot set a negative number of threads" ); --m_sharedThreadData.m_numThreads; WorkerThreadData& data = m_workerThreads[m_sharedThreadData.m_numThreads]; // Signal the thread to be killed, and release the thread data.m_killThread = true; data.m_semaphore.release(); // Wait until the thread actually finishes m_sharedThreadData.m_workerThreadFinished.acquire(); data.m_thread.joinThread(); }
void vHavokCpuJobThreadPool::addThread() { HK_ASSERT2(0xad67bd88, ! m_isRunning, "You can only add or remove working threads via calls from the master thread and not between processAllJobs() and waitForCompletion() calls. "); #if defined(HK_PLATFORM_HAS_SPU) HK_ASSERT2(0xcede9735, m_sharedThreadData.m_numThreads < 2, "Only 2 PPU threads are supported on the PS3" ); #endif HK_ASSERT2(0xcede9734, m_sharedThreadData.m_numThreads < MAX_NUM_THREADS, "A maximum of 6 threads are supported." ); WorkerThreadData& data = m_workerThreads[m_sharedThreadData.m_numThreads]; data.m_sharedThreadData = &m_sharedThreadData; data.m_threadId = m_sharedThreadData.m_numThreads+1; data.m_monitorStreamBegin = HK_NULL; data.m_monitorStreamEnd = HK_NULL; data.m_killThread = false; data.m_clearTimers = false; #if defined(HK_PLATFORM_WIN32) || defined(HK_PLATFORM_XBOX360) #if defined HK_PLATFORM_XBOX360 int numCores = 3; int numThreadsPerCore = 2; #elif defined(HK_PLATFORM_WINRT) //XX GetSystemInfoEx(&sysInfo); int numCores = 4; int numThreadsPerCore = 1; #else SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); int numCores = sysInfo.dwNumberOfProcessors; int numThreadsPerCore = 1; //XX Work out hyper threading (not just logical versus physical processors) numCores /= numThreadsPerCore; #endif //X360: { 2,4,1,3,5, 0, 2,4,.. } int procGroup = (data.m_threadId % numCores) * numThreadsPerCore; data.m_hardwareThreadId = procGroup + (numThreadsPerCore > 1? ((data.m_threadId / numCores) % numThreadsPerCore) : 0 ); #endif data.m_thread.startThread( &hkWorkerThreadFunc, &data, m_threadName, m_stackSize); m_sharedThreadData.m_numThreads++; }
DoorSpring::DoorSpring(const hkpConstraintInstance* hinge, const hkReal strength, const hkReal damping) : hkpUnaryAction(hinge->getEntityA()) , m_hinge(hinge) , m_strength(strength) , m_damping(damping) , m_angVel(0.0f) , m_torque(0.0f) { // Assume the door is hung vertically m_axis = m_entity->getWorld()->getGravity(); m_axis.normalize3(); HK_ASSERT2(0x0, !m_entity->isFixed(), "Entity A must be dynamic, only because that is the entity that I pass to the hkpUnaryAction constructor."); }
void HK_CALL VehicleApiUtils::reorient(hkBool buttonPressed, hkpAction* action, hkpWorld* world) { HK_ASSERT2(0x0, action != HK_NULL, "Action is NULL in VehicleApiUtils::reorient()!"); HK_ASSERT2(0x0, world != HK_NULL, "World is NULL in VehicleApiUtils::reorient()!"); if ( buttonPressed ) { // If not already added, add it if ( action->getWorld() == HK_NULL ) { world->addAction(action); } } else { // If not already removed, remove it if ( action->getWorld() != HK_NULL ) { world->removeAction(action); } } }
void vHavokCpuJobThreadPool::processAllJobs( hkJobQueue* queue, hkJobType firstJobType_unused ) { m_sharedThreadData.m_jobQueue = queue; HK_ASSERT2(0xad56dd77, m_isRunning == false, "Calling vHavokCpuJobThreadPool::processJobs() for the second time, without having called vHavokCpuJobThreadPool::waitForCompletion()."); m_isRunning = true; for (int i = m_sharedThreadData.m_numThreads - 1; i >=0; i--) { WorkerThreadData& data = m_workerThreads[i]; data.m_semaphore.release(); } }
hkaSkeleton* gkRigManager::loadRig( const TCHAR* name ) { // check if we had one RigMap::iterator it = m_mapRigs.find(name); if (it != m_mapRigs.end()) { return it->second; } CHAR szPath[MAX_PATH] = ""; #ifdef UNICODE WideCharToMultiByte(CP_ACP, 0, name, -1, szPath, MAX_PATH, NULL, NULL); #else _tcscpy_s( szPath, MAX_PATH, name ); #endif hkStringBuf assetFile(szPath); hkAssetManagementUtil::getFilePath(assetFile); hkRootLevelContainer* container = getAnimationPtr()->getGlobalLoader()->load( szPath ); HK_ASSERT2(0x27343437, container != HK_NULL , "Could not load asset"); if (!container) { return false; } hkaAnimationContainer* ac = reinterpret_cast<hkaAnimationContainer*>( container->findObjectByType( hkaAnimationContainerClass.getName() )); HK_ASSERT2(0x27343435, ac && (ac->m_skeletons.getSize() > 0), "No skeleton loaded"); hkaSkeleton* skeleton = ac->m_skeletons[0]; m_mapRigs.insert(RigMap::value_type(name, skeleton)); return skeleton; }
void MyExtendedUserDataListener::contactPointConfirmedCallback( hkpContactPointConfirmedEvent& event) { // Get num extra user datas for that body const int numDatas = event.getNumExtendedUserDatas(event.m_callbackFiredFrom); // Create a temp array to store the data hkInplaceArray<hkpContactPointProperties::UserData, 8> data; data.setSize(numDatas); // Get the data event.getExtendedUserDatas(event.m_callbackFiredFrom, data.begin(), numDatas); int id = data[numDatas-1]; HK_ASSERT2(0xad755512, m_contactInfos[id].m_confirmed == false && m_contactInfos[id].m_processed >=1, "Callbacks don't match."); // We might have multiple process callbacks already if the point was involved in a toi m_contactInfos[id].m_confirmed = true; }
void vHavokBehaviorComponent::InitVisionCharacter( VisBaseEntity_cl* entityOwner ) { m_entityOwner = entityOwner; m_isListeningToEvents = false; vHavokBehaviorModule* behaviorModule = vHavokBehaviorModule::GetInstance(); if( behaviorModule != HK_NULL ) { HK_ASSERT2(0x2f1c46e9, m_character == HK_NULL, "Character should be freed first." ); // Try to create a character. May fail if path/file settings are not specified m_character = behaviorModule->addCharacter( this ); if( m_character != HK_NULL ) { // Set up the animation config and create bone mapping UpdateAnimationAndBoneIndexList(); // Set character transform UpdateHavokTransformFromVision(); // Update the Physics UpdateBehaviorPhysics(); // Tag ragdoll rigid bodies to be ignored by nav mesh cutting hkbRagdollDriver* ragdollDriver = m_character->getRagdollDriver(); if (ragdollDriver) { hkbRagdollInterface* ragdollInterface = ragdollDriver->getRagdollInterface(); if (ragdollInterface) { for (int i = 0; i < ragdollInterface->getNumBones(); i++) { hkpRigidBody* rigidBody = static_cast<hkpRigidBody*>(ragdollInterface->getRigidBodyOfBone(i)); if (rigidBody) { rigidBody->addProperty(VHAVOK_PROPERTY_DO_NO_CUT_NAV_MESH, 1); } } } } } } }
hkResult BrowseDemo::readAndSetupPackfile(const char* filename) { hkRefPtr<hkResource> res = hkSerializeUtil::load(filename); if( res ) { // Keep a handle to the loaded data m_packfileData = res; // Get the top level object in the file m_contents = res->getContents<hkRootLevelContainer>(); HK_ASSERT2(0xa6451543, m_contents != HK_NULL, "Could not load root level obejct" ); if( hkpPhysicsData* physicsData = static_cast<hkpPhysicsData*>( m_contents->findObjectByType( hkpPhysicsDataClass.getName() ) ) ) { //HK_ASSERT2(0xa6451544, physicsData != HK_NULL, "Could not find physics data in root level object" ); //HK_ASSERT2(0xa6451535, physicsData->getWorldCinfo() != HK_NULL, "No physics cinfo in loaded file - cannot create a hkWorld" ); if( physicsData->getWorldCinfo() ) { // Create a world and add the physics systems to it m_world = new hkpWorld( *physicsData->getWorldCinfo() ); m_world->lock(); // Register all collision agents hkpAgentRegisterUtil::registerAllAgents( m_world->getCollisionDispatcher() ); // Add all the physics systems to the world for ( int i = 0; i < physicsData->getPhysicsSystems().getSize(); ++i ) { m_world->addPhysicsSystem( physicsData->getPhysicsSystems()[i] ); } // Setup graphics - this creates graphics objects for all rigid bodies and phantoms in the world setupGraphics(); m_world->unlock(); } } } return res ? HK_SUCCESS : HK_FAILURE; }
void updatePackfileDataVersion(hkArray<char>& ioBuf) { hkArray<char> origBuf; origBuf.swap(ioBuf); // Load the entire file inplace hkBinaryPackfileReader reader; reader.loadEntireFileInplace(origBuf.begin(), origBuf.getSize()); // check versioning if( hkVersionUtil::updateToCurrentVersion( reader, hkVersionRegistry::getInstance() ) != HK_SUCCESS ) { HK_WARN_ALWAYS(0, "Couldn't update version, skipping.\n"); } // Get the top level object in the file hkRootLevelContainer* container = static_cast<hkRootLevelContainer*>( reader.getContents( hkRootLevelContainerClass.getName() ) ); HK_ASSERT2(0xa6451543, container != HK_NULL, "Could not load root level obejct" ); hkOstream nativeFile(ioBuf); hkBool success = hkpHavokSnapshot::save(container, hkRootLevelContainerClass, nativeFile.getStreamWriter(), true); HK_ASSERT(0xa6451545, success == true); }
hkDemo::Result DestructibleWallsDemo::stepDemo() { m_world->lock(); { const hkgPad* pad = m_env->m_gamePad; // Cannon control + drawing // check to see if the user has pressed one of the control keys: int x = ((pad->getButtonState() & HKG_PAD_DPAD_LEFT) != 0)? -1:0; x = ((pad->getButtonState() & HKG_PAD_DPAD_RIGHT) != 0)? 1:x; int y = ((pad->getButtonState() & HKG_PAD_DPAD_DOWN) != 0)? -1:0; y = ((pad->getButtonState() & HKG_PAD_DPAD_UP) != 0)? 1:y; m_shootingDirX += x * 0.015f; m_shootingDirY += y * 0.015f; hkVector4 canonStart( m_centerOfScene ); canonStart(2) += m_options.m_WallsWidth*2.0f; hkVector4 canonDir( m_shootingDirX, m_shootingDirY, -1.0f ); canonDir.normalize3(); // display the canon direction { hkpWorldRayCastInput in; in.m_from = canonStart; in.m_to.setAddMul4( in.m_from, canonDir, 100.0f ); in.m_filterInfo = 0; hkpWorldRayCastOutput out; m_world->castRay( in , out ); hkVector4 hit; hit.setInterpolate4( in.m_from, in.m_to, out.m_hitFraction ); HK_DISPLAY_LINE( in.m_from, hit, 0x600000ff ); if ( out.hasHit() ) { HK_DISPLAY_ARROW( hit, out.m_normal, 0x60ff00ff ); } HK_DISPLAY_ARROW( canonStart, canonDir, hkColor::CYAN ); } // Shooting bullets { hkBool shooting = false; if ( pad->wasButtonPressed(HKG_PAD_BUTTON_1)!= 0 ) { shooting = true; } if ( pad->isButtonPressed(HKG_PAD_BUTTON_2)!= 0) { if ( m_gunCounter-- < 0 ) { shooting = true; m_gunCounter = 5; } } if ( shooting ) { hkpMassProperties result; hkpInertiaTensorComputer::computeSphereVolumeMassProperties(m_options.m_cannonBallRadius, m_options.m_cannonBallMass, result); hkpSphereShape* sphereShape = new hkpSphereShape(m_options.m_cannonBallRadius); hkVector4 spherePos(-20.0f, 0.0f + m_options.m_cannonBallRadius, 200.0f); hkpRigidBodyCinfo sphereInfo; sphereInfo.m_mass = result.m_mass; sphereInfo.m_centerOfMass = result.m_centerOfMass; sphereInfo.m_inertiaTensor = result.m_inertiaTensor; sphereInfo.m_shape = sphereShape; sphereInfo.m_motionType = hkpMotion::MOTION_BOX_INERTIA; sphereInfo.m_position = canonStart; sphereInfo.m_qualityType = HK_COLLIDABLE_QUALITY_BULLET; sphereInfo.m_linearVelocity.setMul4( 100.0f, canonDir ); hkpRigidBody* bullet = new hkpRigidBody( sphereInfo ); sphereInfo.m_shape->removeReference(); m_world->addEntity( bullet ); bullet->removeReference(); } } } // release the world m_world->unlock(); // step demo hkDemo::Result res = hkDefaultPhysicsDemo::stepDemo(); //HK_TIMER_BEGIN_LIST( "Update Walls", "Update Walls"/*HK_NULL*/); HK_TIMER_BEGIN( "Update Walls", "update walls"/*HK_NULL*/); if(m_collisionDetectionType == PARALLEL) { HK_ASSERT2(0x7274e9ec, m_fractureUtility!=HK_NULL ,"The parallel simulation wasn't set!!"); // and update walls m_fractureUtility->Update(); } HK_TIMER_END(); HK_TIMER_BEGIN("DebugDisplay", HK_NULL); // DEBUG DISPLAY if(m_fractureUtility->getSimulation() && m_options.m_showDebugDisplay) { // applied impulses for(int i=0; i<m_fractureUtility->getSimulation()->debugImpulses.getSize(); i+=2) { hkVector4 start( m_fractureUtility->getSimulation()->debugImpulses[i] ); hkVector4 end( m_fractureUtility->getSimulation()->debugImpulses[i+1] ); end.mul4( 0.01f ); HK_DISPLAY_ARROW(start, end , 0x00000000); } if(m_fractureUtility->getSimulation()->debugImpulses.getSize() > 100 ) m_fractureUtility->getSimulation()->debugImpulses.clear(); // bricks positions in parallel simulation hkArray<hkVector4> positions; m_fractureUtility->getSimulation()->getAllBricksPositions( positions ); for(int i=0; i<positions.getSize(); ++i) { drawBrick(positions[i] , m_brickHalfExtents); } positions.clear(); // edges of union find for(int i=0; i<m_fractureUtility->getSimulation()->debugEdges.getSize(); i+=2) { HK_DISPLAY_LINE(m_fractureUtility->getSimulation()->debugEdges[i], m_fractureUtility->getSimulation()->debugEdges[i+1] , 0x00000000); } } HK_TIMER_END(); if(m_collisionDetectionType == PARALLEL) { hkArray< hkVector4 > planes; m_fractureUtility->getSimulation()->getAllDebugfracturePlanes(planes); for(int i=0; i< planes.getSize()-2; ++i) { if(planes[i].length3()!=0) { hkVector4 offset(.0f, .5f, .0f/*.5f, .5f, .5f*/); HK_DISPLAY_PLANE(planes[i], offset, 0.5f, 0xffffffff); } } if(!planes.isEmpty() && planes[planes.getSize()-1].length3()!=0) { hkVector4 offset(.0f, .5f, .0f/*.5f, .5f, .5f*/); HK_DISPLAY_PLANE(planes[planes.getSize()-2], offset, 0.5f, 0x00000000); HK_DISPLAY_PLANE(planes[planes.getSize()-1], offset, 0.5f, 0xffff0000); } hkArray<hkVector4> cpts; hkArray<hkVector4> impulses; m_fractureUtility->getSimulation()->getAllDebugImpulsesAndContactPoints(impulses, cpts); for(int i=0; i< cpts.getSize(); ++i) { HK_DISPLAY_ARROW(cpts[i], impulses[i] , 0x00000000); } } return res; }
void DestructibleHierarchy::breakOffNode(int nodeIdx, hkArray<DestructibleHierarchyCollisionEvent>& collisionEvents) { Node* node = &m_nodes[nodeIdx]; int immediateParent = node->m_parentIdx; HK_ASSERT2(0xad78d9dd, immediateParent != INVALID_NODE_INDEX, "where's the parent?"); // detach from parent Node* immediateParentNode = &m_nodes[immediateParent]; HK_ON_DEBUG( int initChildCount = immediateParentNode->m_childrenIdx.getSize() ); for (int c = 0; c < immediateParentNode->m_childrenIdx.getSize(); c++) { if (immediateParentNode->m_childrenIdx[c] == nodeIdx) { immediateParentNode->m_childrenIdx.removeAtAndCopy(c); break; } } HK_ASSERT2(0xad78ddaa, initChildCount-1 == immediateParentNode->m_childrenIdx.getSize(), "child not detached!" ); hkTransform accumulatedTransform = immediateParentNode->m_transform; // Find parent rigid body.. int parentIdx = immediateParent; Node* parentNode = immediateParentNode; while(parentNode->m_body == HK_NULL) { parentIdx = parentNode->m_parentIdx; parentNode = &m_nodes[parentIdx]; hkTransform tmp; tmp.setMul(parentNode->m_transform, accumulatedTransform); accumulatedTransform = tmp; HK_ASSERT2(0xad678daa, parentNode, "No parent rigid body found!"); } // Create new body hkpRigidBody* newBody; { hkpShape* shape = buildShape(nodeIdx); hkpShape* flatShape = hkFlattenShapeHierarchyUtil::flattenHierarchy(shape); shape->removeReference(); newBody = DestructibleHierarchy::buildRigidBody(flatShape, this, nodeIdx); flatShape->removeReference(); const hkSmallArray< hkpCollisionListener* >& listeners = parentNode->m_body->getCollisionListeners(); for (int i = 0; i < listeners.getSize(); i++) { newBody->addCollisionListener(listeners[i]); } } // init velocites for new body { // reposition the body hkTransform parentTransform; if (parentNode->m_initialTransformOfHierarchy) { parentTransform = *parentNode->m_initialTransformOfHierarchy; } else { parentTransform = parentNode->m_body->getTransform(); } hkTransform newBodyTransform; newBodyTransform.setMul( parentTransform, accumulatedTransform ); newBody->setTransform(newBodyTransform); // compute velocities hkVector4 linVel = parentNode->m_body->getLinearVelocity(); hkVector4 angVel = parentNode->m_body->getAngularVelocity(); hkVector4 relCm; relCm.setSub4(newBody->getCenterOfMassInWorld(), parentNode->m_body->getCenterOfMassInWorld()); hkVector4 extraLin; extraLin.setCross(angVel, relCm); linVel.add4(extraLin); newBody->setLinearVelocity( linVel ); newBody->setAngularVelocity( angVel ); } // set newBody position parentNode->m_body->getWorld()->addEntity(newBody); newBody->removeReference(); newBody = HK_NULL; // Update shape of parent body if (!parentNode->m_body->isFixed()) { hkpShape* shape = buildShape(parentIdx); if (shape) { hkVector4 oldCm = parentNode->m_body->getCenterOfMassInWorld(); hkpShape* flatShape = hkFlattenShapeHierarchyUtil::flattenHierarchy(shape); updateShapeOfRigidBody(flatShape, parentNode->m_body); shape->removeReference(); flatShape->removeReference(); hkVector4 relCm; relCm.setSub4(parentNode->m_body->getCenterOfMassInWorld(), oldCm); hkVector4 extraLin; extraLin.setCross(parentNode->m_body->getAngularVelocity(), relCm); hkVector4 linVel; linVel.setAdd4(parentNode->m_body->getLinearVelocity(), extraLin); parentNode->m_body->setLinearVelocity(linVel); } else { parentNode->m_body->getWorld()->removeEntity(parentNode->m_body); parentNode->m_body->removeReference(); parentNode->m_body = HK_NULL; } } else // if (!parentNode->m_body->isFixed()) { // if we're breaking off of a fixed shape -- this must be the one fixed mopp shape const hkpShape* shape = parentNode->m_body->getCollidable()->getShape(); HK_ASSERT2(0xad1788dd, shape->getType() == HK_SHAPE_MOPP, "The fixed body must have a mopp."); const hkpMoppBvTreeShape* mopp = static_cast<const hkpMoppBvTreeShape*>(shape); // Remove shapeKeys from mopp HK_ACCESS_CHECK_OBJECT(parentNode->m_body->getWorld(), HK_ACCESS_RW ); hkArray<hkpShapeKey> brokenOffShapeKeys; collectShapeKeys(nodeIdx, brokenOffShapeKeys); for (int i = brokenOffShapeKeys.getSize()-1; i >=0; i--) { if(brokenOffShapeKeys[i] == HK_INVALID_SHAPE_KEY) { brokenOffShapeKeys.removeAt(i); } else { const hkpMoppBvTreeShape* moppShape = static_cast<const hkpMoppBvTreeShape*>( parentNode->m_body->getCollidable()->getShape() ); removeSubShapeFromDisplay(parentNode->m_body, const_cast<hkpMoppBvTreeShape*>(moppShape), brokenOffShapeKeys[i]); } } hkpRemoveTerminalsMoppModifier modifier(mopp->getMoppCode(), mopp->getShapeCollection(), brokenOffShapeKeys); modifier.applyRemoveTerminals( const_cast<hkpMoppCode*>(mopp->getMoppCode()) ); // disable contact points for the removed shapes.. hkPointerMap<hkpShapeKey, int> shapeKeyMap; shapeKeyMap.reserve(brokenOffShapeKeys.getSize()); for (int k = 0; k < brokenOffShapeKeys.getSize(); k++) { shapeKeyMap.insert(brokenOffShapeKeys[k], 0); } for (int e = 0; e < collisionEvents.getSize(); e++) { if (collisionEvents[e].m_contactMgr) { hkpShapeKey key = collisionEvents[e].m_shapeKey; hkPointerMap<hkpShapeKey, int>::Iterator it = shapeKeyMap.findKey(key); if (shapeKeyMap.isValid(it) && collisionEvents[e].m_body == parentNode->m_body) { static_cast<hkpDynamicsContactMgr*>(collisionEvents[e].m_contactMgr)->getContactPoint(collisionEvents[e].m_contactPointId)->setDistance(100000.0f); collisionEvents.removeAt(e); e--; } } else { collisionEvents.removeAt(e); e--; } } } }
MotionExtractionDemo::MotionExtractionDemo( hkDemoEnvironment* env ) : hkDefaultAnimationDemo(env) { // Disable warnings: if no renderer if( hkString::strCmp( m_env->m_options->m_renderer, "n" ) == 0 ) { hkError::getInstance().setEnabled(0xf0d1e423, false); //'Could not realize an inplace texture of type PNG.' } // want to do software skinning always in this demo // see HardwareSkinningDemo for how to setup hardware palettes etc m_env->m_sceneConverter->setAllowHardwareSkinning(false); // // Setup the camera // { hkVector4 from( 3,3,1 ); hkVector4 to ( 0,0,0 ); hkVector4 up ( 0.0f, 0.0f, 1.0f ); setupDefaultCameras( env, from, to, up, 0.1f, 100 ); // so we can use the sticks on consoles if (m_env->m_options->m_trackballMode == 0) { m_forcedOnTrackball = true; m_env->m_window->getViewport(0)->setNavigationMode(HKG_CAMERA_NAV_TRACKBALL); } } m_loader = new hkLoader(); // Convert the scene { hkString assetFile = hkAssetManagementUtil::getFilePath("Resources/Animation/Scene/hkScene.hkx"); hkRootLevelContainer* container = m_loader->load( assetFile.cString() ); HK_ASSERT2(0x27343437, container != HK_NULL , "Could not load asset"); hkxScene* scene = reinterpret_cast<hkxScene*>( container->findObjectByType( hkxSceneClass.getName() )); HK_ASSERT2(0x27343635, scene, "No scene loaded"); removeLights(m_env); env->m_sceneConverter->convert( scene ); } // Get the rig { hkString assetFile = hkAssetManagementUtil::getFilePath("Resources/Animation/HavokGirl/hkRig.hkx"); hkRootLevelContainer* container = m_loader->load( assetFile.cString() ); HK_ASSERT2(0x27343437, container != HK_NULL , "Could not load asset"); hkaAnimationContainer* ac = reinterpret_cast<hkaAnimationContainer*>( container->findObjectByType( hkaAnimationContainerClass.getName() )); HK_ASSERT2(0x27343435, ac && (ac->m_numSkeletons > 0), "No skeleton loaded"); m_skeleton = ac->m_skeletons[0]; } // Get the animation and the binding { hkString assetFile = hkAssetManagementUtil::getFilePath("Resources/Animation/HavokGirl/hkWalkLoop.hkx"); hkRootLevelContainer* container = m_loader->load( assetFile.cString() ); HK_ASSERT2(0x27343437, container != HK_NULL , "Could not load asset"); hkaAnimationContainer* ac = reinterpret_cast<hkaAnimationContainer*>( container->findObjectByType( hkaAnimationContainerClass.getName() )); HK_ASSERT2(0x27343435, ac && (ac->m_numAnimations > 0 ), "No animation loaded"); m_animation[0] = ac->m_animations[0]; HK_ASSERT2(0x27343435, ac && (ac->m_numBindings > 0), "No binding loaded"); m_binding[0] = ac->m_bindings[0]; assetFile = hkAssetManagementUtil::getFilePath("Resources/Animation/HavokGirl/hkWalkTurnLLoop.hkx"); container = m_loader->load( assetFile.cString() ); ac = reinterpret_cast<hkaAnimationContainer*>( container->findObjectByType( hkaAnimationContainerClass.getName() )); HK_ASSERT2(0x27343435, ac && (ac->m_numAnimations > 0 ), "No animation loaded"); m_animation[1] = ac->m_animations[0]; HK_ASSERT2(0x27343435, ac && (ac->m_numBindings > 0), "No binding loaded"); m_binding[1] = ac->m_bindings[0]; assetFile = hkAssetManagementUtil::getFilePath("Resources/Animation/HavokGirl/hkWalkTurnRLoop.hkx"); container = m_loader->load( assetFile.cString() ); ac = reinterpret_cast<hkaAnimationContainer*>( container->findObjectByType( hkaAnimationContainerClass.getName() )); HK_ASSERT2(0x27343435, ac && (ac->m_numAnimations > 0 ), "No animation loaded"); m_animation[2] = ac->m_animations[0]; HK_ASSERT2(0x27343435, ac && (ac->m_numBindings > 0), "No binding loaded"); m_binding[2] = ac->m_bindings[0]; } // Convert the skin { const char* skinFile = "Resources/Animation/HavokGirl/hkLowResSkin.hkx"; hkString assetFile = hkAssetManagementUtil::getFilePath( skinFile ); hkRootLevelContainer* container = m_loader->load( assetFile.cString() ); HK_ASSERT2(0x27343437, container != HK_NULL , "Could not load asset"); hkxScene* scene = reinterpret_cast<hkxScene*>( container->findObjectByType( hkxSceneClass.getName() )); HK_ASSERT2(0x27343435, scene , "No scene loaded"); hkaAnimationContainer* ac = reinterpret_cast<hkaAnimationContainer*>( container->findObjectByType( hkaAnimationContainerClass.getName() )); HK_ASSERT2(0x27343435, ac && (ac->m_numSkins > 0), "No skins loaded"); m_numSkinBindings = ac->m_numSkins; m_skinBindings = ac->m_skins; // Make graphics output buffers for the skins env->m_sceneConverter->convert( scene ); } // Create the skeleton m_skeletonInstance = new hkaAnimatedSkeleton( m_skeleton ); m_skeletonInstance->setReferencePoseWeightThreshold( 0.01f ); float weights[NUM_ANIMS]; weights[0] = 0.1f; weights[1] = 0.0f; weights[2] = 0.0f; // Grab the animations for (int i=0; i < NUM_ANIMS; i++) { m_control[i] = new hkaDefaultAnimationControl( m_binding[i] ); m_control[i]->setMasterWeight( weights[i] ); m_control[i]->setPlaybackSpeed( 1.0f ); m_skeletonInstance->addAnimationControl( m_control[i] ); m_control[i]->removeReference(); } // make a world so that we can auto create a display world to hold the skin setupGraphics( ); m_currentMotion.setIdentity(); }
void MyExtendedUserDataListener::contactPointAddedCallback( hkpContactPointAddedEvent& event ) { // // Read the shape key hierarchy -- this is done identically for both Psi and Toi contact points. // hkpShapeKey shapeKey; { // This is the body to which the listener is attached. hkpEntity* body = event.m_callbackFiredFrom; // Extended user data only works when you use the default hkpSimpleConstraintContactMgr. // The function below will assert otherwise. // The atom caches information on how many extended user datas we store for each body. // Each body stores its data independently. const int numDatas = event.getNumExtendedUserDatas(event.m_callbackFiredFrom); // Now we can read the data. // The first user data stores the hkpShapeKey of the bottom most hkpCdBody in the shape hierarchy, // the next one stores hkpShapeKey of its parent, and so on, till we store '-1' as the hkpShapeKey // of the root collidable, or we run out of extended user data slots, in which case only a part // of the hierarchy is stored. // // In this demo we expect to have: // extendedUserDatas[0] in the [0,7] range -- this is the hkpShapeKey of one of the 8 transformed sphere shapes grouped under a hkpListShape // extendedUserDatas[1] equal -1 -- this is the root hkpListShape, with no parent, and therefore no hkpShapeKey // // Note that we only have two levels of hierarchy, while our shape is composed of hkpListShape->hkpConvexTransformShape->hkpSphereShape. // That's because hkpConvexTransform/TranslateShapes don't create a corresponding hkpCdBody during collision detection. // Note that the above is not the case for the deprecated hkpTransformShape. hkInplaceArray<hkpContactPointProperties::UserData,8> data; data.setSize(numDatas); event.getExtendedUserDatas(body, data.begin(), numDatas); // Let us store our custom contact point id. Let's store it in the last data slot to avoid overwriting the hkpShapeKey hierarchy. // We know we have enough room, as we set entity->m_numUserDatasInContactPointProperties = 3 which is more that the max hierarchy depth for our shape. data[numDatas-1] = m_uniqueIdCounter++; // And write back all datas event.setExtendedUserDatas(body, data.begin(), numDatas); // Get the hkpShapeKey shapeKey = data[0]; } // Sample demo structure holding history about callbacks triggered for this contact point. { ContactPointInfo& info = m_contactInfos.expandOne(); new (&info) ContactPointInfo(); HK_ASSERT2(0xad7853aa, m_contactInfos.getSize() == m_uniqueIdCounter, "Unique ID is not in synch with m_contactInfos array."); info.m_type = event.isToi() ? ContactPointInfo::TOI : ContactPointInfo::PSI; info.m_added = true; info.m_key = int(shapeKey); } // By setting the ProcessContactCallbackDelay to 0 we will receive callbacks for // any collisions processed for this body every frame (simulation step), i.e. the delay between // any such callbacks is 0 frames. // If you wish to only be notified every N frames simply set the delay to be N-1. // The default is 65536, i.e. (for practical purpose) once for the first collision only, until // the bodies separate to outside the collision tolerance. event.m_callbackFiredFrom->setProcessContactCallbackDelay(0); }