I was thinking about this recently, how to go from a regular rotozoomer to a X rotated floormapper.
In a rotozoomer you just linearly interpolate with the same two fixed point values every pixel, which you recalculate only after each frame.
The X rotated zoomer, I had to devide those interpolators by z each line, so I recalculated them each line but for each pixel on a line it's again the same.
I am working on this cause I am trying to make this effect on CPC, figuring out how I can even precalculate stuff so that I don't have to do divisions per line. But on PC it's fine.
Here is my last code, SDL, C++
// main.h
#ifndef MAIN_H
#define MAIN_H
#define ScreenWidth 800
#define ScreenHeight 600
#define TextureWidth 256
#define TextureHeight 256
#define PI 3.14159265359f
#define FP_MUL 65536
#define FP_SHR 16
#endif
//main.cpp
#include <stdio.h>
#include <math.h>
#include <SDL.h>
#include "main.h"
#undef main
SDL_Surface *screen;
bool exw=false;
unsigned int texture[TextureWidth * TextureHeight];
float zFactor[ScreenHeight];
unsigned int time;
// ---- Demo Init ----
void initZfactor(float z0, float z1)
{
float dz = (z1 - z0) / ScreenHeight;
for (int y=0; y<ScreenHeight; y++)
{
if (z0 > 0)
zFactor[y] = z0;
else
zFactor[y] = 0;
z0 += dz;
}
}
void init()
{
int i = 0;
for (int y=0; y<TextureHeight; y++)
{
for (int x=0; x<TextureWidth; x++)
{
texture[i++] = x ^ y;
}
}
}
// ========================================
// ======== Effect subs start here ========
// ========================================
int xduz[ScreenHeight];
int xdvz[ScreenHeight];
int yduz[ScreenHeight];
int ydvz[ScreenHeight];
void effect(SDL_Surface *screen)
{
unsigned int *vram = (unsigned int*)screen->pixels;
float angle = ((time / 1000.0f) * (2.0f * PI)) * 0.1f;
float x0 = sin(angle) + 1.0f;
float x1 = cos(angle) + 1.0f;
initZfactor(x0, x1);
const int du = cos(angle) * FP_MUL;
const int dv = sin(angle) * FP_MUL;
int xdu = du;
int xdv = dv;
int ydu = dv;
int ydv = -du;
for (int y=0; y<ScreenHeight; y++)
{
if (zFactor[y]!=0)
{
xduz[y] = xdu / zFactor[y];
xdvz[y] = xdv / zFactor[y];
yduz[y] = ydu / zFactor[y];
ydvz[y] = ydv / zFactor[y];
int ddu = 0 - xduz[y] * (ScreenWidth / 2) - yduz[y] * (-ScreenHeight / 2 + y);
int ddv = 0 - xdvz[y] * (ScreenWidth / 2) - ydvz[y] * (-ScreenHeight / 2 + y);
int u = ddu;
int v = ddv;
for (int x=0; x<ScreenWidth; x++)
{
unsigned int c = texture[((u >> FP_SHR) & (TextureHeight - 1)) * TextureWidth + ((v >> FP_SHR) & (TextureWidth - 1))];
*vram++ = (c << 16) | (c << 8) | c;
u += xduz[y];
v += xdvz[y];
}
//ddu += (ydu / zFactor[y]);
//ddv += (ydv / zFactor[y]);
}
}
}
// ------------------------------
// --- Keyboard/Mouse control ---
// ------------------------------
void keyCommands()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
exw=true;
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym==SDLK_ESCAPE)
exw=true;
break;
}
}
}
// ---- Demo Main ----
void demo()
{
do{
time = SDL_GetTicks();
effect(screen);
SDL_Flip(screen);
keyCommands();
}while(exw==false);
}
int main(int argc, char* argv[])
{
screen = SDL_SetVideoMode(ScreenWidth, ScreenHeight, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
init();
demo();
return 0;
}