Jump to content

OrbitObject Camera


TorqueFan

Recommended Posts

I've found a game specific problem that only appears if you are using orbitObject mode for the camera. I was hoping someone may be able to give some advice as to how to get this issue resolved. It's super easy to replicate the problem with a stock build:


• Open up scripts/gameCore.cs and setup the camera for sideview. Here's a sample script (personally I dropped this near the bottom of the spawnPlayer() function):

	%client.setFirstPerson(false);	
%client.setCameraObject(%client.camera);
%client.camera.setOrbitObject(%client.player, "0 0 0", 10, 20, 20, false, "0 0 0", false);
%client.camera.camDist = 10;

 

• Next, just open up an example level (EmptyRoom.mis) and set the visibleDistance fairly low (to see the problem without having to run far):

      visibleDistance = "20";

 

• If you run off with the Player, he will disappear once you reach the distance set by "visibleDistance".


C++

Now, I did a good bit of digging and found the root of the issue. It stems from some changes to the engine sometime after 3.6(ish). At some point, quite a few files were updated to include a new field: mVisibleGhostDistance. A quick search for 'mVisibleGhostDistance' will reveal the field being used in levelInfo.h/.cpp, gameConnection.cpp, sceneObject.cpp, and sceneManager.h/.cpp. Most importantly, in sceneObject.cpp:

   if (conn && (query->visibleDistance = conn->getVisibleGhostDistance()) == 0.0f)
     if ((query->visibleDistance = sceneManager->getVisibleGhostDistance()) == 0.0f)
        query->visibleDistance = sceneManager->getVisibleDistance();

So here the mVisibleGhostDistance is going to be set to the VisibleDistance. By the description in the EngineMethod, the mVisibleGhostDistance is the distance from the camera at which Players are no longer ghosted.


So why, then, is the Player being un-ghosted when the camera is (supposedly) orbiting the player? Because, in truth, when the camera is set to orbit the Player it isn't actually mounted to the Player object. When the camera is set to orbit the Player, for some unknown reason if the Player moves off to the side someplace the 'real' camera object stays back where he left it originally. So if the Player travels out of the 'real' camera's 'visibleGhostDistance' ....poof! he's gone!


I thought it would be easy, so I tried to just mount the camera to the player. LOL, give that a try and then get back to me...I'll even provide a sample script:

%player.mountObject(%client.camera, "cam");

 

Anyways, ultimately, if all of the mVisibleGhostDistance stuff is removed from the engine code the problem is gone. But that isn't a real solution now is it? Not when the goal of the field in the first place was to unghost the Player objects not within range. What I do think is a 'true' solution is finding a way to have the camera's orbitObject() function actually mount the 'real' camera to the Player object so that the camera actually travels with the Player in orbitObject mode. Any comments or suggestions?

Link to comment
Share on other sites

Not sure what that mountObject command is doing. According to the docs mountObject takes an int, not a node name.


mVisibleGhostDistance was introduced here. It's the orbit camera that needs fixing; I'm glad somebody noticed this. I've tried to use camera modes before and been burned by the fact that they aren't really positioned properly, so you can't do a getTransform to see what they're looking at for example.

Link to comment
Share on other sites

That's the same conclusion I came to, thanks for the confirmation. I also thought it was odd that the mountObject accepted the 'cam' string and still mounted the camera, which is why I included it as well - it could be related to the problem.


Go ahead and try that mountObject command though, it does 'work' with the node name.


Only 'works' though because it gives the same result as mounting the camera to any other node(number or otherwise). But regardless of whether a name or an int are used, the mounted camera ends up with a transform that's completely off. On the stock soldier model the camera will be pointing straight up, on my custom model it's near-isometric(with the orbitObject bit plugged in as well).

Link to comment
Share on other sites

Oh right, it'll 'work' because the string "cam" is cast to the integer 0, so it mounts to mount point 0.


Cameras probably don't respond to mounting; I'm not sure what changed when mounting was moved from ShapeBase to SceneObject, but it used to be that every class had to implement mounting. Actually that's definitely still the behaviour or we wouldn't have #680.


So, we should definitely note that the camera classes need a good reworking.

Link to comment
Share on other sites

Gotcha, makes sense. Thank you for your input - it's good to know that this is an issue that's been noticed by others.


I hadn't had the time to dig into camera mounting code yet, for now I can live with having a larger visibleDistance. I'm unsure whether this will matter or not, given a fixed camera angle.


