Game Development Community

dev|Pro Game Development Curriculum

Constraining camera to terrain / interiors

by Keith Johnston · 12/13/2002 (3:08 pm) · 9 comments

This code stops the camera in observer mode from entering interiors and the terrain by maintaining a fixed distance from the terrain and walls. As the camera collides with the terrain / walls, it 'skims' along the surface, continuing to move in the directions that it can move - just not in the direction where the collision occurs.

Note: if the camera moves too quickly this code does not catch the collisions. I'm hoping someone can help me figure out why. Also - I have only tested this on windows, and only with the 1.1.2 revision.

In camera.h, add this function:

Point3F updatePos(const Point3F& oldPos,const Point3F& newPos, F32 testLen);

In camera.cc, add the implementation:

Point3F Camera::updatePos(const Point3F& oldPos,const Point3F& newPos, F32 testLen)
{
	if (oldPos==newPos || gEditingMission)
		return newPos;
	
	Point3F test=newPos-oldPos;
	F32 len = test.len();
	test.normalize();
	Point3F testPos = oldPos+(test*(len+testLen));
	
	RayInfo rinfo;

	if (getContainer()!=NULL) {
		if (getContainer()->castRay(oldPos, testPos, (InteriorObjectType | TerrainObjectType), &rinfo)) {
			float dot = mDot(test,rinfo.normal);
			if (dot < 0) {
				// Get a vector for the direction and distance the test point overshot
				Point3F oops = testPos - rinfo.point;
				// Get the length we shot past
				F32 newLen = mDot(oops,rinfo.normal);
				if (newLen > testLen)
					newLen=testLen;
				
				// Subtract the vector that took us past the collision point
				return (newPos - newLen*rinfo.normal);
			} else
				return newPos;
		}
		else {
			return newPos;
		}
	} else {
		return newPos;
	}   
}

In processTick(), right before the setPosition when not in orbit mode,
add a call to updatePos.

...
if(mode == OrbitObjectMode || mode == OrbitPointMode)
      {
         if(mode == OrbitObjectMode && bool(mOrbitObject))
            mOrbitObject->getWorldBox().getCenter(&mPosition);
         setPosition(mPosition, mRot);
         validateEyePoint(1.0f, &mObjToWorld);
         pos = mPosition;
      }
      else
      {
         // Update pos
         bool faster = move->trigger[0] || move->trigger[1];
         F32 scale = mMovementSpeed * (faster + 1);

         mObjToWorld.getColumn(3,&pos);
         mObjToWorld.getColumn(0,&vec);
         pos += vec * move->x * TickSec * scale;
         mObjToWorld.getColumn(1,&vec);
         pos += vec * move->y * TickSec * scale;
         mObjToWorld.getColumn(2,&vec);
         pos += vec * move->z * TickSec * scale;
		 
	 pos = updatePos(getTransform().getPosition(),pos,5.0); // <- added call

         setPosition(pos,mRot);
      }

Also added the same call in interpolateTick:

void Camera::interpolateTick(F32 dt)          
{
   Parent::interpolateTick(dt);
   Point3F rot = delta.rot + delta.rotVec * dt;

   if(mode == OrbitObjectMode || mode == OrbitPointMode)
   {
      if(mode == OrbitObjectMode && bool(mOrbitObject))
         mOrbitObject->getRenderWorldBox().getCenter(&mPosition);
      setPosition(mPosition, rot);
      validateEyePoint(1.0f, &mObjToWorld);
   }
   else {
      Point3F pos = delta.pos + delta.posVec * dt;
      pos = updatePos(getTransform().getPosition(),pos,5.0); // <-- added call
      setPosition(pos,rot);
   }
}

#1
12/13/2002 (3:22 pm)
This will also apply in 3rd PPOV mode too, do you think ?
#2
12/14/2002 (7:10 am)
Possibly - unfortunately I've modified things so much that I can't easily test that (I don't have a player model - just a camera).
#3
12/16/2002 (1:39 pm)
Works great Keith! I want to also look at disabling this when editing though
#4
12/20/2002 (7:14 am)
Desmond - thanks for the feedback! I just updated the function updatePos in this resource to do as you suggested: the camera now works as normal when editing the mission.
#5
02/20/2003 (12:16 am)
Does anyone else have a problem with the camera being extremely jittery when sliding along a wall? When it's in a corner and is being moved against it, it jitters and shakes like a chihuahua on caffeine. Did I screw this up somehow?
#6
03/07/2003 (1:17 pm)
Well this thing still works in current head (7. march 2003) however I found a bug.
In multiplayer if you are not the server and if you die and wait till body desapears. The camera is then thrown somwhere below the map and you cannot move from that spot. You can respawn and play normaly tho.
#7
12/14/2003 (3:44 pm)
James, I have the jittery problem when sliding along walls.
#8
12/10/2007 (6:56 pm)
this code works with 1.5.x ?
#9
05/28/2009 (3:15 am)
i tried this resource for TGEA1.8.1 , but it is still going inside of the interiors and terrain. the collision is not working properly. can you tell any solution to implement this for TGEA1.8.1?? thanks in Advance..