Example #1
0
	void FPSCamera::rotate(float horizontal, float vertical) {
		m_rotations.y -= horizontal*0.005f*m_rotateSpeed;
		m_rotations.x += vertical*0.005f*m_rotateSpeed;

		if(m_rotations.x < -89.f)
			m_rotations.x = -89.f;
		else if(m_rotations.x > 89.f)
			m_rotations.x = 89.f;

		glm::mat4 rot;
		rot = glm::rotate(rot, m_rotations.y, absoluteUp());
		rot = glm::rotate(rot, m_rotations.x, absoluteLeft());
		auto p = glm::vec3(rot*glm::vec4(absoluteForward(), 1.f));
		/*m_left = -glm::cross(m_forward, absoluteUp());
		m_up = glm::cross(m_forward, m_left);
		m_target = position + m_forward;
		m_viewDirty = true;*/
		lookAt(position+p);
	}
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;
}