'=========================================================================== ' Subject: AN OPTIMIZED BUMP MAP Date: 10-04-97 (17:46) ' Author: Seppo Vuolteenaho Code: PB ' Origin: seppo@geocities.com Packet: GRAPHICS.ABC '=========================================================================== '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ' ' A bump map. ' You should like it. ' ' ' ' Original code: Danny Beardsley (QuickBasic version) ' dsb@cyberdude.com ' http://www.dnai.com/~beards ' It was called "ANIBUMP.BAS" in some ABC packet. ' And it was very nice indeed... ' ' Optimized and modified by: ' Seppo Vuolteenaho (aka Abacus of coi) ' seppo@geocities.com ' http://hem.passagen.se/abacus ' http://hem.passagen.se/coi ' Called "ABA_BUMP.BAS" ' And I think it is pretty good! ' More: ' Tab size in this source is 4. ' Danny and I worked together ' to bring out new and faster ways for ' this bump map. He helped me to solve ' some problems with the the asm. ' ' ' Wishlist: ' - Even faster routine. (Yes, of course it is possible) ' - Lots of snow this winter. ' - More colors! Not only one shade, but eight (or more) ' Blue, green, red, yellow, grey, and.. well, anything! ' It is quite simple, but I haven't bothered to do it yet. ' - Some credits from coder to another (as I am giving Danny) if ' you use this anywhere, or get inspired to do anything similiar, ' or just because you like this code and wish you could do ' something like this :) ' - That you e-mail me (and Danny too) newer versions you make! ' (After all, I gave you this code.) ' ' - ­Let inphormation be phree! '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= $DIM ALL $CPU 80386 $OPTIMIZE SPEED ' No error handling needed, since ' it shouldn't do anything weird. ' (The code was created with error all on ' ON, but was later changed, for more speed) $ERROR ALL ON $LIB ALL OFF $STATIC $STRING 1 $STACK 3000 '$COMPILE EXE '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ' My main idea for speedup is to decrease the ' resolution to 160x200 pixels. ' This constant here will compile the code ' in two different ways. For slower machines ' let it be = 1. If it runs too fast (huh, you ' can never get too fast!) then set it to = 0 ' and you'll get higher quality, but slower. %SkipOnePixel = 0 ' Special effect! Try to set it to 1, and ' watch everything... disappear. %SinkDown = 0 ' He he, Danny doesn't know that I am ' naming this variable like this... :) ' He had a small trick, that would ' increase the framerate considerably. ' Try it out! (I admit, really smart, ' but it leaves some ugly trails behind) %DannyCheater = 0 ' You'll get the fastest bump map if you ' enable the both constants, %SkipOnePixel ' and %DannyCheater '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= DEFINT A-Z DECLARE SUB Bumpize () DECLARE SUB FadeOutPic () DECLARE SUB PreCalculate () DECLARE SUB LoadLogo (FileName AS STRING) DECLARE SUB SetMCGA () DECLARE SUB SetPalette () DECLARE SUB SetText () DECLARE SUB Soften (BYVAL nTimes) DECLARE SUB WaitRetrace () '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= DIM x AS INTEGER DIM y AS INTEGER ' A virtual screen to store the graphics ' we are to draw in the bump map. DIM STATIC pic(31999) AS SHARED INTEGER ' The environment map, 127*127 DIM STATIC EnvMap(16129) AS SHARED BYTE ' we use bytes! ' Danny insists on keeping integers. ' Some global variables. DIM PicSeg AS SHARED WORD DIM EnvMapSeg AS SHARED WORD '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ' Do some important light calculations, and set ' some global variables PreCalculate ' Load a 64000 byte raw data picture. ' It should be a general image loader that ' would automatically detect if it as .GIF, ' .PCX or something. LoadLogo "f:\big_coi.raw" ' Add some bumps to the image. Bumpize ' Soften, so it'll look really nice. ' Uncomment too see how garbled it looks ' without this. (Or try a lower value as parameter) Soften 3 ' Finally set the graphics mode SetMCGA ' And a nice palette for our bumps SetPalette DIM LightPosX AS INTEGER, LightPosY AS INTEGER DIM ym AS INTEGER, lx AS INTEGER DIM yd AS INTEGER, ly AS INTEGER DIM nx AS INTEGER, ny AS INTEGER DIM i AS INTEGER ' ' Main loop ' DO ' FOR i=1 TO 360*3 ! Mov i, 1 ForLoopWith_i: WaitRetrace ' The path for the traversing light is a circle. ' LightPosX = COS( 7*i*3.1416/180 )*77+160 LightPosY = SIN( 11*i*3.1416/180 )*40+100 ' Commentned lines tell what we are ' doing in our asm code: ym = 0 ly = -LightPosY 'FOR y = 0 TO 199 ! Mov SI, 199 ForLoopSI: lx = -LightPosX yd = ym $IF %SkipOnePixel ' Only render halv of the pixels horizontally. ' Better for slow machines. ! Mov DI, 159 $ELSE ' Every pixel is rendered. Should be OK ' with faster machines. Gives better quality. ' (Personally I like the other onne a bit more) ! Mov DI, 319 $ENDIF ' Our inner loop This is the place that should ' be optimized. ForLoopDI: ' Read a pixel from the picture and ' calculate with one pixel below and ' one to the right. 'DEF SEG=picSeg 'c=PEEK(yd)-125 'ny=PEEK(yd+320)-c-ly 'nx=PEEK(yd+1)-c-lx ! Mov ES, picSeg ! Mov BX, yd ! Xor DX, DX ; Clear out these registers ! Xor AX, AX ! Mov CL, ES:[BX] ! Sub CL, 125 ! Mov DL, ES:[BX+320] ! Mov AL, ES:[BX+1] ! Sub DL, CL ! Sub AL, CL ! Sub DX, ly ! Sub AX, lx ! Mov ny, DX ! Mov nx, AX $IF %DannyCheater ' This is what Danny came up with: ! Mov AX, ny ! cmp AX, 10 ! Jb skip ! Cmp AX, 249 ! ja skip ! Mov BX, nx ! cmp BX, 10 ! Jb skip ! Cmp BX, 250 ! ja skip $ELSE ' Almost the original code, now in asm: 'IF ny < 0 OR ny>247 THEN ny=1 ! Mov AX, ny ! Test AX, AX ! Jbe SetNyTo1_001 ! Cmp AX, 247 ! Jbe NoNeedToChangeNy001 SetNyTo1_001: ! Mov ny, 1 NoNeedToChangeNy001: ' IF nx < 0 OR nx>247 THEN nx=1 ! Mov AX, nx ! Test AX, AX ! Jbe SetNxTo1_001 ! Cmp AX, 246 ! Jbe NoNeedToChangeNx001 SetNxTo1_001: ! Mov nx, 1 NoNeedToChangeNx001: $ENDIF ' I guess this part is quite slow? ' And really, I dunno if it is needed at all. 'nx = 125-ABS(125-nx) 'ny = 126-ABS(126-ny) ! Mov AX, 125 ! Mov BX, 125 ! Mov CX, 126 ! Mov DX, 126 ! Sub AX, nx ! Jns NotSet001 ! Neg AX NotSet001: ! Sub BX, AX ! Mov nx, BX ! Sub CX, ny ! Jns NotSet002 ! Neg CX NotSet002: ! Sub DX, CX ! Mov ny, DX 'DEF SEG=&hA000 'POKE y, EnvMap(nx*128+ny) ! mov BX, nx ! Mov AX, EnvMapSeg ! Db &hC1 ! Db &hE3 ! Db 7 ; Shl BX, 7 ! Mov ES, AX ! Add BX, ny ! Mov DX, &hA000 ! Mov AL, ES:[BX] $IF %SkipOnePixel ' Fill entire AX register ! Mov AH, ES:[BX] $ENDIF ! Mov BX, yd ! Mov ES, DX $IF %SkipOnePixel ' Plot two pixels at once. ! Mov ES:[BX], AX $ELSE ! Mov ES:[BX], AL $ENDIF skip: $IF %SkipOnePixel ' Since we plot two pixels at once, we ' skip the next one by adding 2. ! Add lx, 2 ! Add yd, 2 $ELSE ' Get next pixel ! inc lx ! inc yd $ENDIF ! Dec DI ! Jz SkipTrick001 ; PB has these annoying ! Jmp ForLoopDI ; fixup overflow errors... ' So we can't just have ' Jnz ForLoopDI SkipTrick001: ! Add ym, 320 ! Inc ly ! Dec SI ! Jz SkipTrick002 ! Jmp ForLoopSI SkipTrick002: ' Chck if ESC was pressed. ! In AL, &h60 ! Cmp AL, 1 ! Je ExitLoop001 ! Inc i ! Cmp i, 3*360 ! Jne ajmp_001 ! Mov i, 0 ajmp_001: $IF %SinkDown FadeOutPic $ENDIF 'NEXT i ! Jmp ForLoopWith_i LOOP ExitLoop001: ' Clear keypress from keyboard buuffer. ! Mov AH, 0 ! Int &h16 SetText '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= SUB PreCalculate () DIM X AS LOCAL INTEGER, Y AS LOCAL INTEGER DIM nx AS LOCAL SINGLE, ny AS LOCAL SINGLE, nz AS LOCAL SINGLE DIM I AS LOCAL INTEGER, t as LOCAL INTEGER RANDOMIZE TIMER picSeg = VARSEG(pic(0)) EnvMapSeg = VARSEG(EnvMap(0)) FOR y = 0 TO 127 FOR x = 0 TO 125 ' ' This defines the light source (environment map) ' Change the division values to something else ' to get another shape on light. Bigger values ' make it larger. nx = (x - 125) / 122 ny = (y - 127) / 110 ' This formula could be changed too, ' if you want a square light, or something. ' I won't present any code for that, ' you should be able to figure it out. nz = 1-SQR(nx!*nx + ny!*ny!) IF nz < 0 THEN nz = 0 EnvMap(x*128 + y) = nz * 255 NEXT NEXT END SUB '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= SUB LoadLogo (FileName AS STRING) ' ' A very simple image loader. I didn't care to include ' any TGA, GIF or PCX routines, else the code would have been ' bigger than it already is. ' For the wild programmers out there: ' Add a multiple file format loader... perhaps... with ' pull down menus and file boxes? And send it to me ;) ' ' DIM ScrnPtr AS LOCAL DWORD PTR DIM BufPtr AS LOCAL DWORD PTR DIM Offset AS LOCAL WORD DIM Buffer AS SHARED STRING * (1024*16) IF DIR$(FileName) = "" THEN BEEP PRINT "File: "+ FileName+" does not exist!" PRINT "Cannot proceed. Program ended." PRINT SYSTEM ELSE OPEN FileName FOR BINARY AS #1 ScrnPtr = picSeg*65536?? BufPtr = VARPTR32(Buffer) ' Expected: ' ' A 64000 byte .raw image. ' These can be created e.g. in the ' painting program "PSP" ' DIM i AS LOCAL BYTE DO UNTIL EOF(1) OR i>=4 INCR i BufPtr = VARPTR32(Buffer) GET #1, , Buffer FOR Offset=1 TO (1024*16)\4 @ScrnPtr = @BufPtr INCR ScrnPtr, 4 INCR BufPtr, 4 NEXT LOOP END IF CLOSE END SUB '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= SUB Soften (BYVAL nTimes AS INTEGER) DIM Offset AS LOCAL WORD DIM tmp AS LOCAL INTEGER DIM m as integer, counter AS LOCAL BYTE ' ' How many times to go through the screen and soften all the pixels. ' IF nTimes < 1 THEN nTimes = 1 ' A simple status bar. COLOR 7: PRINT STRING$(40, "°"); LOCATE , POS(0)-40 COLOR 14 ' Ouch, the bottleneck in my initialization! ' Would someone care to asm optimize this? ' My asm version just crashes, so I am skipping it :( m = (63999/40) * nTimes DEF SEG = picSeg DO FOR Offset=0 TO 63999 IF (Offset MOD m) = 0 AND Counter<40 THEN PRINT "Û";: INCR Counter $IF 0 ' ' Badoumm. Doesn't work. Why, why, why!? ' Mov ES, PicSeg Mov BX, Offset Mov AX, ES:[BX] Add AX, ES:[BX+320] Add AX, ES:[BX+1] Add AX, ES:[BX-320] Add AX, ES:[BX-1] ' It claims "Division by zero"!? Mov CL, 5 Div CL Mov ES:[BX], AL $ELSE tmp=PEEK(Offset+1) + PEEK(Offset+320) + PEEK(Offset) + _ PEEK(Offset-1) + PEEK(Offset-320) tmp=tmp\5 POKE Offset, tmp $ENDIF NEXT DECR nTimes LOOP WHILE nTimes DEF SEG END SUB '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= SUB Bumpize () DIM Offset AS LOCAL WORD DIM C AS LOCAL WORD ' ' Add random "bumpiness" to all pixels on screen. ' FOR Offset = 0 TO 31998 STEP 2 ' The RND function should ' be replaced. C = RND*120 + (RND*120)*256 ' ES must be changed in the loop since ' RND seems to destryo it. (Why else ' would my system crash?) ! Mov ES, picSeg ! Mov BX, Offset ! Mov DX, ES:[BX] ! Or DX, C ! Mov ES:[BX], DX ! Mov BX, &HFA00 ! Sub BX, Offset ! Mov DX, ES:[BX] ! Or DX, C ! Mov ES:[BX], DX NEXT END SUB '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ' ' Standard wait retrace sub. ' Can be used anywhere, since ' it doesn't destroy any ' CPU registers. ' SUB WaitRetrace () !Push DX !Push AX ! mov DX, &h3DA WRLoop: ! In AL, DX ! Test AL, 8 ! Je WRLoop WRLoop2: ! In AL, DX ! Test AL, 8 ! Jne WRLoop2 ! Pop AX ! Pop DX END SUB '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ' ' Generates a random palette from black, to ???, to white. ' ' SUB SetPalette () RANDOMIZE TIMER DIM R AS SINGLE, G AS SINGLE, B AS SINGLE DIM I AS LOCAL INTEGER, T AS LOCAL INTEGER ' .. R=RND*5+2 G=RND*5+2 B=RND*5+2 FOR I=0 TO 127 OUT &H3C8, i OUT &H3C9, i/R OUT &H3C9, i/G OUT &H3C9, i/B OUT &H3C8, i + 128 t=(127+i)/R IF t>63 THEN t=63 OUT &H3C9, t t=(127+i)/G IF t>63 THEN t=63 OUT &H3C9, t t=(127+i)/B IF t>63 THEN t=63 OUT &H3C9, t NEXT END SUB '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= SUB SetMCGA () ! Mov AX, 19 ! Int 16 END SUB '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= SUB SetText () ! Mov AX, 3 ! Int 16 END SUB '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ' ' Makes everything "sink" and disappear. ' ' SUB FadeOutPic () ! Mov ES, picSeg ! Mov BX, &HFA00 FDOP_002: ! Mov AL, ES:[BX] ! Cmp AL, 2 ! Jna FDOP_001 ' Decrement value on picture if it is above 2 ! Sub AL, 2 ! Mov ES:[BX], AL FDOP_001: ! Dec BX ! Jnz FDOP_002 END SUB '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=