Arcball From Anywhere

Hello; sorry for the delay in the response.

I'll be referring to the OpenGL Red Book once or twice, see if you can check it out at a library (or better yet, buy it.)

First of all thanks for NeHe lesson 48, that's very useful code… I added it to a 3D viewer, and everything works fine as long as my object coordinates are centered around 0 and scaled to something like [-1,1].

By the sound of it, your 3D viewer application probably has some restricted frustum setup.

In the example I use gluPerspective, its on pdf page 88 of the redbook.

Its also possible(but probably unlikely) that your model loader scales the vertices by some specified when loading them. (From what I understand, Quake MD2 and MD3s use this method to store data more compactly.) This would mean that you might be loading a sphere of radius 30, but it might be scaled outside of the view frustum.

However, I'm having a tough time figuring out where I need to put translations if I want to keep the objects in world coordinates.

Well, in the example, anything labeled “dynamic transform” are in world coordinates. In general, 3D models should always have their translations in world coordinates.

Basically, what's supposed to happen is that your “camera transform” “undoes” your “world transform.” Although, at the same time, the “perspective” and “view volume” transforms are also done. So, that is to say, if my model that's a 30 foot radius sphere is 10 feet NE from the origin, and the camera is located at 10 feet N from the origin, looking E, with an aspect ratio of 45 degrees visibility from 0.0(where i am) to 100.0 feet in front of me, then, when it's rendered, to me, in a “local space,” the model will appear to be 10 feet ahead into the screen(that's the east component.)

I've included a diagram that may shed some more light on it as an attachment.

Just as a matter of interest, all static transforms get combined into one uber transform that we generally call a “projection matrix.” The idea is that the camera matrix(dynamic) gets combined into this matrix for each video frame, and then each model's transformation matrix is multiplied by the resultant “temporary projection matrix” to find its on-screen rendering position.

Even though in the example I use 45 degrees(for consistency with other NeHe tutorials), I believe 60 is actually more “accurate” in real life.

Ideally I need to be able to set up a COI (center of interest) and a BBOI (bounding box of interest) in world coordinates and end up with that part of the world centered in the viewport and appropriately scaled.

Right- so again, *in general* your “COI” will be your camera (use glLookAt, pdf page 83.) Your “BBOI” will be your view frustum. Just for view purposes, this is solved. I'll get to your particular problem in a sec.

With a lot of polygon data you will eventually want to use some kind of binary tree, k-ary tree, or graph to represent your polygon data- that is, if it is huge enough to warrant that. This will ensure that you're not drawing tons and tons of data that you never need to draw, but until then your view frustum will guarantee that nothing is drawn that's out of bounds.

Also, when the user drags the mouse the view should rotate around the COI.
I know that the answer lies in translating and scaling the view at the right moment, and in combining that transformation with the arcball rotation matrix, but the solution still escapes me…

Yeah, I can undestand why you would want that. Luckily the ArcBall itself doesn't need to know about translations, all it cares about is rotation.

The dynamic model matrices are computed as “rotate about axis, then translate”, and what you need is an additional translation *before* this gets committed. It may feel a little backwards, but all you have to do is insert a translation after you've pushed the dynamic translation of the model.

So, look for “glMultMatrixf(Transform.M);” in the example, and put a glTranslate your COI's coordinates in *after* that. The reason this feels backward is because (from memory) I believe that the order in which you're required to combine matrices is backward, so if you want something to happen “before” something else, you add it in last.

I just gave that a shot over here and it works like you'd want it to, so I think you're good to go.

Ok, so in summarization:

It'll look like this when you're finished:

glTranslatef(-1.5f,0.0f,-6.0f);
glPushMatrix();
glMultMatrixf(Transform.M);
glTranslatef(COI.X,COI.Y,COI.Z);

One more note, and then I promise I'm done. The (-1.5, 0.0, 6.0) translation should actually be coded into Transform's translation members- that's .TX, .TY, .TZ and the glTranslate containing said point removed. For the example I wanted the two objects to share one rotation, which is why it was done that way.