From what I've been able to deduce, if an object's bounding box is outside the camera's viewing frustum the object isn't rendered. Unfortunately not rendered doesn't mean not scoped.


If I had to hazard a guess I would assume anything within the visibleDistance is being scoped. Which is a pretty big deal if you really think about it. Take, for instance, an isometric styled game. The camera is angled downwards so all you see is a small area around the player. If there are objects clear across the scene not in that viewing frustum being scoped that's really inefficient. Then if you try to set the visibleDistance lower so that doesn't happen, your player can only move like 20 feet.


If a camera could be mounted properly this would be solved, you are 100% correct! The visibleDistance could be set to just outside the viewable frustum, and that visibleDistance culling would travel with the player. Hmm, looks like it's time to give the orbitObject functions a good look-over to implement mounting...

Link to comment
Share on other sites

Okay, I have tracked down a fix for the issue. Credit goes to Adam Beer for spotting the problem area and starting to fix it, and Ivan Mandzhukov for ultimately solving it. In this thread Adam and Ivan unknowingly solved the bulk of this problem while tuning the camera for a different purpose.


I didn't have to do any heavy lifting, really, just mainly some detective work and then I cleaned up the code and updated the 'delta' variable. Basically this code allows the camera to be rotated instead of just hitting the if(isMounted()) statement and having the transform explicitly set without taking any pitch or yaw into account. I've tested this code with a tiny view distance of 10, orbitCamera, and the camera mounted to the Player. Gloriously enough the Player can travel freely taking the camera with him while scoping within viewDistance correctly.


• In camera.cpp find the processTick(). Within this function, replace the isMounted() bit with:

   if (isMounted())
  {
   MatrixF mat;
   VectorF rotVec(0, 0, 0);

   if (move) 
   {
	   rotVec.x = move->pitch;
	   rotVec.z = move->yaw;

	   mRot.x += rotVec.x;
	   mRot.z += rotVec.z;
   }

   MatrixF xRot, zRot, fmat;
   xRot.set(EulerF(rotVec.x, 0, 0));
   zRot.set(EulerF(0, 0, rotVec.z));

   // let's combine the euler rotation
   fmat.mul(zRot, xRot);

   // this will pull the transform and translate to world space
   // we need only the world position,we discard rotation values
   mMount.object->getMountTransform(mMount.node, mMount.xfm, &mat);

   Point3F position;
   mat.getColumn(3, &position);
   fmat.setColumn(3, position);
   Parent::setTransform(fmat);
   updateContainer(); 
   return;
  }

 

• Next up, in camera.cpp find the interpolateTick(). Within this function, replace the isMounted() bit with:

   if (isMounted())
  {
   MatrixF mat;
   Point3F rot = mDelta.rot + mDelta.rotVec * dt;
   MatrixF xRot, zRot, fmat;
   xRot.set(EulerF(rot.x, 0, 0));
   zRot.set(EulerF(0, 0, rot.z));

   // let's combine the euler rotation
   fmat.mul(zRot, xRot);

   // this will pull the transform and translate to world space
   // we need only the world position,we discard rotation values
   mMount.object->getRenderMountTransform(dt, mMount.node, mMount.xfm, &mat);

   Point3F position;
   mat.getColumn(3, &position);
   fmat.setColumn(3, position);
   Parent::setRenderTransform(fmat);
   return;
  }

 

Note:

This solves the wacky transform when setting the camera to orbitObject mode and then mounting it to the Player. So this allows the camera to be mounted in a useful way. What this doesn't do is allow the camera to be mounted properly to any designated node. The node name declared in the mount(%target, %node) function could just as well be named "Tom's Cat". The camera will always be mounted to what appears to be the Player's root node. This should be only a small inconvenience given the optional transform settings for the mount() function, although I hadn't tested this to be sure it is indeed applying the optional transform.

Link to comment
Share on other sites

Of course the nodes are 'supposed' to use node IDs!!! That's why I commented specifically that it still isn't 100% 'correct'... My reference to using a string above served to illustrate that, for whatever reason, it doesn't matter which ID or String you use(which you shouldn't). In any case, the camera will mount to a generic position to the Player. I'll quote myself:

 

What this doesn't do is allow the camera to be mounted properly to any designated node.

 

See, no matter which mount0, mount1 node I mount the camera to it will result in the same transform for the camera as if I named the node with a string(which again technically shouldn't be possible). But, no matter, it works as intended for my purposes. If I get some time this week, I'd be more than happy to PR it but will need to be sure I can do it properly since I hadn't had to do a PR before :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...