[Java] Smooth 3rd-person camera!

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Pyritie
Posts: 120
Joined: Fri Jan 16, 2009 12:59 pm
Contact:

[Java] Smooth 3rd-person camera!

Post by Pyritie »

It's in java so I dunno how useful this might be.

Based on some code vitek posted a while ago.

PViewer.receiver = my eventreceiver

PViewer.elapsedTime

Code: Select all

elapsedTime = 1000000 * (timer2 - timer1) * (0.000001f / 30f); // in seconds
timer1 = milliseconds before you draw the scene (at the beginning of the run loop)
timer2 = milliseconds after you've done everything in the run loop

Some constants in PMain

Code: Select all

public static final short CAMERA_MAX_ZOOM_DISTANCE = 1000;
	public static final short CAMERA_MIN_ZOOM_DISTANCE = 90;
	public static final float CAMERA_LINEAR_VELOCITY = 5f;  // zoom speed
	public static final float CAMERA_ANGULAR_VELOCITY = 0.5f;  // camera turn speed on mouse drag
The camera task, run this every frame

Code: Select all

	class CameraTask {
		private float radius = 400f;
		private float theta = 180f;
		private float phi = 60f;
		private float radiusVelocity = 0;
		private float thetaVelocity = 0;
		private float phiVelocity = 0;
		private final float thetaFriction = 1.3f;
		private final float thetaMaxVelocity = 40;
		private final float phiFriction = 1.3f;
		private final float phiMaxVelocity = 40;
		private final float radiusFriction = 1.3f;
		private final float radiusMaxVelocity = 50;
		private ArrayList<Float> radiusAverages = new ArrayList<Float>(0);
		private ArrayList<Float> thetaAverages = new ArrayList<Float>(0);
		private ArrayList<Float> phiAverages = new ArrayList<Float>(0);
		private final int averageSampleSize = 10; // Lower to make it less smooth, Higher to make it more smooth (but then it starts to jump around a lot)
		
		public CameraTask() {
			for (int a = 0; a < averageSampleSize; a++) {
				radiusAverages.add(0f);
				thetaAverages.add(0f);
				phiAverages.add(0f);
			}
		}
		
		public void run() {
			if (theta > 360 || theta < 0) theta %= 360;
			/*
			 * Zoom in/out
			 */
			if (PViewer.receiver.getLastWheelMovement() > 0) { // zoom in
				if (radiusVelocity > -radiusMaxVelocity)
					radiusVelocity -= PMain.CAMERA_LINEAR_VELOCITY * PViewer.receiver.getLastWheelMovement() * PViewer.elapsedTime * 10;
				
				PViewer.receiver.setLastWheelMovement(0);
			} else if (PViewer.receiver.getLastWheelMovement() < 0) { // zoom out
				if (radiusVelocity < radiusMaxVelocity)
					radiusVelocity -= PMain.CAMERA_LINEAR_VELOCITY * PViewer.receiver.getLastWheelMovement() * PViewer.elapsedTime * 10;
				
				PViewer.receiver.setLastWheelMovement(0);
			} else {
				// if the mouse isn't moving then apply friction
				if (radiusVelocity < -0.1f || radiusVelocity > 0.1f)
					radiusVelocity = radiusVelocity / radiusFriction;
				else
					radiusVelocity = 0;
			}
			/*
			 * Rotate and translate left/right
			 */
			if (PViewer.receiver.getCursorDelta().getX() > 0) { // rotate clockwise
				if (PViewer.receiver.isMouseDownL()) {
					if (thetaVelocity < thetaMaxVelocity)
						thetaVelocity += PMain.CAMERA_ANGULAR_VELOCITY * PViewer.receiver.getCursorDelta().getX() * PViewer.elapsedTime;
				}
				PViewer.receiver.getCursorDelta().setX(0);
			} else if (PViewer.receiver.getCursorDelta().getX() < 0) { // rotate anticlockwise
				if (PViewer.receiver.isMouseDownL()) {
					if (thetaVelocity > -thetaMaxVelocity)
						thetaVelocity += PMain.CAMERA_ANGULAR_VELOCITY * PViewer.receiver.getCursorDelta().getX() * PViewer.elapsedTime;
				}
				PViewer.receiver.getCursorDelta().setX(0);
			} else {
				if (thetaVelocity < -0.1f || thetaVelocity > 0.1f)
					thetaVelocity = thetaVelocity / thetaFriction;
				else
					thetaVelocity = 0;
			}
			/*
			 * Rotate and translate up/down
			 */
			if (PViewer.receiver.getCursorDelta().getY() > 0) { // rotate up
				if (PViewer.receiver.isMouseDownL()) {
					if (phiVelocity < phiMaxVelocity)
						phiVelocity -= PMain.CAMERA_ANGULAR_VELOCITY * PViewer.receiver.getCursorDelta().getY() * PViewer.elapsedTime;
				}
				PViewer.receiver.getCursorDelta().setY(0);
			} else if (PViewer.receiver.getCursorDelta().getY() < 0) { // rotate down
				if (PViewer.receiver.isMouseDownL()) {
					if (phiVelocity < phiMaxVelocity)
						phiVelocity -= PMain.CAMERA_ANGULAR_VELOCITY * PViewer.receiver.getCursorDelta().getY() * PViewer.elapsedTime;
				}
				PViewer.receiver.getCursorDelta().setY(0);
			} else {
				if (phiVelocity < -0.1f || phiVelocity > 0.1f)
					phiVelocity = phiVelocity / phiFriction;
				else
					phiVelocity = 0;
			}
			
			if (thetaVelocity >= thetaMaxVelocity) thetaVelocity = thetaMaxVelocity;
			else if (thetaVelocity <= -thetaMaxVelocity) thetaVelocity = -thetaMaxVelocity;
			if (phiVelocity >= phiMaxVelocity) phiVelocity = phiMaxVelocity;
			else if (phiVelocity <= -phiMaxVelocity) phiVelocity = -phiMaxVelocity;
			if (radiusVelocity >= radiusMaxVelocity) radiusVelocity = radiusMaxVelocity;
			else if (radiusVelocity <= -radiusMaxVelocity) radiusVelocity = -radiusMaxVelocity;
			
			radiusAverages.remove(0);
			radiusAverages.add(radiusVelocity);
			thetaAverages.remove(0);
			thetaAverages.add(thetaVelocity);
			phiAverages.remove(0);
			phiAverages.add(phiVelocity);
			
			float radiusAverageVelocity = 0, thetaAverageVelocity = 0, phiAverageVelocity = 0;
			for (int a = 0; a < averageSampleSize; a++) {
				radiusAverageVelocity += radiusAverages.get(a);
				thetaAverageVelocity += thetaAverages.get(a);
				phiAverageVelocity += phiAverages.get(a);
			}
			
			radiusAverageVelocity /= averageSampleSize;
			thetaAverageVelocity /= averageSampleSize;
			phiAverageVelocity /= averageSampleSize;
			
			if (radiusAverageVelocity != 0) radius += radiusAverageVelocity;
			if (thetaAverageVelocity != 0) theta += thetaAverageVelocity;
			if (phiAverageVelocity != 0) phi += phiAverageVelocity;
			
			// min zoom
			if (radius < PMain.CAMERA_MIN_ZOOM_DISTANCE)
				radius = PMain.CAMERA_MIN_ZOOM_DISTANCE;
			else if (radius > PMain.CAMERA_MAX_ZOOM_DISTANCE)
				radius = PMain.CAMERA_MAX_ZOOM_DISTANCE;
			
			
			/*
			 *  lame ass gimble lock prevention. if you don't want to do this 
			 *  you need to adjust the up vector of the camera so it never is 
			 *  parallel to the look at vector 
			 */
			if (phi < 1f)
				phi = 1f;
			else if (179f < phi)
				phi = 179f;
			
			float sinOfPhi   = (float)Math.sin(VF.D2R(phi));
			float cosOfPhi   = (float)Math.cos(VF.D2R(phi));
			float sinOfTheta = (float)Math.sin(VF.D2R(theta));
			float cosOfTheta = (float)Math.cos(VF.D2R(theta));
			vector3df offset = new vector3df();
			offset.setX(radius * sinOfTheta * sinOfPhi);
			offset.setY(radius * cosOfPhi);
			offset.setZ(radius * cosOfTheta * sinOfPhi);
			
			// camera is a child of the cube, so our offset is actually
			// the position of the camera
			PViewer.camera.setPosition(offset);
			PViewer.camera.updateAbsolutePosition();
			offset.delete();
		}
	}
Hive Workshop | deviantART

I've moved to Ogre.
Post Reply