The way this works is that several cameras are all in the same position, but rotated horizontally. They are each rendered to a different sprite, which is positioned on a different location on the screen. You could expand this to make a spherical camera if you wanted to (of course that would square the number of cameras you would need for the same quality). One idea would be to use a spherical mesh as a template to compute your rotation angles. You'd have to make sure the images fit properly together.
Screenshot: 360 degrees horizontal, 45 degrees vertical, 32 cameras
The setup function call is in two forms:
void InitPanoramicCamera (int width, int height, double fovX, double fovY , int cameraQuantity);
void InitPanoramicCameraAlt(int width, int height, double fovY, double aspectRatio, int cameraQuantity);
As you can see, you can tell it how many cameras you wish it to use. The less you use, the more seams will be visible. All the cameras' images line up perfectly, but because DirectX does perspective rendering, we are having to approximate the cylinder much like as in a mesh. The more cameras you have, the smoother-looking your final output, but it does cost some performance. For example, having 36 cameras cuts my frame-rate down to about a third. You probably will not need that many unless you are doing a full 360 degrees or something like that.
Width and height are those of your output. You can enter in your screen dimensions.
Normally when you call the dbSetCameraFOV() command, it is the field of view of the Y (vertical) axis. Here you can set the field of view for the X and Y axes individually. So if you would like your height to be as though you had called dbSetCameraFOV(45), then set fovY = 45. You can set fovX to anything you would like (well probably 0 would not work
).
Or if you would like to just have a cylindrical camera for your normal scene instead of the flat perspective camera provided by DirectX, you can call the Alt function, with fovY and aspectRatio the same as you would pass to the regular db commands.
Here is the main initialization function:
void InitPanoramicCamera(int width, int height, double fovX, double fovY, int cameraQuantity)
{
int index, camera, image, sprite;
double aspectRatio;
PanoramicCameraQuantity = cameraQuantity;
PanoramicViewAngleOffset = fovX * 0.5;
// Compute the width, fovX, and aspectRatio for each camera.
width /= PanoramicCameraQuantity;
fovX /= PanoramicCameraQuantity;
aspectRatio = tan(fovX * M_PI / 360.0) / tan(fovY * M_PI / 360.0); // M_PI (pi) defined in math.h. #define _USE_MATH_DEFINES before the include.
// This is the cameras' rotation interval
PanoramicViewAngle = fovX;
// Create & Setup Cameras, Images, and Sprites
index = PanoramicCameraQuantity;
while (index--)
{
camera = CameraPanoramicFirst + index;
image = ImageCameraPanoramicFirst + index;
sprite = SpriteImageCameraPanoramicFirst + index;
dbMakeCamera (camera);
dbSetCameraFOV (camera, fovY);
dbSetCameraAspect(camera, aspectRatio);
dbSetCameraRange (camera, ViewMinRange, ViewMaxRange);
dbColorBackdrop (camera, 0);
dbBackdropOff();
dbSetCameraToImage(camera, image, width, height);
dbSprite(sprite, width * index, 0, image);
}
// Allows text to appear over the sprites
dbDrawSpritesFirst();
// Effectively disable the main camera
dbBackdropOff ();
dbSetCameraRange(1, 2);
dbPositionCamera(-3000, -3000, -3000);
dbYRotateCamera (180);
}
Here is the Alt function:
void InitPanoramicCameraAlt(int width, int height, double fovY, double aspectRatio, int cameraQuantity)
{
double fovX;
fovX = 360.0 / M_PI * atan(aspectRatio * tan(fovY * M_PI / 360.0));
InitPanoramicCamera(width, height, fovX, fovY, cameraQuantity);
}
And lastly, the Camera Update Function, to be called each frame, or each time the camera is to move:
// angleX lets you look toward the ceiling or the floor.
// angleY lets you look right or left.
void UpdatePanoramicCamera(double positionX, double positionY, double positionZ, double angleX, double angleY)
{
int index, camera;
index = PanoramicCameraQuantity;
while (index--)
{
camera = CameraPanoramicFirst + index;
dbPositionCamera (camera, positionX, positionY, positionZ);
// Note: If anyone wants to do the math to crunch these down, please post the results in a reply in this thread :)
dbRotateCamera (camera, 0, 0, 0);
dbRollCameraRight(camera, -angleX * dbSin(angleY));
dbPitchCameraUp (camera, angleX * dbCos(angleY));
dbTurnCameraRight(camera, angleY + PanoramicViewAngleOffset + PanoramicViewAngle * index);
}
}
I actually included these functions in a class, which I removed for this sharing. So the variables that begin with capital letters, treat them here as global variables:
int ImageCameraPanoramicFirst; // Set to the next available (unused) image number before calling InitPanoramicCamera()
int SpriteImageCameraPanoramicFirst; // Set to the next available (unused) sprite number before calling InitPanoramicCamera()
int CameraPanoramicFirst; // Set to the next available (unused) camera number before calling InitPanoramicCamera()
int PanoramicCameraQuantity; // Set by InitPanoramicCamera(). The number of cameras used.
double PanoramicViewAngle; // Set by InitPanoramicCamera(). The view angle of each camera.
double PanoramicViewAngleOffset; // Set by InitPanoramicCamera(). Used to keep the final image centered.
double ViewMinRange; // Set to Cameras' Minimum Range before calling InitPanoramicCamera()
double ViewMaxRange; // Set to Cameras' Maximum Range before calling InitPanoramicCamera()