I watched videos of games using mode 7 like Outrun or Space Harrier and I want to make a game which imitates this classic graphic. I was inspired by Notch's Prelude Of The Chambered too.
After experimenting with pseudo 3D solutions from here StackOverflow and reading about 3D computer graphic I tried to make my own Mode 7 implementation with 3D affine transformations. (I've read here on stackoverflow too that using matrices and transformations is better than use pseudo 3d technics.)
A came up with this solution, but it doesn't work perfectly:
void mode7() {
// width and height of texture
int texW = 1024, texH = 1024;
// ... of screen
int screenW = 800, screenH = 800;
// precalculated values
int scrWH = (int) (screenW * screenH);
int imgWH = (int) (texW * texH);
// center of perspective projection -- the point I look at
int toCenterOffsetX = screenW >> 1;
int toCenterOffsetY = screenH >> 1;
// depth
float z = 1f;
int textureIndex;
int screenIndex;
double tempX, tempY, tempZ;
// screen coordinates
double screenX, screenY, screenZ;
// after perspective division
int perX, perY;
// scale
//float scale = 2f;
for(int pixelY = toCenterOffsetY; pixelY < screenH; pixelY++) {
for(int pixelX = 0; pixelX < screenW; pixelX++) {
// z
screenZ = z;
// translating to center
screenY = pixelY - (texH >> 1);
screenX = pixelX - (texW >> 1);
// transformation
// rotating -- glitch like graphic somehow
//tempX = screenX * Math.cos(rotY*RAD) - screenY * Math.sin(rotY*RAD);
//tempY = screenX * Math.sin(rotY*RAD) + screenY * Math.cos(rotY*RAD);
//screenX = tempX;
//screenY = tempY;
// 3D Rx
// | x |
// | y |
// | z |
// ----------------------------------------------
// | 1 0 0 | 1 * x + 0 * y + 0 * z = x'
// | 0 cos -sin | 0 * x + cos * y - sin * z = y'
// | 0 sin cos | 0 * x + sin * y + cos * z = z'
tempX = screenX * 1 + 0 * screenY + 0 * screenZ;
tempY = screenX * 0 + screenY * Math.cos(rotX*RAD) - screenZ * Math.sin(rotX*RAD);
tempZ = screenX * 0 + screenY * Math.sin(rotX*RAD) + screenZ * Math.cos(rotX*RAD);
// rotating
tempX = screenX * Math.cos(rotY*RAD) - screenY * Math.sin(rotY*RAD); // rotates Z instead of Y
tempY = screenX * Math.sin(rotY*RAD) + screenY * Math.cos(rotY*RAD);
screenX = tempX;
screenY = tempY;
// translating back
screenY = (float) tempY; // + (texH >> 1);
screenX = (float) tempX; // + (texW >> 1);
// perspective divison and set horizon
perX = (int) (screenX / tempZ) + toCenterOffsetX;
perY = (int) (screenY / tempZ) + toCenterOffsetY;
// index
//pixelX *= scale; // not working, blank blue screen
//pixelY *= scale;
// removing gaps with nearest neighbour like scaling
//pixelX *= texW / (texW / tempZ);
//pixelY *= texH / (texH / tempZ);
// simplified
//pixelX *= tempZ;
//pixelY *= tempZ; // not working...even one frame won't be drawn, just white screen
textureIndex = (int) ( pixelY * texW + pixelX );
screenIndex = (int) ( perY * screenW + perX );
// write pixels
if( (textureIndex >= 0 && textureIndex < imgWH) &&
(screenIndex >= 0 && screenIndex < scrWH)
) {
output[screenIndex] = input[textureIndex];
}
}
}
}
If I'm right, then I should use something similar to the nearest neighbour scaling to remove the gaps.
Games like Outrun doesn't have a huge map eg. 3000 x 3000 (I think) but it'll take minutes to drive through the picture. I first used a 64 * 64 texture for this but ther results were almost exactly the same. How could I make the map scaled to the proper size?
I leave the optimizations for later.
The input / output arrays are due to the DataBufferInt and getRGB(x, y, w, h... stuff I used to avoid setRGB and other slow methods.
I don't ask for exact, full solutions, I ask for some hints, guides, or mathematical explanations.
I'd like to learn enough so I can create a simple 3D game like Notch did. I know he's already had years of experience of game programming but I hope it's something I can learn without 10+ year of game programming.
Here are the results and the used texture:
(Sorry for my bad English.)