void ScrapeParticleListener::contactProcessCallback(hkpContactProcessEvent& event) { HK_TIMER_BEGIN("ScrapeParticleListener CB", this); hkpProcessCollisionData* collisionData = event.m_collisionData; for(int i = 0; i < collisionData->getNumContactPoints(); ++i) { hkpSimpleConstraintContactMgr* mgr = static_cast<hkpSimpleConstraintContactMgr*>(event.m_internalContactMgr); hkpRigidBody* rigidBodyA = mgr->getConstraintInstance()->getRigidBodyA(); hkpRigidBody* rigidBodyB = mgr->getConstraintInstance()->getRigidBodyB(); if(rigidBodyA->hasProperty(m_emitPropertyKey) && rigidBodyB->hasProperty(m_emitPropertyKey)) { // Compute relative velocity at contact point. hkContactPoint& contactPoint = collisionData->getContactPoint(i).m_contact; hkVector4 velocityA, velocityB, relativeVelocity; rigidBodyA->getPointVelocity(contactPoint.getPosition(), velocityA); rigidBodyB->getPointVelocity(contactPoint.getPosition(), velocityB); relativeVelocity.setSub4(velocityA, velocityB); hkReal relativeSpeed = relativeVelocity.length3(); if(relativeSpeed > m_relativeSpeedThreshold) { hkVector4 direction, normal; if(velocityA.lengthSquared3() > velocityB.lengthSquared3()) { direction.setNeg4(relativeVelocity); normal = contactPoint.getNormal(); } else { direction = relativeVelocity; normal.setNeg4(contactPoint.getNormal()); } direction.normalize3(); // Bend the velocity towards the contact normal. This looks slightly better but can be commented out if desired. const hkReal t = 0.7f; direction.mul4(t); direction.addMul4(1.f-t, normal); direction.normalize3(); // A critical section has to be used here since listener callbacks can be called from different threads. m_criticalSection.enter(); ContactInfo& contactInfo = m_contacts.expandOne(); contactInfo.m_position = contactPoint.getPosition(); contactInfo.m_direction = direction; contactInfo.m_relativeSpeed = relativeSpeed; m_criticalSection.leave(); } } } HK_TIMER_END(); }
void SlidingWorldDemo::recalcAabbsAfterBroadphaseRecenter( hkpWorld* world ) { HK_TIMER_BEGIN("RecalcAabbs", HK_NULL ); // Iterate over all simulation islands. { // Fixed island const hkpSimulationIsland* island = world->getFixedIsland(); for (int b = 0; b < island->getEntities().getSize(); b++ ) { hkpRigidBody* body = static_cast<hkpRigidBody*>( island->getEntities()[b]); body->updateCachedAabb(); } } { // Inactive islands const hkArray<hkpSimulationIsland*>& islands = world->getInactiveSimulationIslands(); for (int i = 0; i < islands.getSize(); i++ ) { hkpSimulationIsland* island = islands[i]; for (int b = 0; b < island->getEntities().getSize(); b++ ) { hkpRigidBody* body = static_cast<hkpRigidBody*>( island->getEntities()[b]); body->updateCachedAabb(); } } } { // Active Islands const hkArray<hkpSimulationIsland*>& islands = world->getActiveSimulationIslands(); for (int i = 0; i < islands.getSize(); i++ ) { hkpSimulationIsland* island = islands[i]; for (int b = 0; b < island->getEntities().getSize(); b++ ) { hkpRigidBody* body = static_cast<hkpRigidBody*>( island->getEntities()[b]); body->updateCachedAabb(); } } } HK_TIMER_END(); }
hkDemo::Result AsymetricCharacterRbDemo::stepDemo() { // Update actual time m_time += m_timestep; hkQuaternion orient; { m_world->lock(); hkReal posX = 0.f; hkReal posY = 0.f; { float deltaAngle = 0.f; CharacterUtils::getUserInputForCharacter(m_env, deltaAngle, posX, posY); m_currentAngle += deltaAngle; orient.setAxisAngle(UP, m_currentAngle); } hkpCharacterInput input; hkpCharacterOutput output; { input.m_inputLR = posX; input.m_inputUD = posY; input.m_wantJump = m_env->m_window->getMouse().wasButtonPressed(HKG_MOUSE_LEFT_BUTTON) || m_env->m_gamePad->wasButtonPressed(HKG_PAD_BUTTON_1); input.m_atLadder = false; input.m_up = UP; input.m_forward.set(1,0,0); input.m_forward.setRotatedDir( orient, input.m_forward ); hkStepInfo stepInfo; stepInfo.m_deltaTime = m_timestep; stepInfo.m_invDeltaTime = 1.0f/m_timestep; stepInfo.m_endTime = m_time; input.m_stepInfo = stepInfo; input.m_characterGravity.set(0,-16,0); input.m_velocity = m_characterRigidBody->getLinearVelocity(); input.m_position = m_characterRigidBody->getPosition(); hkpSurfaceInfo ground; m_characterRigidBody->checkSupport( stepInfo, ground); // Avoid accidental state changes (Smooth movement on stairs) // During transition supported->unsupported continue to return N-frames hkpSurfaceInfo data from previous supported state { // Number of frames to skip (continue with previous hkpSurfaceInfo data) const int skipFramesInAir = 5; if (input.m_wantJump) { m_framesInAir = skipFramesInAir; } if ( ground.m_supportedState != hkpSurfaceInfo::SUPPORTED ) { if (m_framesInAir < skipFramesInAir) { input.m_isSupported = true; input.m_surfaceNormal = m_previousGround->m_surfaceNormal; input.m_surfaceVelocity = m_previousGround->m_surfaceVelocity; input.m_surfaceMotionType = m_previousGround->m_surfaceMotionType; } else { input.m_isSupported = false; input.m_surfaceNormal = ground.m_surfaceNormal; input.m_surfaceVelocity = ground.m_surfaceVelocity; input.m_surfaceMotionType = ground.m_surfaceMotionType; } m_framesInAir++; } else { input.m_isSupported = true; input.m_surfaceNormal = ground.m_surfaceNormal; input.m_surfaceVelocity = ground.m_surfaceVelocity; input.m_surfaceMotionType = ground.m_surfaceMotionType; m_previousGround->set(ground); // Reset old number of frames if (m_framesInAir > skipFramesInAir) { m_framesInAir = 0; } } } } // Apply the character state machine { HK_TIMER_BEGIN( "update character state", HK_NULL ); m_characterContext->update(input, output); HK_TIMER_END(); } // Apply the player character controller { HK_TIMER_BEGIN( "simulate character", HK_NULL ); m_characterRigidBody->setLinearVelocity(output.m_velocity, m_timestep); HK_TIMER_END(); } // Rotate the character { hkVector4 offset; offset.set(1,0,0); offset.setRotatedDir( orient , offset); hkRotation rotation; hkVector4& col0 = rotation.getColumn(0); hkVector4& col1 = rotation.getColumn(1); hkVector4& col2 = rotation.getColumn(2); // Smoothed surface normal hkVector4 surfaceNorm; surfaceNorm = input.m_isSupported ? input.m_surfaceNormal : (hkVector4)UP; m_rigidBodyNormal.addMul4( 0.05f, surfaceNorm ); m_rigidBodyNormal.normalize3(); col1 = m_rigidBodyNormal; col2.setCross( col1, offset); col2.normalize3(); col0.setCross( col1, col2 ); #ifdef HK_DEBUG HK_DISPLAY_ARROW(m_characterRigidBody->getPosition(), col0, 0xffff00ff); HK_DISPLAY_ARROW(m_characterRigidBody->getPosition(), col1, 0xff00ffff); HK_DISPLAY_ARROW(m_characterRigidBody->getPosition(), col2, 0xff0000ff); #endif // Forward orientation controller reorientCharacter( rotation ); } // Display states infos { // classical controller state hkpCharacterStateType state = m_characterContext->getState(); char * stateStr; switch (state) { case HK_CHARACTER_ON_GROUND: stateStr = "On Ground"; break; case HK_CHARACTER_JUMPING: stateStr = "Jumping"; break; case HK_CHARACTER_IN_AIR: stateStr = "In Air"; break; case HK_CHARACTER_CLIMBING: stateStr = "Climbing"; break; default: stateStr = "Other"; break; } char buffer[255]; hkString::snprintf(buffer, 255, "State: %s ", stateStr); m_env->m_textDisplay->outputText(buffer, 20, 450, 0xffffffff); m_world->unlock(); } // Step the world { hkDefaultPhysicsDemo::stepDemo(); } m_world->lock(); // Camera Handling { const hkReal height = 1.25f; hkVector4 forward; forward.set(1,0,0); forward.setRotatedDir( orient, forward ); hkVector4 from, to; to = m_characterRigidBody->getRigidBody()->getPosition(); hkVector4 dir; dir.setMul4( height, UP ); dir.addMul4( -4.0f, forward); from.setAdd4(to, dir); setupDefaultCameras(m_env, from, to, UP, 1.0f); } m_world->unlock(); } return hkDemo::DEMO_OK; }
hkDemo::Result UnevenTerrainVsDemo::stepDemo() { HK_TIMER_BEGIN( "simulate multiply characters", HK_NULL ); m_world->lock(); hkVector4 up; up.setNeg4( m_world->getGravity() ); up.normalize3(); m_tick++; hkpCharacterInput inputRb; hkpCharacterInput inputProxy; // Fill in the state machine input structure for character rigid body { inputRb.m_atLadder = false; inputRb.m_up = up; // Steer the characters inputRb.m_inputLR = 0.0f; inputRb.m_inputUD = 1.0f; { // The factor 70.0f gives a turning circle which fits in the level // for the default walk speed of 10.0f. hkReal walkSpeedFactor = m_options.m_characterWalkSpeed / 70.0f; hkReal time = hkReal(m_tick) * walkSpeedFactor / 60.0f; const hkReal x = hkMath::sin(time); const hkReal z = hkMath::cos(time); inputRb.m_forward.set( x, 0, z ); inputRb.m_forward.normalize3(); } inputRb.m_wantJump = false; hkStepInfo stepInfo; stepInfo.m_deltaTime = m_timestep; stepInfo.m_invDeltaTime = 1.0f/m_timestep; inputRb.m_stepInfo = stepInfo; inputRb.m_characterGravity.set( 0.0f, m_options.m_characterGravity, 0.0f ); inputProxy = inputRb; // character rigid body specific code inputRb.m_velocity = m_characterRigidBody->getLinearVelocity(); inputRb.m_position = m_characterRigidBody->getPosition(); m_characterRigidBody->checkSupport(stepInfo, inputRb.m_surfaceInfo); filterStates( m_filterRigidBody, inputRb.m_surfaceInfo ); m_supportHistoryRb <<= 1; if ( inputRb.m_surfaceInfo.m_supportedState == hkpSurfaceInfo::SUPPORTED ) { ++m_supportHistoryRb; } else { ++m_unsupportedFramesCountRb; } // character proxy specific code inputProxy.m_velocity = m_characterProxy->getLinearVelocity(); inputProxy.m_position = m_characterProxy->getPosition(); hkVector4 down; down.setNeg4(UP); m_characterProxy->checkSupport(down, inputProxy.m_surfaceInfo); filterStates( m_filterProxy, inputProxy.m_surfaceInfo ); m_supportHistoryProxy <<= 1; if ( inputProxy.m_surfaceInfo.m_supportedState == hkpSurfaceInfo::SUPPORTED ) { ++m_supportHistoryProxy; } else { ++m_unsupportedFramesCountProxy; } } hkpCharacterOutput outputRb; hkpCharacterOutput outputProxy; // Apply the character state machine { HK_TIMER_BEGIN( "update character state", HK_NULL ); m_characterContext->update(inputRb, outputRb); m_characterProxyContext->update(inputProxy, outputProxy); HK_TIMER_END(); } //Apply the character controllers { HK_TIMER_BEGIN( "simulate character", HK_NULL ); // Feed the output velocity from state machine into character rigid body m_characterRigidBody->setLinearVelocity(outputRb.m_velocity, m_timestep); m_characterProxy->setLinearVelocity(outputProxy.m_velocity ); hkStepInfo si; si.m_deltaTime = m_timestep; si.m_invDeltaTime = 1.0f/m_timestep; m_characterProxy->integrate( si, m_world->getGravity() ); HK_TIMER_END(); } { // If the character has fallen off the world we reposition them if ( ( m_characterProxy->getPosition()(1) < -10.0f ) || ( m_characterRigidBody->getPosition()(1) < -10.0f ) ) { m_characterProxy->setPosition( characterStart ); m_characterProxy->setLinearVelocity( hkVector4::getZero() ); m_characterRigidBody->getRigidBody()->setPosition(characterStart); m_characterRigidBody->setLinearVelocity( hkVector4::getZero(), m_timestep ); m_tick = 0; } } m_world->unlock(); // Display state { char historyRb[33]; char historyProxy[33]; char buffer[255]; for ( int i = 0; i < 32; ++i ) { historyRb[i] = m_supportHistoryRb & ( 1 << i ) ? '#' : '_'; historyProxy[i] = m_supportHistoryProxy & ( 1 << i ) ? '#' : '_'; } historyRb[32] = '\0'; historyProxy[32] = '\0'; hkString::sprintf( buffer, "Rigid body unsupported frames: %d\n" "Proxy unsupported frames: %d\n\n" "Rigid body support history: %s\n" "Proxy support history: %s\n", m_unsupportedFramesCountRb, m_unsupportedFramesCountProxy, historyRb, historyProxy ); m_env->m_textDisplay->outputText( buffer, 20, 200, 0xffffffff ); } // Step the world { hkDefaultPhysicsDemo::stepDemo(); } HK_TIMER_END(); return hkDemo::DEMO_OK; }
hkDemo::Result OptimizedWorldRaycastDemo::stepDemo() { m_world->lock(); m_time += m_timestep; // // Create a number of ray directions // const int NUM_DIRECTIONS = 8; const hkReal increment = 2.0f; //const int NUM_DIRECTIONS = 125; const hkReal increment = 0.5f; hkVector4 rayDirections[NUM_DIRECTIONS]; { int i = 0; for (hkReal x = -1.0f; x <= 1.0f; x += increment) { for (hkReal y = -1.0f; y <= 1.0f; y += increment) { for (hkReal z = -1.0f; z <= 1.0f; z+= increment) { rayDirections[i++].set(x,y,z); } } } } hkReal angle = 0.5f * m_time; // In these first chapter we simply call the hkpWorld castRay version with no further optimizations { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; HK_TIMER_BEGIN("NotOptimized", HK_NULL); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); m_world->castRay(inputs[i], collectors[i]); } } HK_TIMER_END(); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::RED); } // This second set of raycasts (yellow) is using a the group functionality of the hkpWorldRayCaster. // Internally it builds an the aabb cache. This is an optimization for broadphase raycasts. // Note that we need to pass in array of collectors to the hkpWorldRayCaster. // As the hkpWorldRayCaster has no clue about the size of each collector, we have to pass in the size. // Interesting if we use a size of 0, than all raycast will report to the same collector. { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; // // Cast Rays // HK_TIMER_BEGIN("CastRayGroup", HK_NULL); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); } hkpWorldRayCaster rayCaster; rayCaster.castRayGroup( *m_world->getBroadPhase(), inputs, NUM_DIRECTIONS, m_world->getCollisionFilter(), collectors, sizeof(collectors[0]) ); } HK_TIMER_END(); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::CYAN); } // This set of raycasts (yellow) is using a the hkpBroadPhaseAabbCache. // So by building the aabb cache. This is an optimization for broadphase raycasts. // The idea is that the aabb cache is actually a reduced broadphase, just storing // objects inside the aabb. Therefore using this cache can speed up rayCasts significantly. // Unfortunately we cannot use our simple hkpWorld::castRay() function, but have to use // a small helper class hkpWorldRayCaster (which is actually used by the hkpWorld::castRay()). { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; // // Calc Cache // HK_TIMER_BEGIN_LIST("UseAabbCache", "CalcCache"); hkpBroadPhaseAabbCache* cache; int cacheSize; { hkAabb aabb; aabb.m_min.set( xPos - m_rayLength, -m_rayLength, zPos-m_rayLength); aabb.m_max.set( xPos + m_rayLength, +m_rayLength, zPos+m_rayLength); cacheSize = m_world->getBroadPhase()->getAabbCacheSize(); cache = reinterpret_cast<hkpBroadPhaseAabbCache*>(hkAllocateStack<char>(cacheSize)); m_world->getBroadPhase()->calcAabbCache( aabb, cache ); } // // Cast Rays // HK_TIMER_SPLIT_LIST("CastRays"); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); hkpWorldRayCaster rayCaster; rayCaster.castRay( *m_world->getBroadPhase(), inputs[i], m_world->getCollisionFilter(), cache, collectors[i] ); } } HK_TIMER_END_LIST(); hkDeallocateStack<char>( (char*)cache); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::YELLOW); } // This third set of raycasts (blue) is using a the hkpBroadPhaseAabbCache and // also makes use of the fact that many rays starting at the same position can // be handled specially. // Unfortunately we cannot use our simple hkpWorld::castRay() function, but have to use // a small helper class hkpWorldRayCaster (which is actually used by the hkpWorld::castRay()). { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; // // Calc Cache // HK_TIMER_BEGIN_LIST("CacheSameStart", "CalcCache"); hkpBroadPhaseAabbCache* cache; int cacheSize; { hkAabb aabb; aabb.m_min.set( xPos - m_rayLength, -m_rayLength, zPos-m_rayLength); aabb.m_max.set( xPos + m_rayLength, +m_rayLength, zPos+m_rayLength); cacheSize = m_world->getBroadPhase()->getAabbCacheSize(); cache = reinterpret_cast<hkpBroadPhaseAabbCache*>(hkAllocateStack<char>(cacheSize)); m_world->getBroadPhase()->calcAabbCache( aabb, cache ); } // // Cast Rays // HK_TIMER_SPLIT_LIST("CastRays"); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); } hkpWorldRayCaster rayCaster; rayCaster.castRaysFromSinglePoint( *m_world->getBroadPhase(), inputs, NUM_DIRECTIONS, m_world->getCollisionFilter(), cache, collectors, hkSizeOf( collectors[0] ) ); } HK_TIMER_END_LIST(); hkDeallocateStack<char>( (char*)cache); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::BLUE); } // This 4th set of raycasts (green) is using a the AabbPhantom // A phantom is like a persistent aabb cache, so it only makes sense if our aabb shows some // framecoherency. // Unfortunetaly the current phantom implementations simply casts the ray against all objects // overlapping with the phantom. Therefor, if too many objects are intersecting the phantoms // aabb, performance could get bad. { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; HK_TIMER_BEGIN_LIST("AabbPhantom", "MovePhantom"); { hkAabb aabb; aabb.m_min.set( xPos - m_rayLength, -m_rayLength, zPos-m_rayLength); aabb.m_max.set( xPos + m_rayLength, +m_rayLength, zPos+m_rayLength); m_phantom->setAabb( aabb ); } HK_TIMER_SPLIT_LIST("CastRays"); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); m_phantom->castRay(inputs[i], collectors[i]); } } HK_TIMER_END_LIST(); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::GREEN); } // fastest (purple), combine all optimizations above // We are using a phantom to set up the broadphase cache. This avoids the problem of the hkpPhantom::castRay() { angle += HK_REAL_PI * 0.3f; hkReal xPos = m_rayLength * 3.0f * hkMath::sin(angle); hkReal zPos = m_rayLength * 3.0f * hkMath::cos(angle); hkpWorldRayCastInput inputs[NUM_DIRECTIONS]; hkpClosestRayHitCollector collectors[NUM_DIRECTIONS]; hkpBroadPhaseAabbCache* cache; int cacheSize; HK_TIMER_BEGIN_LIST("AllOpt.", "MovePhantomAndDoCache"); { hkAabb aabb; aabb.m_min.set( xPos - m_rayLength, -m_rayLength, zPos-m_rayLength); aabb.m_max.set( xPos + m_rayLength, +m_rayLength, zPos+m_rayLength); m_phantomUseCache->setAabb( aabb ); cacheSize = m_world->getBroadPhase()->getAabbCacheSize(); cache = reinterpret_cast<hkpBroadPhaseAabbCache*>(hkAllocateStack<char>(cacheSize)); m_world->getBroadPhase()->calcAabbCache( m_phantomUseCache->getOverlappingCollidables(), cache ); } HK_TIMER_SPLIT_LIST("CastRays"); { for (int i = 0; i < NUM_DIRECTIONS; i++) { inputs[i].m_from.set( xPos, 0, zPos); inputs[i].m_to.setAddMul4(inputs[i].m_from, rayDirections[i], 1.f*m_rayLength); } hkpWorldRayCaster rayCaster; rayCaster.castRaysFromSinglePoint( *m_world->getBroadPhase(), inputs, NUM_DIRECTIONS, m_world->getCollisionFilter(), cache, collectors, hkSizeOf( collectors[0] ) ); } HK_TIMER_END_LIST(); hkDeallocateStack<char>( (char*)cache); displayHits( inputs, NUM_DIRECTIONS, collectors, hkColor::PURPLE); } m_world->unlock(); return hkDefaultPhysicsDemo::stepDemo(); }
void SlidingWorldDemo::shiftAllGameObjectDataSilently( hkpWorld* world, const hkVector4& effectiveShiftDistance ) { HK_TIMER_BEGIN("ShiftObjects", HK_NULL ); // Iterate over all simulation islands. { // Fixed island const hkpSimulationIsland* island = world->getFixedIsland(); for (int b = 0; b < island->getEntities().getSize(); b++ ) { hkpRigidBody* body = static_cast<hkpRigidBody*>( island->getEntities()[b]); hkVector4 newPos = body->getPosition(); newPos.add4( effectiveShiftDistance ); body->getRigidMotion()->setPosition( newPos ); body->updateCachedAabb(); HK_UPDATE_GEOMETRY( body->getTransform(), (hkUlong)body->getCollidable() ); } } { // Inactive islands const hkArray<hkpSimulationIsland*>& islands = world->getInactiveSimulationIslands(); for (int i = 0; i < islands.getSize(); i++ ) { hkpSimulationIsland* island = islands[i]; for (int b = 0; b < island->getEntities().getSize(); b++ ) { hkpRigidBody* body = static_cast<hkpRigidBody*>( island->getEntities()[b]); hkVector4 newPos = body->getPosition(); newPos.add4( effectiveShiftDistance ); body->getRigidMotion()->setPosition( newPos ); body->updateCachedAabb(); HK_UPDATE_GEOMETRY( body->getTransform(), (hkUlong)body->getCollidable() ); } } } { // Active Islands const hkArray<hkpSimulationIsland*>& islands = world->getActiveSimulationIslands(); for (int i = 0; i < islands.getSize(); i++ ) { hkpSimulationIsland* island = islands[i]; for (int b = 0; b < island->getEntities().getSize(); b++ ) { hkpRigidBody* body = static_cast<hkpRigidBody*>( island->getEntities()[b]); hkVector4 newPos = body->getPosition(); newPos.add4( effectiveShiftDistance ); body->getRigidMotion()->setPosition( newPos ); body->updateCachedAabb(); HK_UPDATE_GEOMETRY( body->getTransform(), (hkUlong)body->getCollidable() ); // You also need to shift contact points to prevent them from becoming stale. // To do this iterate over all contact points maintained by the entity's collision agents. // // Each entity, if in contact with another entity, has a collision agent to manage contacts. // The collision agents in turn manage contact points via a contact point // manager. To shift contact points we shift all contact points in the manager by the // effectiveShiftDistance passed in. hkpLinkedCollidable& collidableEx = *body->getLinkedCollidable(); for (int collidableIndex = 0; collidableIndex < collidableEx.m_collisionEntries.getSize(); collidableIndex++) { hkpAgentNnEntry* entry = collidableEx.m_collisionEntries[collidableIndex].m_agentEntry; hkpDynamicsContactMgr* contactManager = static_cast<hkpDynamicsContactMgr*>(entry->m_contactMgr); HK_ASSERT(0x0, contactManager != HK_NULL); hkArray<hkContactPointId> contactPointIds; contactManager->getAllContactPointIds( contactPointIds ); for( int contactPointIndex = 0; contactPointIndex < contactPointIds.getSize(); ++contactPointIndex ) { hkContactPoint* contactPoint = contactManager->getContactPoint( contactPointIds[contactPointIndex] ); hkVector4 nPos = contactPoint->getPosition(); nPos.add4( effectiveShiftDistance ); contactPoint->setPosition( nPos ); } } } } } // Iterate over all phantoms, *except for the ones attached to the broadphase*, ie the borders { for ( int p = 0; p < world->getPhantoms().getSize(); p++ ) { hkpPhantom* phantom = world->getPhantoms()[p]; if( phantom->getCollidable()->getType() != hkpWorldObject::BROAD_PHASE_BORDER ) { switch( phantom->getType()) { case HK_PHANTOM_AABB: { hkpAabbPhantom* aabbPhantom = static_cast<hkpAabbPhantom*>( phantom ); // Now do a bad trick to move the aabb. hkAabb& aabb = const_cast<hkAabb&>( aabbPhantom->getAabb() ); aabb.m_min.add4( effectiveShiftDistance ); aabb.m_max.add4( effectiveShiftDistance ); break; } case HK_PHANTOM_SIMPLE_SHAPE: case HK_PHANTOM_CACHING_SHAPE: { hkpShapePhantom* shapePhantom = static_cast<hkpShapePhantom*>( phantom ); hkTransform& transform = const_cast<hkTransform&>( shapePhantom->getTransform()); transform.getTranslation().add4( effectiveShiftDistance ); break; } default: HK_ASSERT2(0xf041604,0,"Unknown Phantom Type" ); } } } } HK_TIMER_END(); }
hkDemo::Result PlatformsCharacterRbDemo::stepDemo() { // Process inputs, calculate state, set new velocity { m_world->lock(); // update total run time m_time += m_timestep; // Get user input data hkReal posX = 0.f; hkReal posY = 0.f; { float deltaAngle = 0.f; CharacterUtils::getUserInputForCharacter(m_env, deltaAngle, posX, posY); m_currentAngle += deltaAngle; m_currentOrient.setAxisAngle(UP, m_currentAngle); } hkpCharacterInput input; hkpCharacterOutput output; { input.m_inputLR = posX; input.m_inputUD = posY; input.m_wantJump = m_env->m_window->getMouse().wasButtonPressed(HKG_MOUSE_LEFT_BUTTON) || m_env->m_gamePad->wasButtonPressed(HKG_PAD_BUTTON_1); input.m_atLadder = false; input.m_up = UP; input.m_forward.set(1,0,0); input.m_forward.setRotatedDir( m_currentOrient, input.m_forward ); hkStepInfo stepInfo; stepInfo.m_deltaTime = m_timestep; stepInfo.m_invDeltaTime = 1.0f/m_timestep; stepInfo.m_endTime = m_time; input.m_stepInfo = stepInfo; input.m_characterGravity.set(0,0,-16); input.m_velocity = m_characterRigidBody->getLinearVelocity(); input.m_position = m_characterRigidBody->getPosition(); hkpSurfaceInfo ground; m_characterRigidBody->checkSupport(stepInfo,ground); input.m_isSupported = (ground.m_supportedState == hkpSurfaceInfo::SUPPORTED); input.m_surfaceNormal = ground.m_surfaceNormal; input.m_surfaceVelocity = ground.m_surfaceVelocity; input.m_surfaceMotionType = ground.m_surfaceMotionType; } // Apply the character state machine { HK_TIMER_BEGIN( "update character state", HK_NULL ); m_characterContext->update(input, output); HK_TIMER_END(); } //Apply the player character controller { HK_TIMER_BEGIN( "simulate character", HK_NULL ); // Feed output velocity from state machine into character rigid body m_characterRigidBody->setLinearVelocity(output.m_velocity, m_timestep); HK_TIMER_END(); } // Display state { hkpCharacterStateType state = m_characterContext->getState(); char * stateStr; switch (state) { case HK_CHARACTER_ON_GROUND: stateStr = "On Ground"; break; case HK_CHARACTER_JUMPING: stateStr = "Jumping"; break; case HK_CHARACTER_IN_AIR: stateStr = "In Air"; break; case HK_CHARACTER_CLIMBING: stateStr = "Climbing"; break; default: stateStr = "Other"; break; } char buffer[255]; hkString::snprintf(buffer, 255, "State : %s", stateStr); m_env->m_textDisplay->outputText(buffer, 20, 270, 0xffffffff); } m_world->unlock(); } // Step the world { hkDefaultPhysicsDemo::stepDemo(); } // Move the platforms { m_world->lock(); moveHorizontalPlatform(); moveVerticalPlatform(); m_world->unlock(); } // Transparent camera handling { m_world->lock(); cameraHandling(); m_world->unlock(); } return hkDemo::DEMO_OK; }
hkDemo::Result CharacterDemo::stepDemo() { hkVector4 up; hkQuaternion orient; { m_world->lock(); // Get user input data int m_upAxisIndex = 2; up.setZero4(); up(m_upAxisIndex) = 1; hkReal posX = 0.f; hkReal posY = 0.f; { float deltaAngle = 0.f; CharacterUtils::getUserInputForCharacter(m_env, deltaAngle, posX, posY); m_currentAngle += deltaAngle; orient.setAxisAngle(up, m_currentAngle); } hkpCharacterInput input; hkpCharacterOutput output; { input.m_inputLR = posX; input.m_inputUD = posY; input.m_wantJump = m_env->m_window->getMouse().wasButtonPressed(HKG_MOUSE_LEFT_BUTTON) || m_env->m_gamePad->wasButtonPressed(HKG_PAD_BUTTON_1); input.m_atLadder = m_listener->m_atLadder; input.m_up = up; input.m_forward.set(1,0,0); input.m_forward.setRotatedDir( orient, input.m_forward ); input.m_stepInfo.m_deltaTime = m_timestep; input.m_stepInfo.m_invDeltaTime = 1.0f / m_timestep; input.m_characterGravity.set(0,0,-16); input.m_velocity = m_characterProxy->getLinearVelocity(); input.m_position = m_characterProxy->getPosition(); if (m_listener->m_atLadder) { hkVector4 right, ladderUp; right.setCross( up, m_listener->m_ladderNorm ); ladderUp.setCross( m_listener->m_ladderNorm, right ); // Calculate the up vector for the ladder if (ladderUp.lengthSquared3() > HK_REAL_EPSILON) { ladderUp.normalize3(); } // Reorient the forward vector so it points up along the ladder input.m_forward.addMul4( -m_listener->m_ladderNorm.dot3(input.m_forward), m_listener->m_ladderNorm); input.m_forward.add4( ladderUp ); input.m_forward.normalize3(); input.m_surfaceNormal = m_listener->m_ladderNorm; input.m_surfaceVelocity = m_listener->m_ladderVelocity; } else { hkVector4 down; down.setNeg4(up); hkpSurfaceInfo ground; m_characterProxy->checkSupport(down, ground); input.m_isSupported = ground.m_supportedState == hkpSurfaceInfo::SUPPORTED; input.m_surfaceNormal = ground.m_surfaceNormal; input.m_surfaceVelocity = ground.m_surfaceVelocity; } } // Apply the character state machine { HK_TIMER_BEGIN( "update character state", HK_NULL ); m_characterContext->update(input, output); HK_TIMER_END(); } //Apply the player character controller { HK_TIMER_BEGIN( "simulate character", HK_NULL ); // Feed output from state machine into character proxy m_characterProxy->setLinearVelocity(output.m_velocity); hkStepInfo si; si.m_deltaTime = m_timestep; si.m_invDeltaTime = 1.0f/m_timestep; m_characterProxy->integrate( si, m_world->getGravity() ); HK_TIMER_END(); } // Display state { hkpCharacterStateType state = m_characterContext->getState(); char * stateStr; switch (state) { case HK_CHARACTER_ON_GROUND: stateStr = "On Ground"; break; case HK_CHARACTER_JUMPING: stateStr = "Jumping"; break; case HK_CHARACTER_IN_AIR: stateStr = "In Air"; break; case HK_CHARACTER_CLIMBING: stateStr = "Climbing"; break; default: stateStr = "Other"; break; } char buffer[255]; hkString::snprintf(buffer, 255, "State : %s", stateStr); m_env->m_textDisplay->outputText(buffer, 20, 270, 0xffffffff); } // // Handle crouching // { hkBool wantCrouch = ( m_env->m_window->getMouse().getButtonState() & HKG_MOUSE_RIGHT_BUTTON ) || (m_env->m_gamePad->getButtonState() & HKG_PAD_BUTTON_2); hkBool isCrouching = m_phantom->getCollidable()->getShape() == m_crouchShape; // We want to stand if (isCrouching && !wantCrouch) { swapPhantomShape(m_standShape); } // We want to crouch if (!isCrouching && wantCrouch) { swapPhantomShape(m_crouchShape); } } m_world->unlock(); } // Step the world hkDefaultPhysicsDemo::stepDemo(); // Camera Handling { m_world->lock(); { const hkReal height = .7f; hkVector4 forward; forward.set(1,0,0); forward.setRotatedDir( orient, forward ); hkVector4 from, to; to = m_characterProxy->getPosition(); to.addMul4(height, up); hkVector4 dir; dir.setMul4( height, up ); dir.addMul4( -4.f, forward); from.setAdd4(to, dir); setupDefaultCameras(m_env, from, to, up, 1.0f); } m_world->unlock(); } return hkDemo::DEMO_OK; }
void ShapeQueryDemo::phantomRayCast( hkpWorld* world, hkReal time, hkArray<hkpAabbPhantom*>& phantoms, hkBool useCollector ) { const int N_RAY_CASTS = 10; hkpWorldRayCastInput rayInputs[N_RAY_CASTS]; hkpWorldRayCastOutput rayOutputs[N_RAY_CASTS]; hkpClosestRayHitCollector rayCollector[ N_RAY_CASTS ]; hkVector4 displacement( 0.0f, -30.0f, 0.0f ); // Perform all queries in one go (do not interleave with examining the results, // as this is bad for code and data cache. { for ( int i = 0; i < N_RAY_CASTS; i++ ) { hkpWorldRayCastInput& in = rayInputs[i]; // create a new position: all objects move in a circle hkReal t = time + (HK_REAL_PI * 2 * i) / N_RAY_CASTS; hkVector4 pos( hkMath::sin( t ) * 12.0f, 10.0f, hkMath::cos( t ) * 12.0f ); // Set our position in our input structure in.m_from = pos; in.m_to.setAdd4( pos, displacement ); // // Query for intersecting objects // for (int k = 0; k < NUM_ITER ; k++ ) { HK_TIMER_BEGIN("raycast", HK_NULL); // Move our phantom so it encapsulates our ray hkpAabbPhantom* phantom = phantoms[i]; { hkAabb aabb; aabb.m_min.setMin4( in.m_to, in.m_from ); aabb.m_max.setMax4( in.m_to, in.m_from ); phantom->setAabb( aabb ); } if ( useCollector ) { hkpClosestRayHitCollector& collector = rayCollector[i]; collector.reset(); phantom->castRay( in, collector ); } else { hkpWorldRayCastOutput& out = rayOutputs[i]; out.reset(); phantom->castRay( in, out ); } HK_TIMER_END( ); } } } // // Batch display the results // NOTE that phantom displays have been forced off for this variant. // { for ( int i = 0; i < N_RAY_CASTS; i++ ) { hkpWorldRayCastInput& in = rayInputs[i]; const hkpWorldRayCastOutput* out; if ( useCollector ) { out = &rayCollector[i].getHit(); hkVector4 off(.1f,0,0); in.m_from.add4( off ); in.m_to.add4( off ); } else { out = &rayOutputs[i]; } // Display the ray { hkVector4 hitPoint; hitPoint.setInterpolate4( in.m_from, in.m_to, out->m_hitFraction ); HK_DISPLAY_LINE(in.m_from, hitPoint, hkColor::rgbFromChars( 240, 200, 0, 200 )); } // Display the result if ( out->hasHit() ) { displayRayHit( world, in, *out ); } } } }
hkDemo::Result PlanetGravityDemo::stepDemo() { // Update lighting { // Update the light source to be at the camera float position[3]; m_cameraPosition.store3( position ); m_flashLight->setPosition( position ); // Update the light direction to be pointing toward the character controller rigid body hkVector4 directionVector; directionVector.setSub4( m_cameraPosition, m_characterRigidBody->getPosition() ); directionVector.mul4( -1.0f ); directionVector.normalize3(); float direction[3]; directionVector.store3( direction ); m_flashLight->setDirection( direction ); } // Detach the camera from the character when P is pressed. if( m_env->m_window->getKeyboard().wasKeyPressed('P') ) { m_detachedCamera = !m_detachedCamera; } // Update turrets for( int i = 0; i < m_turrets.getSize(); i++ ) { Turret& turret = m_turrets[i]; turret.cooldown -= m_timestep; // Make the turret spin turret.hinge->setMotorTargetAngle( turret.hinge->getMotorTargetAngle() + ( m_timestep / 5.f ) ); // Bail out if the turret is "hot" if( turret.cooldown > 0.0f ) { continue; } // Generate a curved raycast and shoot the ray // This has to be done every time-step as it's in world-space { const hkReal radius = 14.8f; hkRotation rot; hkVector4 offset; hkVector4 turretDown; rot.set( turret.turretRigidBody->getRotation() ); offset = turret.turretRigidBody->getPosition(); turretDown.setMul4( -1.0f, rot.getColumn(2) ); hkpLinearParametricCurve myCurve; // Move the ray's source a little up so it's coming from the center of the barrel hkTransform localTransform( hkQuaternion::getIdentity(), hkVector4( 0.0f, 0.0f, 0.7f ) ); // Create a curve of 20 points for( int j = 0; j < 20; j++ ) { hkReal angle = HK_REAL_PI * static_cast<hkReal>(j) / 15.0f; hkVector4 newPoint( radius * 2.0f * sin( angle ), 0.0f, radius * 2.0f * cos( angle ) ); newPoint.setTransformedPos( localTransform, newPoint ); newPoint.setTransformedPos( turret.turretRigidBody->getTransform(), newPoint ); newPoint.addMul4( radius * 2.0f, turretDown ); myCurve.addPoint( newPoint ); } // We only need the closest hit (as our lasers can't pass through objects) // so hkpClosestRayHitCollector is used. hkpClosestRayHitCollector raycastOutput; hkReal t = castCurvedRay( raycastOutput, myCurve, 20 ); // Apply a large force to the closest rb we hit, along the tangent at the colliding point if( raycastOutput.hasHit() ) { hkpRigidBody* hitRb = hkGetRigidBody( raycastOutput.getHit().m_rootCollidable ); if( hitRb->getMotionType() != hkpMotion::MOTION_FIXED ) { hkVector4 tangent; myCurve.getTangent( t, tangent ); tangent.mul4( 15000.0f ); applyScaledLinearImpulse( hitRb, tangent ); turret.cooldown = 3.0f; } } } } m_world->markForWrite(); // Update the character context m_characterRigidBody->m_up = m_worldUp; hkReal posX = 0.0f; hkReal posY = 0.0f; if( !m_detachedCamera ) { float deltaAngle; CharacterUtils::getUserInputForCharacter( m_env, deltaAngle, posX, posY ); if( ( ( hkMath::fabs( posX ) < HK_REAL_MAX ) && ( hkMath::fabs( posY ) < HK_REAL_MAX ) ) && ( posX || posY ) ) { // find new orientation in local space hkVector4 newForward( -posY, 0.0f, -posX ); hkVector4 absoluteForward( 1.0f, 0.0f, 0.0f ); hkReal characterAngle = hkMath::acos( absoluteForward.dot3( newForward ) ); // Calculate cross product to get sign of rotation. hkVector4 crossProduct; crossProduct.setCross( absoluteForward, newForward ); if( crossProduct(1) < 0.0f ) { characterAngle *= -1.0f; } // Rotate the character's rigid body to face in the direction it's moving hkRotation newRotation; newRotation.setAxisAngle( m_worldUp, characterAngle ); m_characterForward.setRotatedDir( newRotation, m_cameraForward ); m_characterForward.normalize3(); } // Rotate the camera's forward vector based on world up vector and mouse movement if( deltaAngle != 0.0f && m_characterRigidBody->getRigidBody()->getRotation().hasValidAxis() ) { hkRotation newRotation; newRotation.setAxisAngle( m_worldUp, deltaAngle ); m_cameraForward.setRotatedDir( newRotation, m_cameraForward ); m_cameraForward.normalize3(); } } HK_TIMER_BEGIN( "set character state", HK_NULL ); hkpCharacterInput input; hkpCharacterOutput output; { input.m_atLadder = false; input.m_inputLR = posX; input.m_inputUD = posY; if( m_detachedCamera ) { input.m_wantJump = false; } else { input.m_wantJump = m_env->m_window->getMouse().wasButtonPressed( HKG_MOUSE_LEFT_BUTTON ) || m_env->m_gamePad->wasButtonPressed( HKG_PAD_BUTTON_1 ); } // Check that we have a valid rotation. Probably won't for the first couple of frames. if( !( m_characterRigidBody->getRigidBody()->getRotation().hasValidAxis() ) ) { input.m_up = hkVector4( 0.0f, 0.0f, 1.0f ); input.m_forward = m_cameraForward; } else { input.m_up = m_worldUp; // Recalculate m_forward so it's perpendicular to m_worldUp hkVector4 newRot; newRot.setCross( m_cameraForward, m_worldUp ); m_cameraForward.setCross( m_worldUp, newRot ); // Display character's current heading hkRotation characterRotation; characterRotation.set( m_characterRigidBody->getRigidBody()->getRotation() ); HK_DISPLAY_ARROW( m_characterRigidBody->getPosition(), characterRotation.getColumn(0), hkColor::LIMEGREEN ); input.m_forward = m_cameraForward; } hkStepInfo stepInfo; stepInfo.m_deltaTime = m_timestep; stepInfo.m_invDeltaTime = 1.0f / m_timestep; input.m_stepInfo = stepInfo; input.m_characterGravity.setMul4( -20.0f, m_worldUp ); input.m_velocity = m_characterRigidBody->getRigidBody()->getLinearVelocity(); input.m_position = m_characterRigidBody->getRigidBody()->getPosition(); { hkpSurfaceInfo ground; m_characterRigidBody->checkSupport( stepInfo, ground ); // Avoid accidental state changes (Smooth movement on stairs) // During transition supported->unsupported continue to return N-frames hkpSurfaceInfo data from previous supported state { // Number of frames to skip (continue with previous hkpSurfaceInfo data) const int skipFramesInAir = 6; if( input.m_wantJump ) { m_framesInAir = skipFramesInAir; } hkpSurfaceInfo* currInfo; if( ground.m_supportedState != hkpSurfaceInfo::SUPPORTED ) { if( m_framesInAir < skipFramesInAir ) { input.m_isSupported = true; currInfo = m_previousGround; } else { input.m_isSupported = false; currInfo = &ground; } m_framesInAir++; } else { input.m_isSupported = true; currInfo = &ground; m_previousGround->set( ground ); // reset old number of frames if( m_framesInAir > skipFramesInAir ) { m_framesInAir = 0; } } input.m_surfaceNormal = currInfo->m_surfaceNormal; input.m_surfaceVelocity = currInfo->m_surfaceVelocity; input.m_surfaceMotionType = currInfo->m_surfaceMotionType; } } HK_TIMER_END(); } // Apply the character state machine { HK_TIMER_BEGIN( "update character state", HK_NULL ); m_characterContext->update( input, output ); HK_TIMER_END(); } //Apply the player character controller { HK_TIMER_BEGIN( "simulate character", HK_NULL ); // Set output velocity from state machine into character rigid body m_characterRigidBody->setLinearVelocity( output.m_velocity, m_timestep ); HK_TIMER_END(); m_world->unmarkForWrite(); } // Rotate the character { hkRotation newOrientation; newOrientation.getColumn(0) = m_characterForward; newOrientation.getColumn(1) = m_worldUp; newOrientation.getColumn(2).setCross( newOrientation.getColumn(0), newOrientation.getColumn(1) ); newOrientation.renormalize(); reorientCharacter( newOrientation ); } // Step the world hkDefaultPhysicsDemo::stepDemo(); // Display state { hkpCharacterStateType state = m_characterContext->getState(); char* stateStr; switch( state ) { case HK_CHARACTER_ON_GROUND: { stateStr = "On Ground"; break; } case HK_CHARACTER_JUMPING: { stateStr = "Jumping"; break; } case HK_CHARACTER_IN_AIR: { stateStr = "In Air"; break; } default: { stateStr = "Other"; break; } } char buffer[255]; hkString::snprintf( buffer, 255, "State : %s", stateStr ); m_env->m_textDisplay->outputText( buffer, 20.f, 270.f, 0xffffffff ); } // // Handle crouching (only for capsule) // if( !m_detachedCamera ) { m_world->markForWrite(); hkBool wantCrouch = ( m_env->m_window->getMouse().getButtonState() & HKG_MOUSE_RIGHT_BUTTON ) || ( m_env->m_gamePad->getButtonState() & HKG_PAD_BUTTON_2 ); hkBool isCrouching = ( m_characterRigidBody->getRigidBody()->getCollidable()->getShape() == m_crouchShape ); // We want to stand if( isCrouching && !wantCrouch ) { m_characterRigidBody->getRigidBody()->setShape( m_standShape ); } // We want to crouch else if( !isCrouching && wantCrouch ) { m_characterRigidBody->getRigidBody()->setShape( m_crouchShape ); } m_world->unmarkForWrite(); } // Transparent camera handling if( !m_detachedCamera ) { m_world->markForWrite(); handleCamera(); m_world->unmarkForWrite(); } return hkDemo::DEMO_OK; }
void ShapeQueryDemo::worldRayCast( hkpWorld* world, hkReal time, hkBool useCollector ) { const int N_RAY_CASTS = 10; hkpWorldRayCastInput rayInputs[N_RAY_CASTS]; hkpWorldRayCastOutput rayOutputs[N_RAY_CASTS]; hkpClosestRayHitCollector rayCollector[ N_RAY_CASTS ]; // We may also choose whether or not we wish to use a collector or output structure to // gather the results of the query (hkpWorld::castRay has two overloaded methods). hkVector4 displacement( 0.0f, -30.0f, 0.0f ); // Perform all queries in one go (do not interleave with examining the results, // as this is bad for code and data cache. { for ( int i = 0; i < N_RAY_CASTS; i++ ) { hkpWorldRayCastInput& in = rayInputs[i]; // create a new position: all objects move in a circle hkReal t = time + (HK_REAL_PI * 2 * i) / N_RAY_CASTS; hkVector4 pos( hkMath::sin( t ) * 8.0f, 10.0f, hkMath::cos( t ) * 8.0f ); // Set our position in our input structure in.m_from = pos; in.m_to.setAdd4( pos, displacement ); // // Query for intersecting objects. monitor the timing. // for (int k = 0; k < NUM_ITER ; k++ ) { HK_TIMER_BEGIN("raycast", HK_NULL); if ( useCollector ) { hkpClosestRayHitCollector& collector = rayCollector[i]; collector.reset(); world->castRay( in, collector ); } else { hkpWorldRayCastOutput& out = rayOutputs[i]; out.reset(); world->castRay( in, out ); } HK_TIMER_END( ); } } } // Once we've cast our rays all that remains to do is display them! // We can do this by using both the input structure (to find the // start position) and the output structure (which in the case // of using collectors uses the collector's internal // hkpWorldRayCastOutput structure) to find the end point. // We plot N_RAY_CAST casts, one for each iteration, in a GREEN colour. { for ( int i = 0; i < N_RAY_CASTS; i++ ) { hkpWorldRayCastInput& in = rayInputs[i]; const hkpWorldRayCastOutput* out; if ( useCollector ) { out = &rayCollector[i].getHit(); } else { out = &rayOutputs[i]; } // Display the ray { hkVector4 hitPoint; hitPoint.setInterpolate4( in.m_from, in.m_to, out->m_hitFraction ); HK_DISPLAY_LINE(in.m_from, hitPoint, hkColor::GREEN); } // Display the result if ( out->hasHit() ) { displayRayHit( world, in, *out ); } } } }
hkDemo::Result LowFrequencyCharactersDemo::stepDemo() { hkVector4 up; hkpCharacterInput input[NUM_CHARACTERS]; { m_world->lock(); // Get user input data int m_upAxisIndex = 1; up.setZero4(); up(m_upAxisIndex) = 1; m_tick++; hkQuaternion orient; hkReal posX = 0.f; hkReal posY = 0.f; { float deltaAngle = 0.f; CharacterUtils::getUserInputForCharacter(m_env, deltaAngle, posX, posY); m_currentAngle += deltaAngle; orient.setAxisAngle(up, m_currentAngle); } // Fill in the state machine input structure hkpCharacterOutput output[NUM_CHARACTERS]; { for (int i=0; i < NUM_CHARACTERS; i++) { hkInt32 slot = m_characterProxy[i]->getShapePhantom()->getProperty(HK_SCHEDULE_FREQUENCY).getInt(); if (shouldSimulate(i, m_tick)) { if (i==0) { input[i].m_inputLR = posX; input[i].m_inputUD = posY; input[i].m_wantJump = m_env->m_window->getMouse().wasButtonPressed(HKG_MOUSE_LEFT_BUTTON) || m_env->m_gamePad->wasButtonPressed(HKG_PAD_BUTTON_1); input[i].m_forward.set(1,0,0); input[i].m_forward.setRotatedDir( orient, input[i].m_forward ); } else { hkReal time = hkReal(m_tick) / 60.0f; input[i].m_inputLR = hkMath::sin(time + i); input[i].m_inputUD = hkMath::cos(time + i); input[i].m_wantJump = false; input[i].m_forward.set(1,0,0); } input[i].m_atLadder = false; input[i].m_up = up; input[i].m_stepInfo.m_deltaTime = m_timestep; input[i].m_stepInfo.m_invDeltaTime = 1.0f / (m_timestep * (1 << slot)); input[i].m_characterGravity.set(0,-40,0); input[i].m_velocity = m_characterProxy[i]->getLinearVelocity(); input[i].m_position = m_characterProxy[i]->getPosition(); hkVector4 down; down.setNeg4(up); m_characterProxy[i]->checkSupport(down, input[i].m_surfaceInfo); } } } // Apply the character state machine { HK_TIMER_BEGIN( "update character state", HK_NULL ); for (int i=0; i < NUM_CHARACTERS; i++) { if (shouldSimulate(i, m_tick)) { m_characterContext[i]->update(input[i], output[i]); } } HK_TIMER_END(); } //Apply the player character controller { HK_TIMER_BEGIN( "simulate character", HK_NULL ); for (int i=0; i < NUM_CHARACTERS; i++) { if (shouldSimulate(i, m_tick)) { // Since we're simulatating some characters at less than full frequency we need to adjust the // timestep we pass to the proxy hkInt32 slot = m_characterProxy[i]->getShapePhantom()->getProperty(HK_SCHEDULE_FREQUENCY).getInt(); hkStepInfo csi; csi.m_deltaTime = m_timestep * (1 << slot); csi.m_invDeltaTime = 1.0f / csi.m_deltaTime; // Feed output from state machine into character proxy m_characterProxy[i]->setLinearVelocity(output[i].m_velocity); m_characterProxy[i]->integrate( csi, m_world->getGravity() ); // If the character has fallen off the world we reposition them hkVector4 pos = m_characterProxy[i]->getPosition(); if (pos(1) < -10.0f) { pos.setZero4(); pos(1) = 5; m_characterProxy[i]->setPosition(pos); } } } HK_TIMER_END(); } m_world->unlock(); } // Step the world hkDefaultPhysicsDemo::stepDemo(); { m_world->lock(); // Camera Handling { const hkReal height = 1.7f; hkVector4 from, to; to = m_characterProxy[0]->getPosition(); to.addMul4(height, up); hkVector4 dir; dir.setMul4( height, up ); dir.addMul4( -8.f, input[0].m_forward); from.setAdd4(to, dir); setupDefaultCameras(m_env, from, to, up, 1.0f); } { char buffer[255]; hkString::snprintf(buffer, 255, "%d capsules at 60Hz, %d boxes at 30Hz, %d spheres at 15Hz", m_numCapsules, m_numBoxes, m_numSpheres); m_env->m_textDisplay->outputText(buffer, 20, 410, 0xffffffff); } m_world->unlock(); } return hkDemo::DEMO_OK; }
// // Do some random raycasts into the world // Both the kd-tree and the (deprecated) hkp3AxisSweep versions are used for comparison // void KdTreeVsBroadphaseDemo::doRaycasts() { const int numRays = 100; hkLocalArray<hkpWorldRayCastInput> inputs( numRays ); inputs.setSize(numRays); // Need fixed-size collector arrays to make sure the constructors get called hkpWorldRayCastOutput iterativeCollectors[numRays]; hkpClosestRayHitCollector worldCollectors[numRays]; for (int i=0; i < numRays; i++) { hkVector4 start, end; start.set( hkMath::randRange(-m_worldSizeX, m_worldSizeX), hkMath::randRange(-m_worldSizeY, m_worldSizeY), hkMath::randRange(-m_worldSizeZ, m_worldSizeZ)); end.set( hkMath::randRange(-m_worldSizeX, m_worldSizeX), hkMath::randRange(-m_worldSizeY, m_worldSizeY), hkMath::randRange(-m_worldSizeZ, m_worldSizeZ)); // Flatten out the rays in one component - this triggers a special case in the raycasting code { end(i%3) = start(i%3); } inputs[i].m_from = start; inputs[i].m_to = end; inputs[i].m_filterInfo = 0; } // // Raycast using the world's kd-tree // HK_TIMER_BEGIN("kdTreeRaycast", HK_NULL); // Check that the tree isn't dirty HK_ASSERT(0x3fe8daf1, m_world->m_kdTreeManager->isUpToDate()); for (int i=0; i < numRays; i++) { m_world->castRay(inputs[i], iterativeCollectors[i]); } HK_TIMER_END(); HK_TIMER_BEGIN("worldRc", HK_NULL); { // // Mark the world's kd-tree as dirty, forcing raycasting to go through the old (slow) hkp3AxisSweep algorithm // You should NOT usually be doing this. // m_world->markKdTreeDirty(); for (int i=0; i < numRays; i++) { m_world->castRay(inputs[i], worldCollectors[i]); } } HK_TIMER_END(); // Check that the results agree, and draw the results { for (int i=0; i<numRays; i++) { HK_ASSERT(0x0, iterativeCollectors[i].hasHit() == worldCollectors[i].hasHit()); HK_ASSERT(0x0, hkMath::equal(iterativeCollectors[i].m_hitFraction, worldCollectors[i].m_earlyOutHitFraction)); if (iterativeCollectors[i].hasHit()) { hkVector4 hitpoint; hitpoint.setInterpolate4(inputs[i].m_from, inputs[i].m_to, iterativeCollectors[i].m_hitFraction); HK_DISPLAY_STAR(hitpoint, .1f, hkColor::RED); HK_DISPLAY_ARROW(hitpoint, iterativeCollectors[i].m_normal, hkColor::CYAN); HK_DISPLAY_LINE(inputs[i].m_from, hitpoint, hkColor::BLUE); } else { HK_DISPLAY_LINE(inputs[i].m_from, inputs[i].m_to, hkColor::WHITE); } } } }
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; }
hkDemo::Result RayTraceDemo::stepDemo() { if ( m_env->m_window->getMouse().getButtonState() != 0 ) { m_rotateCounter = 60; } m_rotateCounter--; // rotate the camera viewpoint if (m_rotateCounter <= 0) { hkgWindow* w = m_env->m_window; hkgCamera* c = w->getViewport(0)->getCamera(); hkVector4 from; c->getFrom( &from(0) ); hkVector4 to; c->getTo(&to(0)); hkVector4 up; c->getUp(&up(0)); up.set(0,1,0); hkVector4 d; d.setSub4(to, from); hkQuaternion q( up, .03f); d.setRotatedDir(q, d); from.setSub4(to, d); setupDefaultCameras(m_env, from, to, up ); } // // Do our raytrace // { // // Get our camera information // hkVector4 from; hkVector4 zOffset; hkVector4 upOffset; hkVector4 rightOffset; { from.setZero4(); zOffset.setZero4(); upOffset.setZero4(); rightOffset.setZero4(); hkgWindow* w = m_env->m_window; hkgCamera* c = w->getViewport(0)->getCamera(); c->getFrom( &from(0) ); c->getDir( &zOffset(0) ); c->getUp( &upOffset(0) ); rightOffset.setCross( upOffset, zOffset ); } // // global info // hkVector4 lightPos; lightPos.setAddMul4( from, upOffset, 10 ); hkReal lightStrength = 150.0f; // reflect the light in the normal graphics too { hkVector4 lightDir; hkVector4 lightTo; m_env->m_window->getViewport(0)->getCamera()->getDir( &lightTo(0) ); lightTo.add4( from ); lightDir.setSub4( lightTo, lightPos); setSoleDirectionLight(m_env, &lightDir(0), 0xffffffff); } // // Prepare the data for iterations // { hkgWindow* w = m_env->m_window; hkgCamera* c = w->getViewport(0)->getCamera(); hkReal fa = c->getFar(); zOffset.mul4( fa ); upOffset.mul4( fa * c->getFOV() / 45 * CANVAS_HEIGHT/ CANVAS_WIDTH); rightOffset.mul4( fa * c->getFOV() / 45 ); } hkpShapeRayCastInput ray; ray.m_from = from; ray.m_to.setAdd4( from, zOffset ); ray.m_to.addMul4( 0.5f, upOffset ); ray.m_to.addMul4( 0.5f, rightOffset ); upOffset.mul4( 1.0f / CANVAS_HEIGHT ); rightOffset.mul4( 1.0f / CANVAS_WIDTH ); m_numPrimaryRays = 0; m_numShadowRays = 0; hkpShapeRayCastInput lightRay; lightRay.m_from = lightPos; // // Do the raytracer // HK_TIMER_BEGIN("Raytrace Frame", HK_NULL); m_stopwatch.start(); { hkpShapeRayCastOutput output; hkpShapeRayCastOutput output2; int* data = reinterpret_cast<int*>(m_texture->getDataPointer()); hkVector4 yStart = ray.m_to; for (int y = 0; y < CANVAS_HEIGHT; y++) { ray.m_to = yStart; for (int x = 0; x < CANVAS_WIDTH; x++ ) { #if (HK_ENDIAN_BIG==1) data[0] = 0x3f3f7fFF; // background (rgba in that byte order) #else data[0] = 0xFF7f3f3f; // background #endif output.reset(); m_numPrimaryRays++; m_shape->castRay( ray, output ); if ( output.hasHit() ) { hkReal brightness = .4f; hkVector4 hitPos; hitPos.setInterpolate4( ray.m_from, ray.m_to, output.m_hitFraction ); hkVector4 lightDir; lightDir.setSub4( lightPos, hitPos ); // get the direct light info { hkReal lightDot = output.m_normal.dot3( lightDir ); if ( lightDot > 0.0f ) { // // Do shadows // output2.reset(); lightRay.m_to.setInterpolate4( lightRay.m_from,hitPos, 0.999f); m_numShadowRays++; if (!m_shape->castRay( lightRay, output2 )) { hkReal lightDirInvLen = lightDir.lengthInverse3(); brightness += lightDot * lightDirInvLen * lightStrength; // add some glare { hkVector4 reflectedDir; reflectedDir.setAddMul4( lightDir, output.m_normal, -2.0f * lightDot ); hkVector4 rayDirection; rayDirection.setSub4( ray.m_to, ray.m_from ); hkReal dot2 = reflectedDir.dot3( rayDirection ); if ( dot2 > 0 ) { dot2 *= lightDirInvLen; dot2 *= dot2; dot2 /= rayDirection.lengthSquared3(); dot2 *= dot2; dot2 *= dot2; dot2 *= dot2; dot2 *= dot2; brightness += dot2 * lightStrength * 2; } } } } } unsigned s = (unsigned)hkMath::hkToIntFast( brightness ); if ( s > 255 ) s = 255; #if (HK_ENDIAN_BIG==1) data[0] = 0x000000ff | (s<<24) | (s<<16) | (s<<8); // rgba in that byte order #else data[0] = 0xff000000 | (s<<16) | (s<<8) | s; #endif } data++; ray.m_to.sub4( rightOffset ); } yStart.sub4( upOffset ); } } m_stopwatch.stop(); HK_TIMER_END(); } // // Output statistics // { hkReal timePerRayuSecs = m_stopwatch.getSplitSeconds() * 1e6f / (m_numPrimaryRays + m_numShadowRays); m_stopwatch.reset(); char buf[512]; hkString::snprintf(buf, 512, "Primary Rays:%d\nShadow Rays:%d\nAverage time per ray:%f microsecs", m_numPrimaryRays, m_numShadowRays, timePerRayuSecs); m_env->m_textDisplay->outputText(buf, 20, 350); } // // Draw the texture (really slow as we don't have a quicker reinit for the textures / direct write yet in the HKG) // m_env->m_window->getContext()->lock(); if (m_textureRealized) { m_texture->free(); } m_texture->realize(true); m_env->m_window->getContext()->unlock(); m_textureRealized = true; return DEMO_OK; }
hkDemo::Result MultipleCharacterRbsDemo::stepDemo() { HK_TIMER_BEGIN( "simulate multiply characters", HK_NULL ); m_world->lock(); hkVector4 up; up.setNeg4( m_world->getGravity() ); up.normalize3(); int numCharacters = m_characterRigidBodies.getSize(); m_tick++; hkLocalArray<hkpCharacterInput> input( numCharacters ) ; input.setSize(numCharacters); // Fill in the state machine input structure { for (int i=0; i < numCharacters; i++) { input[i].m_atLadder = false; input[i].m_up = up; // Steer the characters hkReal time = hkReal(m_tick) / 60.0f; input[i].m_inputLR = hkMath::sin(time + i); input[i].m_inputUD = hkMath::cos(time + i); input[i].m_wantJump = false; input[i].m_forward.set(1,0,0); hkStepInfo stepInfo; stepInfo.m_deltaTime = m_timestep; stepInfo.m_invDeltaTime = 1.0f/m_timestep; input[i].m_stepInfo = stepInfo; input[i].m_characterGravity = m_world->getGravity(); input[i].m_velocity = m_characterRigidBodies[i]->getLinearVelocity(); input[i].m_position = m_characterRigidBodies[i]->getPosition(); hkpSurfaceInfo ground; m_characterRigidBodies[i]->checkSupport(stepInfo, ground); input[i].m_isSupported = (ground.m_supportedState == hkpSurfaceInfo::SUPPORTED); input[i].m_surfaceNormal = ground.m_surfaceNormal; input[i].m_surfaceVelocity = ground.m_surfaceVelocity; } } hkLocalArray<hkpCharacterOutput> output( numCharacters); output.setSize(numCharacters); // Apply the character state machine { HK_TIMER_BEGIN( "update character state", HK_NULL ); for (int i=0; i < numCharacters; i++) { m_characterContexts[i]->update(input[i], output[i]); } HK_TIMER_END(); } //Apply the character controllers { HK_TIMER_BEGIN( "simulate character", HK_NULL ); for (int i=0; i < numCharacters; i++) { // Feed the output velocity from state machine into character rigid body m_characterRigidBodies[i]->setLinearVelocity(output[i].m_velocity, m_timestep); // If the character has fallen off the world we reposition them hkVector4 pos = m_characterRigidBodies[i]->getRigidBody()->getPosition(); if (pos(1) < -10.0f) { pos.setZero4(); pos(1) = 5; m_characterRigidBodies[i]->getRigidBody()->setPosition(pos); } } HK_TIMER_END(); m_world->unlock(); } // Step the world { hkDefaultPhysicsDemo::stepDemo(); } HK_TIMER_END(); return hkDemo::DEMO_OK; }
///[stepGame] /// This is called every simulation timestep. We need to /// - Steer the first vehicle based on user input. /// - Step the simulation. /// - Sync each vehicle's display wheels. /// - Update the camera that follows the first vehicle. /// - Draw skidmarks for the first vehicle if it is skidding. /// - Update the RPM meter and speedometer. /// hkDemo::Result VehicleManagerDemo::stepDemo() { const MTVehicleRayCastDemoVariant& variant = g_MTVehicleRayCastDemoVariants[m_variantId]; // Steer the vehicle from user input. { m_world->markForWrite(); steer(); m_world->unmarkForWrite(); } m_world->markForWrite(); HK_TIMER_BEGIN( "Simulate vehicles", HK_NULL ); if ( variant.m_demoType == MTVehicleRayCastDemoVariant::MULTITHREADED_RAY_CAST || variant.m_demoType == MTVehicleRayCastDemoVariant::MULTITHREADED_LINEAR_CAST ) { // Multithreaded m_vehicleManager->stepVehiclesSynchronously( m_world, m_jobThreadPool, m_jobQueue, NUM_SPUS ); } else { // Singlethreaded m_vehicleManager->stepVehicles( m_world->m_dynamicsStepInfo.m_stepInfo ); } HK_TIMER_END(); m_world->unmarkForWrite(); // // Step the world. // { hkDefaultPhysicsDemo::stepDemo(); } { m_world->markForWrite(); for (int vehicleId = 0; vehicleId < m_vehicles.getSize(); vehicleId++ ) { VehicleApiUtils::syncDisplayWheels(m_env, *m_vehicles[vehicleId].m_vehicle, m_displayWheelId[vehicleId], m_tag); } // Update the "follow" camera. VehicleDisplayUtils::VehicleDataAndDisplayInfo& playerVehicle = m_vehicles[0]; if (m_followCarView) { VehicleApiUtils::updateCamera( m_env, *playerVehicle.m_vehicle->getChassis(), m_timestep, m_camera); } VehicleDisplayUtils::updateTyremarks( m_timestep, playerVehicle.m_vehicle ); VehicleDisplayUtils::updateInfo( m_env, m_vehicles[0] ); m_world->unmarkForWrite(); } return DEMO_OK; }