correction - use SCREENWIDTH/SCREENHEIGHT
[AROS-Contrib.git] / Demo / NewVox / newvox.c
blob92ccb32276e98ca6235672340d76e25dee84c003
1 /*
2 Landscape rendering
4 Ok.. i know that voxel is something else... but a lot of people is using
5 the name "voxel" to mean this kind of rendering tecnique.
6 I wrote this to explain the basic idea behind the rendering of newvox4;
7 newvox4 is very badly written (it's named 4 because is the fourth of
8 a sequel of experiments) and is coded in pascal + asm.
9 Since i got a few request of an explanation i decided to write the kernel
10 of the rendering in C hoping that this will be easier to understand.
11 This implements only the base landscape (no sky or floating ball) and
12 with keyboard only support but i think you can get the idea of how I
13 implemented those other things.
15 I'm releasing this code to the public domain for free... and as it's
16 probably really obvious there's no warranty of any kind on it.
17 You can do whatever you want with this source; however a credit in any
18 program that uses part of this code would be really appreciated :)
20 Any comment is welcome :)
22 Andrea "6502" Griffini, programmer
23 agriff@ix.netcom.com
24 http://vv.val.net/~agriffini
26 #include <dos/dos.h>
27 #include <intuition/intuition.h>
28 #include <graphics/gfx.h>
29 #include <cybergraphx/cybergraphics.h>
30 #include <proto/exec.h>
31 #include <proto/dos.h>
32 #include <proto/graphics.h>
33 #include <proto/cybergraphics.h>
34 #include <proto/intuition.h>
36 #include <math.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
41 #define SCREENWIDTH 320
42 #define SCREENHEIGHT 200
43 #define SCREENCY (SCREENHEIGHT / 2)
45 /***********************************************************************************/
47 struct IntuitionBase *IntuitionBase;
48 struct GfxBase *GfxBase;
49 struct Library *CyberGfxBase;
50 struct Screen *scr;
51 struct Window *win;
52 struct RastPort *rp;
54 ULONG cgfx_coltab[256];
55 UBYTE remaptable[256];
56 BYTE Keys[256];
57 WORD winx = -1, winy = -1;
58 BOOL forcescreen, forcewindow;
59 BOOL mustremap, truecolor, remapped, wbscreen = TRUE;
60 float startflyspeed = 0;
62 /***********************************************************************************/
64 typedef unsigned char byte;
66 byte HMap[256*256]; /* Height field */
67 byte CMap[256*256]; /* Color map */
68 byte Video[SCREENWIDTH*SCREENHEIGHT]; /* Off-screen buffer */
69 byte Video_Remapped[SCREENWIDTH*SCREENHEIGHT];
71 /***********************************************************************************/
73 /* Reduces a value to 0..255 (used in height field computation) */
74 int Clamp(int x)
76 return (x<0 ? 0 : (x>255 ? 255 : x));
79 /***********************************************************************************/
81 /* Heightfield and colormap computation */
82 void ComputeMap(void)
84 int p,i,j,k,k2,p2;
86 /* Start from a plasma clouds fractal */
87 HMap[0]=128;
88 for ( p=256; p>1; p=p2 )
90 p2=p>>1;
91 k=p*8+20; k2=k>>1;
92 for ( i=0; i<256; i+=p )
94 for ( j=0; j<256; j+=p )
96 int a,b,c,d;
98 a=HMap[(i<<8)+j];
99 b=HMap[(((i+p)&255)<<8)+j];
100 c=HMap[(i<<8)+((j+p)&255)];
101 d=HMap[(((i+p)&255)<<8)+((j+p)&255)];
103 HMap[(i<<8)+((j+p2)&255)]=
104 Clamp(((a+c)>>1)+(rand()%k-k2));
105 HMap[(((i+p2)&255)<<8)+((j+p2)&255)]=
106 Clamp(((a+b+c+d)>>2)+(rand()%k-k2));
107 HMap[(((i+p2)&255)<<8)+j]=
108 Clamp(((a+b)>>1)+(rand()%k-k2));
113 /* Smoothing */
114 for ( k=0; k<3; k++ )
115 for ( i=0; i<256*256; i+=256 )
116 for ( j=0; j<256; j++ )
118 HMap[i+j]=(HMap[((i+256)&0xFF00)+j]+HMap[i+((j+1)&0xFF)]+
119 HMap[((i-256)&0xFF00)+j]+HMap[i+((j-1)&0xFF)])>>2;
122 /* Color computation (derivative of the height field) */
123 for ( i=0; i<256*256; i+=256 )
124 for ( j=0; j<256; j++ )
126 k=128+(HMap[((i+256)&0xFF00)+((j+1)&255)]-HMap[i+j])*4;
127 if ( k<0 ) k=0;
128 if (k>255) k=255;
129 CMap[i+j]=k;
133 /***********************************************************************************/
135 int lasty[SCREENWIDTH], /* Last pixel drawn on a given column */
136 lastc[SCREENWIDTH]; /* Color of last pixel on a column */
139 Draw a "section" of the landscape; x0,y0 and x1,y1 and the xy coordinates
140 on the height field, hy is the viewpoint height, s is the scaling factor
141 for the distance. x0,y0,x1,y1 are 16.16 fixed point numbers and the
142 scaling factor is a 16.8 fixed point value.
145 /***********************************************************************************/
147 void line(int x0,int y0,int x1,int y1,int hy,int s)
149 int i,sx,sy;
151 /* Compute xy speed */
152 sx=(x1-x0)/SCREENWIDTH; sy=(y1-y0)/SCREENWIDTH;
153 for ( i=0; i<SCREENWIDTH; i++ )
155 int c,y,h,u0,v0,u1,v1,a,b,h0,h1,h2,h3;
157 /* Compute the xy coordinates; a and b will be the position inside the
158 single map cell (0..255).
160 u0=(x0>>16)&0xFF; a=(x0>>8)&255;
161 v0=((y0>>8)&0xFF00); b=(y0>>8)&255;
162 u1=(u0+1)&0xFF;
163 v1=(v0+256)&0xFF00;
165 /* Fetch the height at the four corners of the square the point is in */
166 h0=HMap[u0+v0]; h2=HMap[u0+v1];
167 h1=HMap[u1+v0]; h3=HMap[u1+v1];
169 /* Compute the height using bilinear interpolation */
170 h0=(h0<<8)+a*(h1-h0);
171 h2=(h2<<8)+a*(h3-h2);
172 h=((h0<<8)+b*(h2-h0))>>16;
174 /* Fetch the color at the four corners of the square the point is in */
175 h0=CMap[u0+v0]; h2=CMap[u0+v1];
176 h1=CMap[u1+v0]; h3=CMap[u1+v1];
178 /* Compute the color using bilinear interpolation (in 16.16) */
179 h0=(h0<<8)+a*(h1-h0);
180 h2=(h2<<8)+a*(h3-h2);
181 c=((h0<<8)+b*(h2-h0));
183 /* Compute screen height using the scaling factor */
184 y=(((h-hy)*s)>>11)+100;
186 /* Draw the column */
187 if ( y<(a=lasty[i]) )
189 unsigned char *b=Video+a*SCREENWIDTH+i;
190 int sc,cc;
193 if ( lastc[i]==-1 )
194 lastc[i]=c;
196 sc=(c-lastc[i])/(a-y);
197 cc=lastc[i];
199 if ( a>(SCREENHEIGHT-1) ) { b-=(a-(SCREENHEIGHT-1))*SCREENWIDTH; cc+=(a-(SCREENHEIGHT-1))*sc; a=SCREENHEIGHT-1; }
200 if ( y<0 ) y=0;
201 while ( y<a )
203 *b=cc>>18; cc+=sc;
204 b-=SCREENWIDTH; a--;
206 lasty[i]=y;
209 lastc[i]=c;
211 /* Advance to next xy position */
212 x0+=sx; y0+=sy;
216 /***********************************************************************************/
218 float FOV=3.141592654/4; /* half of the xy field of view */
220 /***********************************************************************************/
223 // Draw the view from the point x0,y0 (16.16) looking at angle a
225 void View(int x0,int y0,float aa)
227 int d;
228 int a,b,h,u0,v0,u1,v1,h0,h1,h2,h3;
230 #if 0
231 /* Clear offscreen buffer */
232 memset(Video,0,SCREENWIDTH*SCREENHEIGHT);
233 #else
234 /* stegerg */
236 for(b = 0; b < SCREENHEIGHT; b++)
238 memset(Video + b * SCREENWIDTH, 64 + ((256 - 64) * b / (SCREENHEIGHT-1)), SCREENWIDTH);
240 #endif
241 /* Initialize last-y and last-color arrays */
242 for ( d=0; d<SCREENWIDTH; d++ )
244 lasty[d]=SCREENHEIGHT;
245 lastc[d]=-1;
248 /* Compute viewpoint height value */
250 /* Compute the xy coordinates; a and b will be the position inside the
251 single map cell (0..255).
253 u0=(x0>>16)&0xFF; a=(x0>>8)&255;
254 v0=((y0>>8)&0xFF00); b=(y0>>8)&255;
255 u1=(u0+1)&0xFF;
256 v1=(v0+256)&0xFF00;
258 /* Fetch the height at the four corners of the square the point is in */
259 h0=HMap[u0+v0]; h2=HMap[u0+v1];
260 h1=HMap[u1+v0]; h3=HMap[u1+v1];
262 /* Compute the height using bilinear interpolation */
263 h0=(h0<<8)+a*(h1-h0);
264 h2=(h2<<8)+a*(h3-h2);
265 h=((h0<<8)+b*(h2-h0))>>16;
267 /* Draw the landscape from near to far without overdraw */
268 for ( d=0; d<100; d+=1+(d>>6) )
270 line(x0+d*65536*cos(aa-FOV),y0+d*65536*sin(aa-FOV),
271 x0+d*65536*cos(aa+FOV),y0+d*65536*sin(aa+FOV),
272 h-30,SCREENCY*256/(d+1));
275 if (truecolor)
277 WriteLUTPixelArray(Video,
280 SCREENWIDTH,
282 cgfx_coltab,
283 win->BorderLeft,
284 win->BorderTop,
285 SCREENWIDTH,
286 SCREENHEIGHT,
287 CTABFMT_XRGB8);
289 else if (mustremap)
291 LONG i;
292 UBYTE *src = Video;
293 UBYTE *dest = Video_Remapped;
295 for(i = 0; i < SCREENWIDTH * SCREENHEIGHT; i++)
297 *dest++ = remaptable[*src++];
299 WriteChunkyPixels(rp,
300 win->BorderLeft,
301 win->BorderTop,
302 win->BorderLeft + SCREENWIDTH - 1,
303 win->BorderTop + SCREENHEIGHT - 1,
304 Video_Remapped,
305 SCREENWIDTH);
308 else
310 WriteChunkyPixels(rp,
311 win->BorderLeft,
312 win->BorderTop,
313 win->BorderLeft + SCREENWIDTH - 1,
314 win->BorderTop + SCREENHEIGHT - 1,
315 Video,
316 SCREENWIDTH);
321 /***********************************************************************************/
323 void cleanup(char *msg)
325 if (msg)
327 printf("newvox: %s\n",msg);
330 if (win) CloseWindow(win);
332 if (remapped)
334 WORD i;
336 for(i = 0; i < 256; i++)
338 ReleasePen(scr->ViewPort.ColorMap, remaptable[i]);
342 if (scr)
344 if (wbscreen)
345 UnlockPubScreen(0, scr);
346 else
347 CloseScreen(scr);
350 if (CyberGfxBase) CloseLibrary(CyberGfxBase);
351 if (GfxBase) CloseLibrary((struct Library *)GfxBase);
352 if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
354 exit(0);
357 /***********************************************************************************/
359 #define ARG_TEMPLATE "STARTFLYSPEED=S/N/K,WINPOSX=X/N/K,WINPOSY=Y/N/K,FORCESCREEN=SCR/S,FORCEWINDOW=WIN/S"
360 #define ARG_SPEED 0
361 #define ARG_X 1
362 #define ARG_Y 2
363 #define ARG_SCR 3
364 #define ARG_WIN 4
365 #define NUM_ARGS 5
367 static IPTR args[NUM_ARGS];
369 static void getarguments(void)
371 struct RDArgs *myargs;
373 if ((myargs = ReadArgs(ARG_TEMPLATE, args, NULL)))
375 if (args[ARG_SCR])
376 forcescreen = TRUE;
377 else if (args[ARG_WIN])
378 forcewindow = TRUE;
380 if (args[ARG_X]) winx = *(IPTR *)args[ARG_X];
381 if (args[ARG_Y]) winy = *(IPTR *)args[ARG_Y];
383 if (args[ARG_SPEED]) startflyspeed = (float)(*(IPTR *)args[ARG_SPEED]);
385 FreeArgs(myargs);
389 /***********************************************************************************/
391 void openlibs(void)
393 if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39)))
395 cleanup("Can't open intuition.library V39!");
398 if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 39)))
400 cleanup("Can't open graphics.library V39!");
403 if (!(CyberGfxBase = OpenLibrary("cybergraphics.library",0)))
405 cleanup("Can't open cybergraphics.library!");
409 /***********************************************************************************/
411 static void getvisual(void)
413 if (forcescreen)
414 wbscreen = FALSE;
416 if (!wbscreen)
418 scr = OpenScreenTags(NULL, SA_Width , SCREENWIDTH ,
419 SA_Height , SCREENHEIGHT ,
420 SA_Depth , 8 ,
421 TAG_DONE);
422 if (!scr) cleanup("Failed to open specified screen!");
424 else if (!(scr = LockPubScreen(NULL)))
426 cleanup("Failed to lock pub screen (workbench)!");
429 truecolor = (GetBitMapAttr(scr->RastPort.BitMap, BMA_DEPTH) >= 15) ? TRUE : FALSE;
431 if ((!truecolor) && (wbscreen))
432 mustremap = TRUE;
435 /***********************************************************************************/
437 void makewin(void)
439 struct TagItem winonwbtags[] =
441 {WA_DragBar , TRUE },
442 {WA_DepthGadget , TRUE },
443 {WA_CloseGadget , TRUE },
444 {WA_Title , (IPTR)"NewVox" },
445 {TAG_DONE }
448 struct TagItem winonscrtags[] =
450 {WA_Borderless, TRUE },
451 {TAG_DONE }
454 if (winx == -1) winx = (scr->Width - SCREENWIDTH - scr->WBorLeft - scr->WBorRight) / 2;
455 if (winy == -1) winy = (scr->Height - SCREENHEIGHT - scr->WBorTop - scr->WBorTop - scr->Font->ta_YSize - 1) / 2;
457 win = OpenWindowTags(NULL, WA_CustomScreen , (IPTR)scr,
458 WA_Left , winx,
459 WA_Top , winy,
460 WA_InnerWidth , SCREENWIDTH,
461 WA_InnerHeight , SCREENHEIGHT,
462 WA_AutoAdjust , TRUE,
463 WA_Activate , TRUE,
464 WA_IDCMP , IDCMP_CLOSEWINDOW |
465 IDCMP_RAWKEY,
466 TAG_MORE , wbscreen ? winonwbtags : winonscrtags);
469 if (!win) cleanup("Can't open window");
471 rp = win->RPort;
474 /***********************************************************************************/
476 #define KC_LEFT 0x4F
477 #define KC_RIGHT 0x4E
478 #define KC_UP 0x4C
479 #define KC_DOWN 0x4D
480 #define KC_ESC 0x45
482 /***********************************************************************************/
484 void getevents(void)
486 struct IntuiMessage *msg;
488 while ((msg = (struct IntuiMessage *)GetMsg(win->UserPort)))
490 switch(msg->Class)
492 case IDCMP_CLOSEWINDOW:
493 Keys[KC_ESC] = 1;
494 break;
496 case IDCMP_RAWKEY:
498 WORD code = msg->Code & ~IECODE_UP_PREFIX;
500 Keys[code] = (code == msg->Code) ? 1 : 0;
503 break;
506 ReplyMsg((struct Message *)msg);
511 /***********************************************************************************/
513 int main(int argc, char *argv[])
515 int done;
516 int i;
517 float ss,sa,a,s;
518 int x0,y0;
520 getarguments();
521 openlibs();
522 getvisual();
524 /* Set up the first 64 colors to a grayscale */
525 for ( i=0; i<64; i++ )
527 ULONG red, green, blue;
529 /* red = green = blue = i * 4; */
530 green = i * 4; red = blue = 10;
532 cgfx_coltab[i] = (red << 16) + (green << 8) + blue;
535 /* stegerg: add a sky */
536 for (i = 0; i < (256 - 64); i++)
538 ULONG red, green, blue;
540 blue = 0xFF;
541 red = green = 255 - (i * 255 / (256 - 64 - 1));
543 cgfx_coltab[64 + i] = (red << 16) + (green << 8) + blue;
546 if (!truecolor)
548 WORD i;
550 for(i = 0; i < 256; i++)
552 ULONG r = (cgfx_coltab[i] >> 16) & 0xFF;
553 ULONG g = (cgfx_coltab[i] >> 8) & 0xFF;
554 ULONG b = cgfx_coltab[i] & 0xFF;
556 if (mustremap)
558 ULONG red = r * 0x01010101;
559 ULONG green = g * 0x01010101;
560 ULONG blue = b * 0x01010101;
562 remaptable[i] = ObtainBestPen(scr->ViewPort.ColorMap,
563 red,
564 green,
565 blue,
566 OBP_Precision, PRECISION_IMAGE,
567 OBP_FailIfBad, FALSE,
568 TAG_DONE);
569 remapped = TRUE;
571 else
574 ULONG red = r * 0x01010101;
575 ULONG green = g * 0x01010101;
576 ULONG blue = b * 0x01010101;
578 SetRGB32(&scr->ViewPort, i, red, green, blue);
583 /* Compute the height map */
584 ComputeMap();
586 makewin();
588 /* Main loop
590 a = angle
591 x0,y0 = current position
592 s = speed constant
593 ss = current forward/backward speed
594 sa = angular speed
596 done=0;
597 a=0; x0=y0=0;
598 s=1024; /*s=4096;*/
599 ss=startflyspeed; sa=0;
600 while(!done)
602 /* Draw the frame */
603 View(x0,y0,a);
605 /* Update position/angle */
606 x0+=ss*cos(a); y0+=ss*sin(a);
607 a+=sa;
609 /* Slowly reset the angle to 0 */
610 if ( sa != 0 )
612 if ( sa < 0 )
613 sa += 0.001;
614 else
615 sa -= 0.001;
618 /* User input */
620 getevents();
622 if (Keys[KC_ESC]) {
623 done = 1;
625 if (Keys[KC_UP]) {
626 ss+=s;
628 if (Keys[KC_DOWN]) {
629 ss-=s;
631 if (Keys[KC_RIGHT]) {
632 sa+=0.003;
634 if (Keys[KC_LEFT]) {
635 sa-=0.003;
638 WaitTOF();
641 cleanup(0);
642 return 0;
645 /***********************************************************************************/