3D ReadMe

Contributed to the public domain by Lars Jensen
Updated Jan 27, 2004 (added RBDeveloper errata)


INTRODUCTION

REALbasic is the easiest, fastest, cheapest way I know to do cross-platform (Mac and Windows) 3D. It's not perfect, but it will get you started with a minimum of hassle. But that doesn't mean that there are no hassles, one of them being documentation -- the doc that comes with REALbasic is lacking in "getting started" info, and not accurate in some places.

That's where this document comes in. I've attempted to collect various hard-won pieces of knowledge into this one page, so that you can go from zero to 3D with as few potholes as possible.

Joe Strout's 3D FAQ: Joe Strout, the driving force behind REALbasic's 3D system, has provided an excellent 3D FAQ to help get you going. This should be your starting point to learn about RB3D. The page you're looking at should be considered a companion to Joe's document, not a replacement for it -- in other words, if you haven't got Joe's FAQ yet, do so now, and skim through it at least.


BACKGROUNDS
Because of fairly sketchy documentation, backgrounds were a stumbling block for me when getting started with 3D. But there's no reason for them to be; they're pretty simple. The key is that a background is a 3D shape like any other object, it's just that you happen to be inside it rather than outside, and it's treated a little differently by the system. You just have to be aware of a few things:

Automatic repositioning: Whenever the scene is rendered, if RB3DSpace.Background is not nil, then that object is automatically repositioned so that its origin is at the camera Position. You can set its Position to be something else, but when the scene is rendered, the background Position will be reset to the camera Position again. That is the whole point of backgrounds; it's what gives them that "far away" feel as the camera moves around the closer objects.

(Note that background orientation is not automatically updated in this way. So, for example, you could have a background textured with a starry night sky that slowly rotates.)

Automatic null shader: Backgrounds are automatically rendered with a null shader, so that any local lighting (or lack of it) won't affect the look of the background. You don't want to see glare on the sky when you turn on a flashlight...

Background size: The background needs to be big enough to surround all the other objects in the scene -- or at least the ones you want to see. But if it's bigger than your RB3DSpace.Yon value, you won't see its farthest-away parts.

Surface orientation: Since the background object always surrounds the camera, its surfaces need to be oriented inward, not outward as is the case with most other objects. In whatever tool you use to create the background model, make sure the surface normals point toward the interior of the object. (A shortcut is to set RenderBackFaces to true for the background object, but that would be inefficient, because you'd be doing a lot of unnecessary processing at runtime).

That's all that is special about backgrounds. In fact, you could implement a background yourself with a regular 3D object if you wanted to -- just turn on NullShader and keep updating the object's position to match the camera's position, and you'd have your background. The RB3DSpace.Background property is just a more convenient way to get the same result.

Many backgrounds are simply square boxes with a texture on each side (called "skyboxes"). In my games, I tend to use cylinders because they look better when the camera goes up high, and because it's easier to wrap the kind of textures around them that I tend to find on the Web. (With skyboxes, the textures need to be aligned just right so they match up on the cube edges. There are tools for this, but I haven't gotten around to learning them yet.)


BUGS
REALbasic Developer errata: These aren't RB bugs, but I didn't have a better place to put significant errors in key REALbasic Developer articles. I know of only one at present: In issue 1.1, in an article about using 3D for sprite-based animation, the distance formula in the magazine uses sin(angle) instead of tan(angle). The correct mathematics are something like:

  degrees = 0.0174532925 // (radians per degree)
angle = view.fieldOfView * degrees * 0.5
if view.width < view.height then
  distance = (view.width * 0.5) / tan(angle)
else
  distance = (view.height * 0.5) / tan(angle)
end if
view.camera.position.Z = distance
view.hither = distance - 10
view.yon = distance + 10

(I don't have the article to quote it more precisely.)

Light.Direction/Position: (Fixed in RB 5.5) Lights can have a direction vector or a position vector. If one is NIL, the other one shouldn't be. That much is in the docs. What isn't in the docs is that once one of these is assigned, it somehow stays as part of the light, even if you subsequently assign NIL to it in an attempt to change the light's mode of operation. As you switch from position to direction, nearby objects will receive more and more illumination, until they are awash in glare. Feedback ID = cuhwsldf

Light.Brightness limit: (Fixed in Quesa 1.6d18) In Quesa 1.6d15-17, values over 100 for Brightness are ignored; they render as if the value was 100.

RemoveLight crash: This always happens in SceneBench, but I haven't been able to get it to happen in a simpler app. Basically, RB3DSpace.RemoveLight crashes, whether by object reference or by index. I first observed this in RB 4.5 and it's still happening through 5.5a6. I have no idea why or how to work around it. Feedback ID = gedhlkkl

FindObject/FindPoint: (Fixed in Quesa 1.6d18) In versions of Quesa through 1.6d17 (note: I haven't tested these exhaustively), you have to do a little processing to get FindPoint or FindObject to work properly in Windows. Posted on the Quesa mailing list:


  In order to get the pick to work correctly, you have to apply a scaling factor that depends on both the screen size and the view size and position. In RB code, it looks like this:

#If TargetWin32
  x2 = x * Screen(0).width / Rb3DSpace1.width - Rb3DSpace1.left
  y2 = y * Screen(0).height / Rb3DSpace1.height - Rb3DSpace1.top
#Else
  x2 = x // All works as expected on the Mac
  y2 = y
#Endif //

// (and then pass x2, y2 to the picking function)

So, for both X and Y, we have to multiply by the ratio of the screen size to view size, and then subtract off the top/left of the view (so that the coordinates are now relative to the view rather than the window). On the Mac none of that is necessary; we just pass in window coordinates and it works.


TRICKS
Shared objects: Multiple RB3DSpace views can share the same list of objects. It's this easy:

  RB3DSpace2.Objects = RB3DSpace1.Objects

Once you've done this, then every time you Update the spaces, they will look like two views onto the same world, because that's exactly what they are. Make sure to Update the spaces with as little intervening code as possible; it's very distracting to see a noticeable delay.

There is a gotcha with this trick: The two spaces cannot generally share the same Background object if they are to have separate cameras. That's because each space will attempt to update the position of the background (see the "Backgrounds" section), so if it's the same object, its position will always be out of sync with one of the cameras. The fix for this is simple: just Clone one background to create the other one.

Stereoscopic views can be created with shared objects; just set the cameras a little to one side of one another. The spaces can't be very large onscreen, because your eyes wouldn't be able to fuse them, but the effect is quite entertaining. You might be able to get away with a shared background in this case, since the cameras are close to one another and their orientation is always the same.

2-player games are another application for shared objects; two (or more) players can be at the same computer, using the same keyboard (or other input devices) each with his own 3D view of the action. The game Whiplash is an example of this. You'll certainly want separate Background objects in this case.


©2003-2004 Lars Jensen, except where specified. Please email us with errors or topic ideas.