;----------------------------------------------------------- ; ; MXPG.ASM - Convex polygon fill with Gouraud shading ; Copyright (c) 1994 by Alessandro Scotti ; ;----------------------------------------------------------- WARN PRO NOWARN RES INCLUDE MODEX.DEF PUBLIC mxGouraudPoly ;----------------------------------------------------------- ; ; "Local" definitions ; TPOINT STRUC X DW ? Y DW ? TPOINT ENDS ; Do NOT change order! TSCAN STRUC Y1 DW ? E1 DB ? C1 DB ? Y2 DW ? E2 DB ? C2 DB ? TSCAN ENDS MAXSCANCOLUMNS EQU POLYSCANBUFSIZE / SIZE TSCAN MX_TEXT SEGMENT USE16 PARA PUBLIC 'CODE' ASSUME cs:MX_TEXT, ds:NOTHING, es:NOTHING EXTRN mx_VideoSegment : WORD EXTRN mx_CodeSegment : WORD EXTRN mx_BytesPerLine : WORD EXTRN mx_ClipX1 : WORD EXTRN mx_ClipY1 : WORD EXTRN mx_ClipX2 : WORD EXTRN mx_ClipY2 : WORD EXTRN mx_ScanBuffer : NEAR ;----------------------------------------------------------- ; ; Scans an edge using the DDA (digital differential analyzer) algorithm. ; Also interpolates color for shading. ; ; Input: ; DS:BX = pointer to start point (X1, Y1) ; DS:SI = pointer to end point (X2, Y2) ; ES:DI = pointer to edge buffer ; DX = start color ; AX = end color ; Output: ; ES:DI = updated pointer to edge buffer ; Notes: ; must preserve DS:SI. ; subScan PROC NEAR mov cx, ds:[si].X sub cx, ds:[bx].X ; Get width jg @@1 ret @@1: push bp ; Save BP push di ; Save scan info offset push cx ; Save height push ax ; Save colors push dx mov ax, ds:[si].Y mov bx, ds:[bx].Y sub ax, bx ; Get height jg @@T2B ; Scan top to bottom jl @@B2T ; Scan bottom to top ; Special case: vertical line mov ax, bx @@V: mov es:[di].Y1, ax add di, SIZE TSCAN dec cx jnz @@V jmp @@GetColorInfo ; Scan top to bottom @@T2B: cwd div cx mov bp, ax xor ax, ax div cx xchg ax, bx ; BP:BX = fixed 16:16 step mov dx, 8000h @@T2BLoop: mov es:[di].Y1, ax add di, SIZE TSCAN add dx, bx adc ax, bp dec cx jnz @@T2BLoop jmp @@GetColorInfo ; Scan bottom to top @@B2T: neg ax cwd div cx mov bp, ax xor ax, ax div cx xchg ax, bx mov dx, 8000h @@B2TLoop: mov es:[di].Y1, ax add di, SIZE TSCAN sub dx, bx sbb ax, bp dec cx jnz @@B2TLoop ; Now get the color info @@GetColorInfo: pop bx ; Restore colors pop ax pop cx ; Height pop di ; ES:DI -> scan info sub ax, bx ; Get color range jg @@CL2R jl @@CR2L ; Special case: same color mov ah, bl mov al, 80h @@CV: mov WORD PTR es:[di].E1, ax add di, SIZE TSCAN dec cx jnz @@CV jmp @@Exit ; Scan left to right @@CL2R: cwd div cx mov bp, ax xor ax, ax div cx xchg ax, bx ; BP:BX = fixed 16:16 step mov dx, 8000h @@CL2RLoop: mov es:[di].C1, al mov es:[di].E1, dh add di, SIZE TSCAN add dx, bx adc ax, bp dec cx jnz @@CL2RLoop jmp @@Exit ; Scan right to left @@CR2L: neg ax cwd div cx mov bp, ax xor ax, ax div cx xchg ax, bx mov dx, 8000h @@CR2LLoop: mov es:[di].C1, al mov es:[di].E1, dh add di, SIZE TSCAN sub dx, bx sbb ax, bp dec cx jnz @@CR2LLoop @@Exit: pop bp ret subScan ENDP ;----------------------------------------------------------- ; ; Fills a scan column. ; ; Input: ; DS:SI = current TSCAN ; ES:DI = address of top pixel ; CX = number of pixels to write ; DX = base color ; Output: ; none ; subFillScan PROC NEAR mov ax, WORD PTR ds:[si].E2 mov bx, WORD PTR ds:[si].E1 cmp ah, bh jg @@L2R ; Color increases jl @@R2L ; Color decreases ; Special case: color doesn't change add ax, dx mov dx, [mx_BytesPerLine] @@V: mov es:[di], ah add di, dx dec cx jnz @@V ret ; Color increases @@L2R: .push bp, si mov si, bx add si, dx ; Relocate color sub ax, bx xor dx, dx div cx mov bp, ax ; BP = color step, integer part xor ax, ax div cx mov bx, ax ; BX = color step, fractional part mov dx, 8000h mov ax, [mx_BytesPerLine] xchg si, ax @@L2RLoop: mov es:[di], ah add dx, bx adc ax, bp add di, si dec cx jnz @@L2RLoop .pop bp, si ret ; Color decreases @@R2L: .push bp, si mov si, bx add si, dx ; Relocate color sub ax, bx neg ax xor dx, dx div cx mov bp, ax ; BP = color step, integer part xor ax, ax div cx mov bx, ax ; BX = color step, fractional part mov dx, 8000h mov ax, [mx_BytesPerLine] xchg si, ax @@R2LLoop: mov es:[di], ah sub dx, bx sbb ax, bp add di, si dec cx jnz @@R2LLoop .pop bp, si ret subFillScan ENDP ;----------------------------------------------------------- ; ; Fills a convex polygon with the specified color. ; Interpolates pixel colors using the Gouraud algorithm. ; ; Input: ; Count = number of vertexes ; Map = indexes of points and colors (integer) ; Points = array of points (integer X, Y coordinates) ; Colors = array of colors (integer) ; Color = base color ; Output: ; none ; Notes: ; vertexes must be in counterclockwise order, arrays are 0-based. ; mxGouraudPoly PROC FAR ARG Color:WORD, \ Colors:DWORD, \ Points:DWORD, \ Map:DWORD, \ Count:WORD = ARG_SIZE LOCAL WritePlane:BYTE:2, \ ScanOffsetT:WORD, \ ScanOffsetB:WORD, \ ScanCount:WORD, \ Holder:WORD, \ Height:WORD, \ MinIdxT:WORD, \ MinIdxB:WORD, \ MaxIdx:WORD, \ Width:WORD, \ BoxX1:WORD, \ BoxY1:WORD, \ BoxX2:WORD, \ BoxY2::WORD = AUTO_SIZE .enter AUTO_SIZE .push ds, si, es, di ASSUME ds:NOTHING ; Check that at least three vertexes are specified mov cx, [Count] cmp cx, 3 jb @@Exit ;------------------------------ ; Find bounding box for polygon les di, [Map] lds si, [Points] ; Pointer to vertex array mov [BoxX1], 32767 mov [BoxX2], -32768 mov [BoxY1], 32767 mov [BoxY2], -32768 xor dx, dx @@MinMaxLoop: mov bx, es:[di] ; Get index of vertex .shl bx, 2 ; Get offset in point array add bx, si ; Check X range @@CheckMinX: mov ax, ds:[bx].X ; Get X coordinate cmp ax, [BoxX1] jge @@CheckMaxX mov [BoxX1], ax mov [MinIdxT], dx mov [MinIdxB], dx @@CheckMaxX: cmp ax, [BoxX2] jle @@CheckMinY mov [BoxX2], ax mov [MaxIdx], dx ; Check Y range @@CheckMinY: mov ax, ds:[bx].Y cmp ax, [BoxY1] jge @@CheckMaxY mov [BoxY1], ax @@CheckMaxY: cmp ax, [BoxY2] jle @@CheckDone mov [BoxY2], ax ; Repeat thru all points @@CheckDone: inc di ; Next map entry inc di inc dx inc dx dec cx jnz @@MinMaxLoop ;--------------------------------- ; Check if polygon is full clipped mov ax, [BoxX2] cmp ax, [mx_ClipX1] ; Is poly full clipped? jl @@Exit mov bx, [BoxX1] cmp bx, [mx_ClipX2] ; Is poly full clipped? jg @@Exit sub ax, bx ; Get width jle @@Exit ; Exit if not positive mov ax, [BoxY2] cmp ax, [mx_ClipY1] ; Is poly full clipped? jl @@Exit mov bx, [BoxY1] cmp bx, [mx_ClipY2] ; Is poly full clipped? jg @@Exit sub ax, bx ; Get height jle @@Exit ; Exit if not positive dec [Count] shl [Count], 1 ; We'll work with word offsets mov es, [mx_CodeSegment] ;-------------- ; Scan top edge mov ax, OFFSET mx_ScanBuffer mov [ScanOffsetT], ax mov si, [MinIdxT] ; Offset of bottom point index @@STLoop: lds bx, [Map] ; DS:BX -> map table mov di, ds:[bx+si] ; Index of top point #1 dec si ; Next point dec si test si, si jnl @@ST1 mov si, [Count] @@ST1: mov [MinIdxT], si ; Save new index of top point mov si, ds:[bx+si] ; Get index of top point #2 lds bx, [Colors] ; Get pointer to color array shl di, 1 ; Convert indexes to offsets shl si, 1 mov ax, ds:[bx+si] ; Get colors mov dx, ds:[bx+di] lds bx, [Points] ; DS:BX -> point array shl si, 1 shl di, 1 add si, bx ; DS:SI -> top point #2 add bx, di ; DS:BX -> top point #1 mov di, [ScanOffsetT] call subScan ; Scan edge mov [ScanOffsetT], di mov si, [MinIdxT] cmp si, [MaxIdx] ; End of edge? jne @@STLoop ; No, continue ;----------------- ; Scan bottom edge mov ax, OFFSET mx_ScanBuffer + OFFSET Y2 mov [ScanOffsetB], ax mov si, [MinIdxB] ; Offset of bottom point index @@SBLoop: lds bx, [Map] ; DS:BX -> map table mov di, ds:[bx+si] ; Index of bottom point #1 inc si ; Next bottom point inc si cmp si, [Count] jbe @@SB1 xor si, si @@SB1: mov [MinIdxB], si ; Save new index of bottom point mov si, ds:[bx+si] ; Get index of bottom point #2 lds bx, [Colors] ; Get pointer to color array shl di, 1 ; Convert indexes to offsets shl si, 1 mov ax, ds:[bx+si] ; Get colors mov dx, ds:[bx+di] lds bx, [Points] ; DS:BX -> point array shl si, 1 shl di, 1 add si, bx ; DS:SI -> top point #2 add bx, di ; DS:BX -> top point #1 mov di, [ScanOffsetB] call subScan ; Scan edge mov [ScanOffsetB], di mov si, [MinIdxB] cmp si, [MaxIdx] ; End of edge? jne @@SBLoop ; No, continue ;-------------------- ; Clip left and right mov si, OFFSET mx_ScanBuffer mov ax, [BoxX1] mov cx, [BoxX2] sub cx, ax ; CX = bounding box width mov bx, [mx_ClipX1] sub bx, ax jle @@ClipL1 ; No need to clip left sub cx, bx ; Update width add ax, bx ; BoxX1 = mx_ClipX1 mov [BoxX1], ax .shl bx, 3 ; Warning!!! This is an hand-coded add si, bx ; multiply by the size of TSCAN @@ClipL1: mov bx, ax add bx, cx ; Last scan column sub bx, [mx_ClipX2] jle @@ClipL2 ; No need to clip right sub cx, bx ; Clip right @@ClipL2: test cx, cx ; Is clipped width positive? jle @@Exit ; No, exit mov [ScanCount], cx ; Save number of columns to draw mov [ScanOffsetT], si ; Remember offset of (clipped) buffer mov ds, [mx_CodeSegment] ; DS:SI -> scan buffer ;------------------------------ ; Check if Y clipping is needed mov ax, [BoxY1] cmp ax, [mx_ClipY1] jl @@ClipTB ; Need to clip top mov ax, [BoxY2] cmp ax, [mx_ClipY2] jg @@ClipTB ; Need to clip bottom jmp @@ClipYExit ; Skip Y clipping ;-------------------- ; Clip top and bottom @@ClipTB: mov di, cx ; DI = scan count inc di ; Increment count for pre-loop test sub si, SIZE TSCAN @@ClipYLoop: dec di ; Any column left? jz @@ClipYExit ; No, exit add si, SIZE TSCAN mov ax, ds:[si].Y1 ; Y1 mov cx, ds:[si].Y2 ; Y2 mov dx, [mx_ClipY2] cmp ax, dx ; Full clipped? jg @@ClipYClip ; Yes, skip this column cmp cx, dx ; Need to clip bottom? jle @@ClipY1 ; No, continue ; Clip bottom, need to scale colors too mov ds:[si].Y2, dx mov bx, cx sub bx, dx ; Clip distance sub cx, ax ; Height jle @@ClipYClip mov ax, WORD PTR ds:[si].E1 sub ax, WORD PTR ds:[si].E2 imul bx idiv cx add WORD PTR ds:[si].E2, ax mov ax, ds:[si].Y1 ; Restore AX and CX mov cx, ds:[si].Y2 @@ClipY1: mov dx, [mx_ClipY1] cmp cx, dx ; Full top clipped? jl @@ClipYClip ; Yes, skip sub cx, ax ; Get height jle @@ClipYClip ; Skip if not positive cmp ax, dx ; Need to clip top? jge @@ClipYLoop ; No, continue ; Clip top, need to scale colors too mov ds:[si].Y1, dx ; Y1 = mx_ClipY1 sub dx, ax ; DX = number of pixels clipped cmp cx, dx jbe @@ClipYClip ; Full clipped, skip mov ax, WORD PTR ds:[si].E2 sub ax, WORD PTR ds:[si].E1 ; AX = color distance imul dx idiv cx add WORD PTR ds:[si].E1, ax ; Update starting color jmp @@ClipYLoop @@ClipYClip: mov ds:[si].Y1, -1 ; Mark column as clipped jmp @@ClipYLoop @@ClipYExit: ;------------- ; Draw columns mov es, [mx_VideoSegment] mov si, [ScanOffsetT] mov cl, BYTE PTR [BoxX1] ; Init write plane and cl, 03h mov al, 11h shl al, cl mov [WritePlane], al .shr [BoxX1], 2 mov ax, [Color] ; Make 8:8 fixed color mov ah, al xor al, al mov [Color], ax @@DrawLoop: mov ax, ds:[si].Y1 test ax, ax ; Was column clipped? js @@DrawNext ; Yes, skip mov cx, ds:[si].Y2 sub cx, ax ; CX = height jle @@DrawNext mul [mx_BytesPerLine] ; Get pixel address add ax, [BoxX1] mov di, ax mov ah, [WritePlane] mov al, 02h mov dx, TS out dx, ax mov dx, [Color] call subFillScan @@DrawNext: rol [WritePlane], 1 adc [BoxX1], 0 ; Bump pointer to video memory if needed add si, SIZE TSCAN dec [ScanCount] jnz @@DrawLoop @@Exit: xor ax, ax .pop ds, si, es, di .leave ARG_SIZE mxGouraudPoly ENDP MX_TEXT ENDS END