Размер мира - 32x32x128 (решил не заморачиваться над чем-то большим). Данные о высотах хранятся в массиве 32x32x128 послойно. Массив заполняется значениями -1, далее если нужно узнать высоты слоев с координатой (x,y) делаем так: idx = x*32*32 + y*32; Далее hei[idx] - нижняя координата 1 слоя, hei[idx+1] - верхняя 1-го, потом 2 слой итд, пока не найдем -1.
На экране рисуются уже не столбцы от низа экрана, а отрезки, соответствующие высоте слоя. Вся картинка оказывается нарисованной как бы "лентами", параллельными экрану.
Самое сложное в методе (и поэтому я не уверен, что он единcтвенно верный) - обнаружение перекрытий на экране. Вспомните, в ландшафте мы сравнивали экранную координату sy c величиной из массива, корректируя массив по необходимости. Тут же приходится считать перекрытия отрезков, что несколько сложнее. К тому же быстрота подсчета перекрытий зависит от числа связных множеств (отрезков), объединение которых составляет множество точек, уже нарисованных на экране.
Тем не менее такая простая сцена рендерится быстрее, чем методом рисования back-to-front.
Вот код:
Открыть спойлер
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <SDL.h>
//#define DEBUG_PRINT(A) printf A
#define DEBUG_PRINT(A) ;
int bsize = 2;
int scw = 1280;
int sch = 1024;
int mapa = 32;
int mapb = 32;
int mapc = 128;
int min (int a, int b)
{
if (a<b) return a;
else return b;
}
int max (int a, int b)
{
if (a>b) return a;
else return b;
}
void filldata (int *hei, int *col)
{
int idx,startidx;
int x,y,z;
for (y=0; y<mapb; y++)
{
for (x=0; x<mapa; x++)
{
int filled = -1;
if ((x>10)&&(x<30)&&(y>10)&&(y<30)) filled = 1;
if (filled == 1)
{
startidx = y*mapa*mapb + x*mapa;
hei[startidx] = 4;
hei[startidx+1] = 25;
}
filled = -1;
int dist = (x-20)*(x-20)+(y-20)*(y-20);
if (dist<=81) filled = 1;
if (filled==1)
{
hei[startidx+2] = 40;
hei[startidx+3] = 41+x;
}
for (z=0; z<mapc; z++)
{
idx = y*mapa*mapb + x*mapa + z;
col[idx] = 3*sqrt(x*x+y*y+z*z);
}
}
}
}
void fillpalette (SDL_Surface *screen)
{
SDL_Color colors[256];
int i;
/* Fill colors with color information */
for(i=0;i<=256;i++)
{
colors[i].r=i;
colors[i].g=i/3;
colors[i].b=i/3;
}
SDL_SetPalette(screen, SDL_LOGPAL|SDL_PHYSPAL, colors, 0, 256);
}
void printdata (int *hei)
{
int x,y,z;
FILE *out = fopen ("testout.dat", "w");
for (y=0; y<mapb; y++)
{
for (x=0; x<mapa; x++)
{
for (z=0; z<mapc; z++)
{
int idx = y*mapa*mapb + x*mapa + z;
fprintf (out, "%i %i %i\n", x, y, hei[idx]);
}
}
}
fclose (out);
}
void blacken_screen (SDL_Surface *screen)
{
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = scw;
rect.h = sch;
SDL_FillRect (screen, &rect, 0);
}
void checkevents(double *ang, int *x, int *y, int *z, int *k)
{
SDL_Event event;
if (SDL_PollEvent(&event))
{
switch (event.type) {
case SDL_QUIT:
exit(0);
}
}
Uint8 *keystate = SDL_GetKeyState (NULL);
if (keystate[SDLK_LEFT]) *ang += 0.02;
if (keystate[SDLK_RIGHT]) *ang -= 0.02;
if (keystate[SDLK_UP]) *k -= 1;
if (keystate[SDLK_DOWN]) *k += 1;
if (keystate[SDLK_k]) *z -= 1;
if (keystate[SDLK_j]) *z += 1;
if (keystate[SDLK_a] || keystate[SDLK_d] || keystate[SDLK_w] || keystate[SDLK_s])
{
int xd, yd;
xd = 0;
yd = 0;
if (keystate[SDLK_a]) xd = -3;
if (keystate[SDLK_d]) xd = 3;
if (keystate[SDLK_w]) yd = 3;
if (keystate[SDLK_s]) yd = -3;
*x += yd*cos(*ang) + xd*sin(*ang);
*y += -yd*sin(*ang) + xd*cos(*ang);
}
}
int main ()
{
int hei[mapa*mapb*mapc-1];
int col[mapa*mapb*mapc-1];
memset (hei, -1, sizeof(int)*mapa*mapb*mapc);
filldata (hei, col);
printdata (hei);
if ( SDL_Init(SDL_INIT_VIDEO) < 0 )
{
fprintf(stderr, "Cannot initialize SDL: %s\n", SDL_GetError());
exit(1);
}
SDL_Surface *screen;
screen = SDL_SetVideoMode(scw, sch, 8, SDL_SWSURFACE|SDL_HWPALETTE);
if ( screen == NULL )
{// Если установить разрешение не удалось
fprintf(stderr, "Cannot set resolution %ix%i: %s\n", scw, sch, SDL_GetError());
exit(1);
}
fillpalette (screen);
int posx = 0;
int posy = 0;
int posz = 80;
double ang = 0;
int k = 1;
double dd = 1;
double de = dd * 128; // 128 is the max "distance"
int x,y,z;
double amax = M_PI/3.4; // FOV
double a;
int bin1 = mapb*mapb*mapb-mapa*mapa;
int bin2 = mapa*mapa-mapa;
int bin3 = mapc-1;
int sx;
int minsytop[scw-1];
int maxsybottom[scw-1];
int areas;
while (1)
{
SDL_LockSurface(screen);
blacken_screen (screen);
double sinang = sin(ang);
double cosang = cos(ang);
for (sx=0;sx<scw;sx++)
{
memset (minsytop, -1, sizeof(int)*scw);
memset (maxsybottom, -1, sizeof(int)*scw);
minsytop[0] = 0;
maxsybottom[0] = sch-1;
areas = 1;
double d;
a = -amax + 2*amax*sx/scw;
double tana = tan (a);
for (d=0;d<=de;d+=dd)
{
double y1 = d*/*cos(amax)**/tana;
double x1 = d;//*cos(amax);
x = posx + x1*cosang+y1*sinang;
y = posy - x1*sinang+y1*cosang;
z = posz - k*d;//*sin(amax);
x = abs(x);
y = abs(y);
int idx = ((y*mapa*mapb)&bin1) + ((x*mapa)&bin2);
int zi;
DEBUG_PRINT (("Inside d\n"));
for (zi=0; zi<mapc/2; zi++)
{
int idxstart = idx + zi*2;
int hstart = hei[idxstart];
int idxend = idx + zi*2 + 1;
int hend = hei[idxend];
if (hstart==-1) break;
DEBUG_PRINT (("gstart = %i, hend = %i\n", hstart, hend));
int sy = sch/2 + 150*(z-hstart)/(d+1);
int sy2 = sch/2 + 150*(z-hend)/(d+1);
int area;
for (area=0; area<sch; area++)
{
DEBUG_PRINT (("Area = %i\n", area));
int top = minsytop[area];
int bottom = maxsybottom[area];
if (top==-1) break;
int ovsy2 = max (top, sy2); // Overlapped one
int ovsy = min (bottom, sy);
DEBUG_PRINT (("sy = %i, sy2 = %i\n", sy, sy2));
DEBUG_PRINT (("bottom = %i, top = %i\n", bottom, top));
if (ovsy2<sch)
{
if (ovsy>=sch) ovsy = sch-1;
if (ovsy2<0) ovsy2 = 0;
int syi;
for (syi=ovsy; syi>=ovsy2; syi--)
{
*((Uint8*)screen->pixels+sx+scw*syi) = col[idx];
}
}
// Adjust minsytop and maxsybottom
if ((sy2>top)&&(sy<bottom)) //Split
{
maxsybottom[area] = sy2;
minsytop[area+1] = sy;
maxsybottom[area+1] = bottom;
areas++;
}
//if (top==ovsy2) minsytop[area] = sy; // Correct top
//if (bottom==ovsy) maxsybottom[area] = sy2; // Correct bottom
if ((sy2<top)&&(sy>top)&&(sy<bottom)) minsytop[area] = sy;
if ((sy>bottom)&&(sy2<bottom)&&(sy2>top)) maxsybottom[area] = sy2;
/*if ((sy2<top)&&(sy>bottom)) // Destroy area
{
area--;
}*/
}
DEBUG_PRINT (("Areas %i\n", areas));
}
}
}
SDL_UnlockSurface (screen);
SDL_Flip (screen);
checkevents(&ang, &posx, &posy, &posz, &k);
}
return atexit(SDL_Quit);
}