Merged-in lua 5.2.2.
[AROS-Contrib.git] / Demo / NewVox / newvox.c
blob52a06de1cdac2f3666d4a57066812d461c9faa52
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; if (k>255) k=255;
128 CMap[i+j]=k;
132 /***********************************************************************************/
134 int lasty[SCREENWIDTH], /* Last pixel drawn on a given column */
135 lastc[SCREENWIDTH]; /* Color of last pixel on a column */
138 Draw a "section" of the landscape; x0,y0 and x1,y1 and the xy coordinates
139 on the height field, hy is the viewpoint height, s is the scaling factor
140 for the distance. x0,y0,x1,y1 are 16.16 fixed point numbers and the
141 scaling factor is a 16.8 fixed point value.
144 /***********************************************************************************/
146 void line(int x0,int y0,int x1,int y1,int hy,int s)
148 int i,sx,sy;
150 /* Compute xy speed */
151 sx=(x1-x0)/SCREENWIDTH; sy=(y1-y0)/SCREENWIDTH;
152 for ( i=0; i<SCREENWIDTH; i++ )
154 int c,y,h,u0,v0,u1,v1,a,b,h0,h1,h2,h3;
156 /* Compute the xy coordinates; a and b will be the position inside the
157 single map cell (0..255).
159 u0=(x0>>16)&0xFF; a=(x0>>8)&255;
160 v0=((y0>>8)&0xFF00); b=(y0>>8)&255;
161 u1=(u0+1)&0xFF;
162 v1=(v0+256)&0xFF00;
164 /* Fetch the height at the four corners of the square the point is in */
165 h0=HMap[u0+v0]; h2=HMap[u0+v1];
166 h1=HMap[u1+v0]; h3=HMap[u1+v1];
168 /* Compute the height using bilinear interpolation */
169 h0=(h0<<8)+a*(h1-h0);
170 h2=(h2<<8)+a*(h3-h2);
171 h=((h0<<8)+b*(h2-h0))>>16;
173 /* Fetch the color at the four corners of the square the point is in */
174 h0=CMap[u0+v0]; h2=CMap[u0+v1];
175 h1=CMap[u1+v0]; h3=CMap[u1+v1];
177 /* Compute the color using bilinear interpolation (in 16.16) */
178 h0=(h0<<8)+a*(h1-h0);
179 h2=(h2<<8)+a*(h3-h2);
180 c=((h0<<8)+b*(h2-h0));
182 /* Compute screen height using the scaling factor */
183 y=(((h-hy)*s)>>11)+100;
185 /* Draw the column */
186 if ( y<(a=lasty[i]) )
188 unsigned char *b=Video+a*SCREENWIDTH+i;
189 int sc,cc;
192 if ( lastc[i]==-1 )
193 lastc[i]=c;
195 sc=(c-lastc[i])/(a-y);
196 cc=lastc[i];
198 if ( a>(SCREENHEIGHT-1) ) { b-=(a-(SCREENHEIGHT-1))*SCREENWIDTH; cc+=(a-(SCREENHEIGHT-1))*sc; a=SCREENHEIGHT-1; }
199 if ( y<0 ) y=0;
200 while ( y<a )
202 *b=cc>>18; cc+=sc;
203 b-=SCREENWIDTH; a--;
205 lasty[i]=y;
208 lastc[i]=c;
210 /* Advance to next xy position */
211 x0+=sx; y0+=sy;
215 /***********************************************************************************/
217 float FOV=3.141592654/4; /* half of the xy field of view */
219 /***********************************************************************************/
222 // Draw the view from the point x0,y0 (16.16) looking at angle a
224 void View(int x0,int y0,float aa)
226 int d;
227 int a,b,h,u0,v0,u1,v1,h0,h1,h2,h3;
229 #if 0
230 /* Clear offscreen buffer */
231 memset(Video,0,SCREENWIDTH*SCREENHEIGHT);
232 #else
233 /* stegerg */
235 for(b = 0; b < SCREENHEIGHT; b++)
237 memset(Video + b * SCREENWIDTH, 64 + ((256 - 64) * b / (SCREENHEIGHT-1)), SCREENWIDTH);
239 #endif
240 /* Initialize last-y and last-color arrays */
241 for ( d=0; d<SCREENWIDTH; d++ )
243 lasty[d]=SCREENHEIGHT;
244 lastc[d]=-1;
247 /* Compute viewpoint height value */
249 /* Compute the xy coordinates; a and b will be the position inside the
250 single map cell (0..255).
252 u0=(x0>>16)&0xFF; a=(x0>>8)&255;
253 v0=((y0>>8)&0xFF00); b=(y0>>8)&255;
254 u1=(u0+1)&0xFF;
255 v1=(v0+256)&0xFF00;
257 /* Fetch the height at the four corners of the square the point is in */
258 h0=HMap[u0+v0]; h2=HMap[u0+v1];
259 h1=HMap[u1+v0]; h3=HMap[u1+v1];
261 /* Compute the height using bilinear interpolation */
262 h0=(h0<<8)+a*(h1-h0);
263 h2=(h2<<8)+a*(h3-h2);
264 h=((h0<<8)+b*(h2-h0))>>16;
266 /* Draw the landscape from near to far without overdraw */
267 for ( d=0; d<100; d+=1+(d>>6) )
269 line(x0+d*65536*cos(aa-FOV),y0+d*65536*sin(aa-FOV),
270 x0+d*65536*cos(aa+FOV),y0+d*65536*sin(aa+FOV),
271 h-30,SCREENCY*256/(d+1));
274 if (truecolor)
276 WriteLUTPixelArray(Video,
279 SCREENWIDTH,
281 cgfx_coltab,
282 win->BorderLeft,
283 win->BorderTop,
284 SCREENWIDTH,
285 SCREENHEIGHT,
286 CTABFMT_XRGB8);
288 else if (mustremap)
290 LONG i;
291 UBYTE *src = Video;
292 UBYTE *dest = Video_Remapped;
294 for(i = 0; i < SCREENWIDTH * SCREENHEIGHT; i++)
296 *dest++ = remaptable[*src++];
298 WriteChunkyPixels(rp,
299 win->BorderLeft,
300 win->BorderTop,
301 win->BorderLeft + SCREENWIDTH - 1,
302 win->BorderTop + SCREENHEIGHT - 1,
303 Video_Remapped,
304 SCREENWIDTH);
307 else
309 WriteChunkyPixels(rp,
310 win->BorderLeft,
311 win->BorderTop,
312 win->BorderLeft + SCREENWIDTH - 1,
313 win->BorderTop + SCREENHEIGHT - 1,
314 Video,
315 SCREENWIDTH);
320 /***********************************************************************************/
322 void cleanup(char *msg)
324 if (msg)
326 printf("newvox: %s\n",msg);
329 if (win) CloseWindow(win);
331 if (remapped)
333 WORD i;
335 for(i = 0; i < 256; i++)
337 ReleasePen(scr->ViewPort.ColorMap, remaptable[i]);
341 if (scr)
343 if (wbscreen)
344 UnlockPubScreen(0, scr);
345 else
346 CloseScreen(scr);
349 if (CyberGfxBase) CloseLibrary(CyberGfxBase);
350 if (GfxBase) CloseLibrary((struct Library *)GfxBase);
351 if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
353 exit(0);
356 /***********************************************************************************/
358 #define ARG_TEMPLATE "STARTFLYSPEED=S/N/K,WINPOSX=X/N/K,WINPOSY=Y/N/K,FORCESCREEN=SCR/S,FORCEWINDOW=WIN/S"
359 #define ARG_SPEED 0
360 #define ARG_X 1
361 #define ARG_Y 2
362 #define ARG_SCR 3
363 #define ARG_WIN 4
364 #define NUM_ARGS 5
366 static IPTR args[NUM_ARGS];
368 static void getarguments(void)
370 struct RDArgs *myargs;
372 if ((myargs = ReadArgs(ARG_TEMPLATE, args, NULL)))
374 if (args[ARG_SCR])
375 forcescreen = TRUE;
376 else if (args[ARG_WIN])
377 forcewindow = TRUE;
379 if (args[ARG_X]) winx = *(IPTR *)args[ARG_X];
380 if (args[ARG_Y]) winy = *(IPTR *)args[ARG_Y];
382 if (args[ARG_SPEED]) startflyspeed = (float)(*(IPTR *)args[ARG_SPEED]);
384 FreeArgs(myargs);
388 /***********************************************************************************/
390 void openlibs(void)
392 if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39)))
394 cleanup("Can't open intuition.library V39!");
397 if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 39)))
399 cleanup("Can't open graphics.library V39!");
402 if (!(CyberGfxBase = OpenLibrary("cybergraphics.library",0)))
404 cleanup("Can't open cybergraphics.library!");
408 /***********************************************************************************/
410 void getvisual(void)
412 if (!(scr = LockPubScreen(NULL)))
414 cleanup("Can't lock pub screen!");
417 if (GetBitMapAttr(scr->RastPort.BitMap, BMA_DEPTH) <= 8)
419 if (!forcewindow)
421 wbscreen = FALSE;
423 else
425 mustremap = TRUE;
429 if (forcescreen) wbscreen = FALSE;
431 if (!wbscreen)
433 UnlockPubScreen(NULL, scr);
434 wbscreen = FALSE;
436 scr = OpenScreenTags(NULL, SA_Width , SCREENWIDTH ,
437 SA_Height , SCREENHEIGHT ,
438 SA_Depth , 8 ,
439 TAG_DONE);
440 if (!scr) cleanup("Can't open screen!");
443 truecolor = (GetBitMapAttr(scr->RastPort.BitMap, BMA_DEPTH) >= 15) ? TRUE : FALSE;
446 /***********************************************************************************/
448 void makewin(void)
450 struct TagItem winonwbtags[] =
452 {WA_DragBar , TRUE },
453 {WA_DepthGadget , TRUE },
454 {WA_CloseGadget , TRUE },
455 {WA_Title , (IPTR)"NewVox" },
456 {TAG_DONE }
459 struct TagItem winonscrtags[] =
461 {WA_Borderless, TRUE },
462 {TAG_DONE }
465 if (winx == -1) winx = (scr->Width - SCREENWIDTH - scr->WBorLeft - scr->WBorRight) / 2;
466 if (winy == -1) winy = (scr->Height - SCREENHEIGHT - scr->WBorTop - scr->WBorTop - scr->Font->ta_YSize - 1) / 2;
468 win = OpenWindowTags(NULL, WA_CustomScreen , (IPTR)scr,
469 WA_Left , winx,
470 WA_Top , winy,
471 WA_InnerWidth , SCREENWIDTH,
472 WA_InnerHeight , SCREENHEIGHT,
473 WA_AutoAdjust , TRUE,
474 WA_Activate , TRUE,
475 WA_IDCMP , IDCMP_CLOSEWINDOW |
476 IDCMP_RAWKEY,
477 TAG_MORE , wbscreen ? winonwbtags : winonscrtags);
480 if (!win) cleanup("Can't open window");
482 rp = win->RPort;
485 /***********************************************************************************/
487 #define KC_LEFT 0x4F
488 #define KC_RIGHT 0x4E
489 #define KC_UP 0x4C
490 #define KC_DOWN 0x4D
491 #define KC_ESC 0x45
493 /***********************************************************************************/
495 void getevents(void)
497 struct IntuiMessage *msg;
499 while ((msg = (struct IntuiMessage *)GetMsg(win->UserPort)))
501 switch(msg->Class)
503 case IDCMP_CLOSEWINDOW:
504 Keys[KC_ESC] = 1;
505 break;
507 case IDCMP_RAWKEY:
509 WORD code = msg->Code & ~IECODE_UP_PREFIX;
511 Keys[code] = (code == msg->Code) ? 1 : 0;
514 break;
517 ReplyMsg((struct Message *)msg);
522 /***********************************************************************************/
524 int main(int argc, char *argv[])
526 int done;
527 int i;
528 float ss,sa,a,s;
529 int x0,y0;
531 getarguments();
532 openlibs();
533 getvisual();
535 /* Set up the first 64 colors to a grayscale */
536 for ( i=0; i<64; i++ )
538 ULONG red, green, blue;
540 /* red = green = blue = i * 4; */
541 green = i * 4; red = blue = 10;
543 cgfx_coltab[i] = (red << 16) + (green << 8) + blue;
546 /* stegerg: add a sky */
547 for (i = 0; i < (256 - 64); i++)
549 ULONG red, green, blue;
551 blue = 0xFF;
552 red = green = 255 - (i * 255 / (256 - 64 - 1));
554 cgfx_coltab[64 + i] = (red << 16) + (green << 8) + blue;
557 if (!truecolor)
559 WORD i;
561 for(i = 0; i < 256; i++)
563 ULONG r = (cgfx_coltab[i] >> 16) & 0xFF;
564 ULONG g = (cgfx_coltab[i] >> 8) & 0xFF;
565 ULONG b = cgfx_coltab[i] & 0xFF;
567 if (mustremap)
569 ULONG red = r * 0x01010101;
570 ULONG green = g * 0x01010101;
571 ULONG blue = b * 0x01010101;
573 remaptable[i] = ObtainBestPen(scr->ViewPort.ColorMap,
574 red,
575 green,
576 blue,
577 OBP_Precision, PRECISION_IMAGE,
578 OBP_FailIfBad, FALSE,
579 TAG_DONE);
580 remapped = TRUE;
582 else
585 ULONG red = r * 0x01010101;
586 ULONG green = g * 0x01010101;
587 ULONG blue = b * 0x01010101;
589 SetRGB32(&scr->ViewPort, i, red, green, blue);
594 /* Compute the height map */
595 ComputeMap();
597 makewin();
599 /* Main loop
601 a = angle
602 x0,y0 = current position
603 s = speed constant
604 ss = current forward/backward speed
605 sa = angular speed
607 done=0;
608 a=0; x0=y0=0;
609 s=1024; /*s=4096;*/
610 ss=startflyspeed; sa=0;
611 while(!done)
613 /* Draw the frame */
614 View(x0,y0,a);
616 /* Update position/angle */
617 x0+=ss*cos(a); y0+=ss*sin(a);
618 a+=sa;
620 /* Slowly reset the angle to 0 */
621 if ( sa != 0 )
623 if ( sa < 0 )
624 sa += 0.001;
625 else
626 sa -= 0.001;
629 /* User input */
631 getevents();
633 if (Keys[KC_ESC]) {
634 done = 1;
636 if (Keys[KC_UP]) {
637 ss+=s;
639 if (Keys[KC_DOWN]) {
640 ss-=s;
642 if (Keys[KC_RIGHT]) {
643 sa+=0.003;
645 if (Keys[KC_LEFT]) {
646 sa-=0.003;
649 WaitTOF();
652 cleanup(0);
653 return 0;
656 /***********************************************************************************/