Metropoli BBS
VIEWER: math.inc MODE: TEXT (CP437)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Filename     : Math.inc
; Included from: 3D1.ASM, 3D2.ASM, 3D3.ASM
; Description  : General math functions.
;
; Written by: John McCarthy
;             1316 Redwood Lane
;             Pickering, Ontario.
;             Canada, Earth, Milky Way (for those out-of-towners)
;             L1X 1C5
;
; Internet/Usenet:  BRIAN.MCCARTHY@CANREM.COM
;         Fidonet:  Brian McCarthy 1:229/15
;   RIME/Relaynet: ->CRS
;
; Home phone, (905) 831-1944, don't call at 2 am eh!
;
; Send me your protected mode source code!
; Send me your Objects!
; But most of all, Send me a postcard!!!!
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           public rotate        ; rotate using vmatrix
           public make3d        ; calculate 3d ?actual*?/z ( both x and y)
           public make3dx       ; xactual*x/z
           public make3dy       ; yactual*y/z
           public erotate       ; 32 bit rotate using ematrix
           public zsolve        ; solve single equation variable
           public ysolve
           public xsolve
           public cosign
           public sign
           public arctan
           public compound      ; generate rotation matrix (includes camera)
           public setsincose    ; set camera matrix
           public temp_matrix   ; set user defined/temporary matrix
           public temp_rotate   ; rotate point by temp matrix
           public matrix_multiply ; multiply tmatrix by vmatrix

           public set_precal7
           public set_precal147
           public frotate

           public fzsolve
           public fxsolve
           public fysolve

           public precal1
           public precal4
           public precal7

           public sqrt          ; eax=sqr(eax), thanks to TRAN!
           public _squareroot32 ; eax=sqr(eax)
           public sqrax2bx2     ; ax = sqr(ax^2+bx^2)

           public pre_cal_lambert   ; scan object si and calculate surface normals
           public calc_normal       ; guess...from 3 points, returns vector ebx,ecx,ebp
           public calc_d            ; calculate D from plane equation
           public lambert           ; calculate surface normal rotation matrix for object si
           public set_up_all_lambert; scans objects from si to di and calls pre_cal_lambert
           public lrotate           ; given normal for surface, figures out intensity

           public lx1               ; points to load up before calling calc_normal
           public ly1
           public lz1
           public lx2
           public ly2
           public lz2
           public lx3
           public ly3
           public lz3

           align 16

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Rotate - 32 bit rotate point using vmatrix
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;     vmatrix - 32 bit rotation matrix - set up by "compound" routine
; Out:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;
; Notes:
;
; All rotations (erotate,rotate,temp_rotate) are 32 bit.
; Frotate uses rotation along a plane and uses ematrix with precal147
;
; point rotation
; ebx = x   ecx = y   ebp = z    32 bit rotation!
; clobbers edx,esi,eax
;
; remember , matrix offsets are:
;
;  0 1 2     multiply those by 4 for the word address of the matrix
;  3 4 5
;  6 7 8
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

rotate:
           mov eax,vmatrix+8    ; solve x = bx(0)+cx(1)+bp(2)
           imul ebp
           shrd eax,edx,14
           mov edi,eax
           mov eax,vmatrix+4
           imul ecx
           shrd eax,edx,14
           add edi,eax
           mov eax,vmatrix+0
           imul ebx
           shrd eax,edx,14
           add edi,eax   ; di = new x

           mov eax,vmatrix+20   ; solve y = bx(3)+cx(4)+bp(5)
           imul ebp
           shrd eax,edx,14
           mov esi,eax
           mov eax,vmatrix+16
           imul ecx
           shrd eax,edx,14
           add esi,eax
           mov eax,vmatrix+12
           imul ebx
           shrd eax,edx,14
           add esi,eax   ; si = new y

           mov eax,vmatrix+32   ; solve z = bx(6)+cx(7)+bp(8)
           imul ebp
           shrd eax,edx,14
           mov ebp,eax
           mov eax,vmatrix+28
           imul ecx
           shrd eax,edx,14
           add ebp,eax
           mov eax,vmatrix+24
           imul ebx
           shrd eax,edx,14
           add ebp,eax   ; bp = new z

           mov ecx,esi
           mov ebx,edi

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Make3d - scale 3d point into 2d point
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
; Out:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;
; Notes:
;
; fast ratios found in macros.inc since
; multiplication has been substituted with fast lea
;
; trashes eax,edx,edi
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

make3d:                            ; bp must always be positive
           cmul eax,ebx,ratiox     ; use fast constant multiply

           idiv ebp
           mov ebx,eax

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Make3dy - scale 3d point into 2d point on x axis only
; In:
;    ECX - y point
;    EBP - z point
; Out:
;    ECX - y point
;    EBP - z point
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
make3dy:
           cmul eax,ecx,ratioy

           idiv ebp
           mov ecx,eax

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Make3dx - scale 3d point into 2d point on y axis only
; In:
;    EDI - x point
;    ESI - z point
; Out:
;    EDI - x point
;    ESI - z point
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

make3dx:                           ; bp must always be positive
           cmul eax,edi,ratiox

           idiv esi
           mov edi,eax

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Checkfront: checks if a side is visible. (counter-clockwise)
;
; In:
;   (EDI,EBP) - xy of point 1
;   (ESI,ECX) - xy of point 2
;   (EDX,EBX) - xy of point 3
; Out:
;   ECX < 0 if side counter-clockwise
;
; Notes: routine courtesy of "RAZOR"
; eg:
;          call checkfront
;          cmp ecx,0
;          jng dontdraw
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░


           align 16

checkfront:
           cmp edi,esi
           jng s cfc2
           mov eax,edi
           mov edi,esi
           mov esi,edx
           mov edx,eax
           mov eax,ebp
           mov ebp,ecx
           mov ecx,ebx
           mov ebx,eax
cfc:
           cmp edi,esi
           jng s cfc2
           mov eax,edi
           mov edi,esi
           mov esi,edx
           mov edx,eax
           mov eax,ebp
           mov ebp,ecx
           mov ecx,ebx
           mov ebx,eax
cfc2:
           mov eax,edx               ; ax = x3
           sub eax,edi               ; ax = x3 - x1
           sub ecx,ebp               ; cx = y2 - y1
           imul ecx                  ; ax = (x3-x1)*(y2-y1)
           mov ecx,eax               ; save it...
           mov eax,esi               ; ax = x2
           sub eax,edi               ; ax = x2 - x1
           sub ebx,ebp               ; bx = y3 - y1
           imul ebx                  ; ax = (x2-x1)*(y3-y1)
           sub ecx,eax               ; cx = (x3-x1)*(y2-y1)-(x2-x1)*(y3-y1)
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; ERotate - 32 bit rotate point using ematrix
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;     ematrix - 32 bit rotation matrix - set up by "setsincose" routine
; Out:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;
; Notes:
;
; point rotation for eye - solves all x,y,z parameters
; camera rotation is 32 bit and uses ematrix
;
; remember , matrix offsets are:
;
;  0 1 2     multiply those by 4 for the doubleword address of the matrix
;  3 4 5
;  6 7 8
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16

erotate:
           mov eax,ematrix+8
           imul ebp
           shrd eax,edx,14
           mov edi,eax
           if usez eq yes
           mov eax,ematrix+4
           imul ecx
           shrd eax,edx,14
           add edi,eax
           endif
           mov eax,ematrix+0
           imul ebx
           shrd eax,edx,14
           add edi,eax   ; di = new x

           mov eax,ematrix+20
           imul ebp
           shrd eax,edx,14
           mov esi,eax
           mov eax,ematrix+16
           imul ecx
           shrd eax,edx,14
           add esi,eax
           mov eax,ematrix+12
           imul ebx
           shrd eax,edx,14
           add esi,eax   ; si = new y

           mov eax,ematrix+32
           imul ebp
           shrd eax,edx,14
           mov ebp,eax
           mov eax,ematrix+28
           imul ecx
           shrd eax,edx,14
           add ebp,eax
           mov eax,ematrix+24
           imul ebx
           shrd eax,edx,14
           add ebp,eax   ; bp = new z

           mov ecx,esi
           mov ebx,edi

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Zsolve - 32 bit rotate point using ematrix - solve one variable only
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;    ematrix - 32 bit rotation matrix - set up by "setsincose" routine
;
; Out:
;    EBX - x point (same as entry)
;    ECX - y point (same as entry)
;    EBP - z point (same as entry)
;    ESI - new z point/location
;
; Notes:
;
; solve z from ematrix - same as above erotate but only solves z for fast
; test of where object is - result is in esi
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16

zsolve:
           mov eax,ematrix+32
           imul ebp
           shrd eax,edx,14
           mov esi,eax
           mov eax,ematrix+28
           imul ecx
           shrd eax,edx,14
           add esi,eax
           mov eax,ematrix+24
           imul ebx
           shrd eax,edx,14
           add esi,eax   ; si = new z
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Xsolve - 32 bit rotate point using ematrix - solve one variable only
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;    ematrix - 32 bit rotation matrix - set up by "setsincose" routine
;
; Out:
;    EBX - x point (same as entry)
;    ECX - y point (same as entry)
;    EBP - z point (same as entry)
;    EDI - new x point/location
;
; Notes:
; if object z test from above routine is positive, this routine will solve
; the rest of the rotation matrix.  this is so we don't waste time solving
; for x and y locations if the object is behind the camera anyway.
; saves imuls
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16
xsolve:
           mov eax,ematrix+8
           imul ebp
           shrd eax,edx,14
           mov edi,eax
           if usez eq yes
           mov eax,ematrix+4
           imul ecx
           shrd eax,edx,14
           add edi,eax
           endif
           mov eax,ematrix+0
           imul ebx
           shrd eax,edx,14
           add edi,eax   ; di = new x
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Ysolve - 32 bit rotate point using ematrix - solve one variable only
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;    ESI - new z point
;    EDI - new x point
;    ematrix - 32 bit rotation matrix - set up by "setsincose" routine
;
; Out:
;    EBX - x new point from EDI
;    ECX - y new point
;    EBP - z new point from ESI
;
; Notes:
;
; solve y from ematrix - same as above xsolve but solves y for fast
; test of where object is.  final variables are then cleaned up to
; immitate the erotate function in parts.
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16
ysolve:
           mov eax,ematrix+16
           imul ecx
           shrd eax,edx,14
           mov ecx,eax
           mov eax,ematrix+12
           imul ebx
           shrd eax,edx,14
           add ecx,eax
           mov eax,ematrix+20
           imul ebp
           shrd eax,edx,14
           add ecx,eax   ; cx = new y

           mov ebx,edi   ; final test, move into appropriate regs
           mov ebp,esi

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
;   Sign - 16 bit theta to 32bit sin(@)
; In:
;     AX - theta  0 - 65536 (0-360)
; Out:
;    EAX - sin (@)   (-4000h to 4000h)
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; CoSign - 16 bit theta to 32bit cos(@)
; In:
;     AX - theta  0 - 65536 (0-360)
; Out:
;    EAX - cos (@)   (-4000h to 4000h)
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Notes:
; calculate sin into eax, from ax, smashes bx
; after imul by sin, shr eax,14 to compensate for decimal factor!
;  eg:
;    mov eax,sin(@)
;    mov ebx,32bitnumber
;    imul ebx
;    shrd eax,edx,14
;    eax = ebx*sin(@)
;
;    mov ax,sin(@)
;    mov bx,16bitnumber
;    imul bx
;    shrd ax,dx,14
;    eax = bx*sin(@)
;
; eax is simply a sign extended ax
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16

cosign:
           add ax,4000h
sign:
           shr ax,2
           cmp ax,2000h
           jge s q3o4         ; quadrant 3 or 4

           cmp ax,1000h
           jl s q0            ; quad 1

           mov ebx,1fffh
           sub bx,ax
           jmp s halfsign     ; quad 2
q0:
           movzx ebx,ax
           jmp s halfsign
q3o4:
           cmp ax,3000h
           jl s q3
           mov ebx,3fffh
           sub bx,ax
           call halfsign      ; quad 4
           neg eax
           ret
q3:
           and ax,0fffh
           movzx ebx,ax       ; quad 3
           call halfsign
           neg eax
           ret
halfsign:
           xor eax,eax
           mov ax,w sinus[ebx*2]
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Arctan - 32 bit rise/run to 16bit arctan(rise/run)
; In:
;    EAX - Run
;    ECX - Rise
; Out:
;     AX - arctan(ECX/EAX)
;
; Notes:
; smashes cx,ax,dx,si
; arctan(ecx/0) is valid and tested for
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16

arctan:
           cmp eax,0
           jl s qd2or3
           cmp ecx,0
           jge s halftax      ; quadrant 1
           neg ecx            ; quadrant 4, ax=-ax
           call halftan
           neg ax
           shl eax,2
           ret
qd2or3:
           neg eax
           cmp ecx,0
           jge s qd2
           neg ecx            ; quad 3, ax=ax+8192
           call halftan
           add ax,8192
           shl eax,2
           ret
qd2:
           call halftan
           neg ax
           add ax,8192
           shl eax,2
           ret
halftax:
           call halftan
           shl eax,2
           ret

           align 16

halftan:
           xor edx,edx

; cx=rise  positive
; ax=run   positive

           cmp eax,ecx
           jl s opptan        ; greater than 45 degrees, other side...

           xchg ecx,eax       ; ax<cx
           shld edx,eax,11    ; *2048 edx = high dword for divide
           shl eax,11         ; *2048
           div ecx
           movzx esi,ax
           mov ax,w negtan[esi*2] ; resulting angle (0-512 is 0-45) in ax
           ret

           align 16

opptan:
           shld edx,eax,11    ; *2048 edx = high dword for divide
           shl eax,11         ; *2048

           div ecx
           movzx esi,ax       ; ax remainder
           mov cx,w negtan[esi*2]
           mov eax,1000h
           sub ax,cx          ; resulting angle (2048-4096 is 45-90) in ax
           ret

           align 16

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Compound - generate object matrix, 12 imul's first
; In:
;    ESI - Object # to get angles from
;    vxs[esi*2] - object x angle (0-65536)
;    vys[esi*2] - object y angle (0-65536)
;    vzs[esi*2] - object z angle (0-65536)
; Out:
;    vmatrix - resulting rotation matrix including camera matrix
;    ESI = ESI
;
; Notes:
;              x                         y                      z
;
;x=  cz * cy - sx * sy * sz   - sz * cy - sx * sy * cz     - cx * sy
;
;y=         sz * cx                   cx * cz                - sx
;
;z=  cz * sy + sx * sz * cy   - sy * sz + sx * cy * cz       cx * cy
;
;then perform matrix multiply by negative x and z matricies
;
; -x matrix                             -z matrix
;     x       y       z                   x       y       z
;
;x    1       0       0                cz     sz       0
;
;y    0      cx       sx              -sz     cz       0
;
;z    0     -sx       cx                0      0       1
;
; notice original object matrix takes 12 imuls, camera modify takes 24, can
; you do this faster? (less imuls)
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

compound:
           push esi

           mov ax,vxs[esi*2]
           neg ax
           push eax
           call cosign
           mov vcosx,eax
           pop eax
           call sign
           mov vsinx,eax
           mov ebp,eax            ; bp = sx
           neg eax
           mov [vmatrix+20],eax

           mov ax,vzs[esi*2]
           neg ax
           push eax
           call cosign
           mov vcosz,eax
           mov edi,eax            ; di = cz
           pop eax
           call sign
           mov vsinz,eax
           mov edx,eax            ; dx = sz

           mov ax,vys[esi*2]
           neg ax
           add ax,eyeay
           push eax
           call cosign
           mov vcosy,eax
           pop eax
           call sign
           mov vsiny,eax          ; ax = sy

           mov ebx,edx            ; save sz

           mov ecx,eax            ; save sy

           imul ebx               ; bx = - sy * sz
           shr eax,14
           movsx ebx,ax
           neg ebx
           mov [vmatrix+28],ebx

           mov eax,ecx            ; si = cz * sy
           imul edi
           shr eax,14
           movsx esi,ax
           mov [vmatrix+24],esi

           mov eax,vcosy

           imul edi               ; di = cy * cz
           shr eax,14
           movsx edi,ax
           mov [vmatrix+0],edi

           mov eax,vsinz
           mov ecx,vcosy

           imul ecx               ; cx = - sz * cy
           shr eax,14
           movsx ecx,ax
           neg ecx
           mov [vmatrix+4],ecx

           mov eax,ebp
           imul esi
           shr eax,14
           movsx esi,ax
           neg esi
           add [vmatrix+4],esi

           mov eax,ebp
           imul edi
           shr eax,14
           movsx edi,ax
           add [vmatrix+28],edi

           mov eax,ebp
           imul ebx
           shr eax,14
           movsx ebx,ax
           add [vmatrix+0],ebx

           mov eax,ebp
           imul ecx
           shr eax,14
           movsx ecx,ax
           neg ecx
           add [vmatrix+24],ecx

           mov esi,vcosx

           mov eax,vcosy
           imul esi                   ; cx * cy
           shr eax,14
           movsx eax,ax
           mov [vmatrix+32],eax

           mov eax,vsiny
           imul esi                   ;-cx * sy
           shr eax,14
           movsx eax,ax
           neg eax
           mov [vmatrix+8],eax

           mov eax,vsinz
           imul esi                   ; cx * sz
           shr eax,14
           movsx eax,ax
           mov [vmatrix+12],eax

           mov eax,vcosz
           imul esi                   ; cx * cz
           shr eax,14
           movsx eax,ax
           mov [vmatrix+16],eax

           mov edi,ecosx              ; now perform camera x rotation,12 imuls
           mov esi,esinx
           mov ebp,esi
           neg ebp

           mov eax,[vmatrix+12]
           imul edi
           shr eax,14
           movsx ecx,ax

           mov eax,[vmatrix+24]
           imul esi
           shr eax,14
           movsx eax,ax

           add ecx,eax                ; ecx = new vmatrix+12

           mov eax,[vmatrix+12]
           imul ebp
           shr eax,14
           movsx ebx,ax

           mov eax,[vmatrix+24]
           imul edi
           shr eax,14
           movsx eax,ax

           add ebx,eax                ; ebx = new vmatrix+24

           mov [vmatrix+12],ecx
           mov [vmatrix+24],ebx

           mov eax,[vmatrix+16]
           imul edi
           shr eax,14
           movsx ecx,ax

           mov eax,[vmatrix+28]
           imul esi
           shr eax,14
           movsx eax,ax

           add ecx,eax                ; ecx = new vmatrix+16

           mov eax,[vmatrix+16]
           imul ebp
           shr eax,14
           movsx ebx,ax

           mov eax,[vmatrix+28]
           imul edi
           shr eax,14
           movsx eax,ax

           add ebx,eax                ; ebx = new vmatrix+28

           mov [vmatrix+16],ecx
           mov [vmatrix+28],ebx

           mov eax,[vmatrix+20]
           imul edi
           shr eax,14
           movsx ecx,ax

           mov eax,[vmatrix+32]
           imul esi
           shr eax,14
           movsx eax,ax

           add ecx,eax                ; ecx = new vmatrix+20

           mov eax,[vmatrix+20]
           imul ebp
           shr eax,14
           movsx ebx,ax

           mov eax,[vmatrix+32]
           imul edi
           shr eax,14
           movsx eax,ax

           add ebx,eax                ; ebx = new vmatrix+32

           mov [vmatrix+20],ecx
           mov [vmatrix+32],ebx

           if usez eq yes

           mov edi,ecosz              ; now perform camera z rotation,12 imuls
           mov esi,esinz
           mov ebp,esi
           neg esi

           mov eax,[vmatrix+0]
           imul edi
           shr eax,14
           movsx ecx,ax

           mov eax,[vmatrix+12]
           imul esi
           shr eax,14
           movsx eax,ax

           add ecx,eax

           mov eax,[vmatrix+0]
           imul ebp
           shr eax,14
           movsx ebx,ax

           mov eax,[vmatrix+12]
           imul edi
           shr eax,14
           movsx eax,ax

           add ebx,eax

           mov [vmatrix+0],ecx
           mov [vmatrix+12],ebx

           mov eax,[vmatrix+4]
           imul edi
           shr eax,14
           movsx ecx,ax

           mov eax,[vmatrix+16]
           imul esi
           shr eax,14
           movsx eax,ax

           add ecx,eax

           mov eax,[vmatrix+4]
           imul ebp
           shr eax,14
           movsx ebx,ax

           mov eax,[vmatrix+16]
           imul edi
           shr eax,14
           movsx eax,ax

           add ebx,eax

           mov [vmatrix+4],ecx
           mov [vmatrix+16],ebx

           mov eax,[vmatrix+8]
           imul edi
           shr eax,14
           movsx ecx,ax

           mov eax,[vmatrix+20]
           imul esi
           shr eax,14
           movsx eax,ax

           add ecx,eax

           mov eax,[vmatrix+8]
           imul ebp
           shr eax,14
           movsx ebx,ax

           mov eax,[vmatrix+20]
           imul edi
           shr eax,14
           movsx eax,ax

           add ebx,eax

           mov [vmatrix+8],ecx
           mov [vmatrix+20],ebx

           endif

           pop esi
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Setsincose - generate rotation matrix for  y,x,z  camera rotation
;
; In:
;    eyeax - camera x angle (0-65536)
;    eyeay - camera y angle (0-65536)
;    eyeaz - camera z angle (0-65536)
; Out:
;    vmatrix - resulting rotation matrix including camera matrix
;
; Notes:
; called only once every frame.  completed in 12 multiplys
; matrix is also used for objects with no rotation (always angle 0,0,0)
;
; where is my postcard! see readme.doc for info.
;
;              x                    y                    z
;
; x=  cz * cy + sx * sy * sz     -cx * sz     - sy * cz + sx * cy * sz
;
; y=  sz * cy - sx * sy * cz      cx * cz     - sy * sz - sz * cy * cz
;
; z=         cx * sy                 sx                cx * cy
;
;
;  matrix offsets: (doublewords)
;
;     x  y  z
;
; x    0  4  8
; y   12 16 20
; z   24 28 32
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16

setsincose:

           mov ax,eyeax
           call cosign
           mov ecosx,eax          ; ecosx and such are used by object rotation
           mov ax,eyeax           ; ematrix is used to find where object is
           call sign
           mov esinx,eax
           mov [ematrix+28],eax
           mov ebp,eax            ; bp = sx

           if usez eq yes
           mov ax,eyeaz
           call cosign
           mov ecosz,eax
           mov edi,eax            ; di = cz
           mov ax,eyeaz
           call sign
           mov esinz,eax
           mov edx,eax            ; dx = sz
           endif

           if usez eq no
           mov edi,4000h          ; di = cos 0
           mov ecosz,4000h
           xor edx,edx            ; dx = sin 0
           mov esinz,0
           endif

           mov ax,eyeay
           call cosign
           mov ecosy,eax
           mov ax,eyeay
           call sign
           mov esiny,eax          ; ax = sy

           mov ebx,edx            ; save sz

           mov ecx,eax            ; save sy

           imul bx                ; bx = sy * sz
           shrd ax,dx,14
           movsx ebx,ax
           neg ebx
           mov [ematrix+20],ebx
           neg ebx

           mov eax,ecx            ; si = - (cz * sy)
           imul di
           shrd ax,dx,14
           movsx esi,ax
           neg esi
           mov [ematrix+8],esi

           mov eax,ecosy

           imul di                ; di = cy * cz
           shrd ax,dx,14
           movsx edi,ax
           mov [ematrix+0],edi

           mov eax,esinz
           mov ecx,ecosy

           imul cx                ; cx = sz * cy
           shrd ax,dx,14
           movsx ecx,ax
           mov [ematrix+12],ecx

           mov eax,ebp
           imul si
           shrd ax,dx,14
           movsx esi,ax
           add [ematrix+12],esi

           mov eax,ebp
           imul di
           shrd ax,dx,14
           movsx edi,ax
           neg edi
           add [ematrix+20],edi

           mov eax,ebp
           imul bx
           shrd ax,dx,14
           movsx ebx,ax
           add [ematrix+0],ebx

           mov eax,ebp
           imul cx
           shrd ax,dx,14
           movsx ecx,ax
           add [ematrix+8],ecx

           mov esi,ecosx

           mov eax,ecosy
           imul si                    ; cx * cy
           shrd ax,dx,14
           movsx eax,ax
           mov [ematrix+32],eax

           mov eax,esiny
           imul si                    ; cx * sy
           shrd ax,dx,14
           movsx eax,ax
           mov [ematrix+24],eax

           mov eax,esinz
           imul si                    ;-cx * sz
           shrd ax,dx,14
           movsx eax,ax
           neg eax
           mov [ematrix+4],eax

           mov eax,ecosz
           imul si                    ; cx * cz
           shrd ax,dx,14
           movsx eax,ax
           mov [ematrix+16],eax

           neg esinx                  ; reverse angles for object rotation
           neg esiny

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Temp_Matrix: generate temp matrix, 12 imul's, from object esi
;
; In:
;    ESI - Object # to get angles from
;    vxs[esi*2] - object x angle (0-65536)
;    vys[esi*2] - object y angle (0-65536)
;    vzs[esi*2] - object z angle (0-65536)
; Out:
;    tmatrix - resulting rotation matrix (excluding camera matrix)
;    ESI = ESI
;
; Notes:
;              x                         y                      z
;
;x=  cz * cy - sx * sy * sz   - sz * cy - sx * sy * cz     - cx * sy
;
;y=         sz * cx                   cx * cz                - sx
;
;z=  cz * sy + sx * sz * cy   - sy * sz + sx * cy * cz       cx * cy
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

temp_matrix:
           push esi

           mov ax,vxs[esi*2]
           neg ax
           push eax
           call cosign
           mov vcosx,eax
           pop eax
           call sign
           mov vsinx,eax
           mov ebp,eax            ; bp = sx
           neg eax
           mov [tmatrix+20],eax

           mov ax,vzs[esi*2]
           neg ax
           push eax
           call cosign
           mov vcosz,eax
           mov edi,eax            ; di = cz
           pop eax
           call sign
           mov vsinz,eax
           mov edx,eax            ; dx = sz

           mov ax,vys[esi*2]
           neg ax
           push eax
           call cosign
           mov vcosy,eax
           pop eax
           call sign
           mov vsiny,eax          ; ax = sy

           mov ebx,edx            ; save sz

           mov ecx,eax            ; save sy

           imul ebx               ; bx = - sy * sz
           shr eax,14
           movsx ebx,ax
           neg ebx
           mov [tmatrix+28],ebx

           mov eax,ecx            ; si = cz * sy
           imul edi
           shr eax,14
           movsx esi,ax
           mov [tmatrix+24],esi

           mov eax,vcosy

           imul edi               ; di = cy * cz
           shr eax,14
           movsx edi,ax
           mov [tmatrix+0],edi

           mov eax,vsinz
           mov ecx,vcosy

           imul ecx               ; cx = - sz * cy
           shr eax,14
           movsx ecx,ax
           neg ecx
           mov [tmatrix+4],ecx

           mov eax,ebp
           imul esi
           shr eax,14
           movsx esi,ax
           neg esi
           add [tmatrix+4],esi

           mov eax,ebp
           imul edi
           shr eax,14
           movsx edi,ax
           add [tmatrix+28],edi

           mov eax,ebp
           imul ebx
           shr eax,14
           movsx ebx,ax
           add [tmatrix+0],ebx

           mov eax,ebp
           imul ecx
           shr eax,14
           movsx ecx,ax
           neg ecx
           add [tmatrix+24],ecx

           mov esi,vcosx

           mov eax,vcosy
           imul esi                   ; cx * cy
           shr eax,14
           movsx eax,ax
           mov [tmatrix+32],eax

           mov eax,vsiny
           imul esi                   ;-cx * sy
           shr eax,14
           movsx eax,ax
           neg eax
           mov [tmatrix+8],eax

           mov eax,vsinz
           imul esi                   ; cx * sz
           shr eax,14
           movsx eax,ax
           mov [tmatrix+12],eax

           mov eax,vcosz
           imul esi                   ; cx * cz
           shr eax,14
           movsx eax,ax
           mov [tmatrix+16],eax

           pop esi
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Temp_Rotate - 32 bit rotate point using tmatrix
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;     tmatrix - 32 bit rotation matrix - set up by "temp_matrix" routine
; Out:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;
; Notes:
;  Same as rotate and erotate
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

temp_rotate:
           mov eax,tmatrix+8    ; solve x = bx(0)+cx(1)+bp(2)
           imul ebp
           shrd eax,edx,14
           mov edi,eax
           mov eax,tmatrix+4
           imul ecx
           shrd eax,edx,14
           add edi,eax
           mov eax,tmatrix+0
           imul ebx
           shrd eax,edx,14
           add edi,eax   ; di = new x

           mov eax,tmatrix+20   ; solve y = bx(3)+cx(4)+bp(5)
           imul ebp
           shrd eax,edx,14
           mov esi,eax
           mov eax,tmatrix+16
           imul ecx
           shrd eax,edx,14
           add esi,eax
           mov eax,tmatrix+12
           imul ebx
           shrd eax,edx,14
           add esi,eax   ; si = new y

           mov eax,tmatrix+32   ; solve z = bx(6)+cx(7)+bp(8)
           imul ebp
           shrd eax,edx,14
           mov ebp,eax
           mov eax,tmatrix+28
           imul ecx
           shrd eax,edx,14
           add ebp,eax
           mov eax,tmatrix+24
           imul ebx
           shrd eax,edx,14
           add ebp,eax   ; bp = new z

           mov ecx,esi
           mov ebx,edi

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Matrix_Multiply: multiply tmatrix by vmatrix, [vmatrix]=[tmatrix][vmatrix]
;
; In:
;    vmatrix - rotation matrix
;    tmatrix - rotation matrix
; Out:
;    vmatrix - resulting rotation matrix
;
; Notes:
;
; [ tmatrix+ 0 tmatrix+ 2 tmatrix+ 4 ] [ vmatrix+ 0 vmatrix+ 2 vmatrix+ 4 ]
; [                                  ] [                                  ]
; [ tmatrix+ 6 tmatrix+ 8 tmatrix+10 ] [ vmatrix+ 6 vmatrix+ 8 vmatrix+10 ]
; [                                  ] [                                  ]
; [ tmatrix+12 tmatrix+14 tmatrix+16 ] [ vmatrix+12 vmatrix+14 vmatrix+16 ]
;
; Think of it this way, this routine will generate a resulting matrix as  if
; you rotated an  object by tmatrix, then rotated  the  object  by  vmatrix.
; Instead, call this routine then you will only have to rotate the object by
; Vmatrix!.
;
; Notice Tmatrix is done before Vmatrix!!  This is used for calculating the
; positions of arms on bodies, hands on arms, fingers on hands...etc...
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

matrix_multiply:

           mov ebx,[vmatrix+0]
           mov ecx,[vmatrix+4]
           mov ebp,[vmatrix+8]

           mov eax,[tmatrix+0]
           imul ebx
           shrd eax,edx,14
           mov esi,eax

           mov eax,[tmatrix+12]
           imul ecx
           shrd eax,edx,14
           add esi,eax

           mov eax,[tmatrix+24]
           imul ebp
           shrd eax,edx,14
           add esi,eax

           push esi             ; tmatrix+0

           mov eax,[tmatrix+4]
           imul ebx
           shrd eax,edx,14
           mov esi,eax

           mov eax,[tmatrix+16]
           imul ecx
           shrd eax,edx,14
           add esi,eax

           mov eax,[tmatrix+28]
           imul ebp
           shrd eax,edx,14
           add esi,eax

           push esi             ; tmatrix+4

           mov eax,[tmatrix+8]
           imul ebx
           shrd eax,edx,14
           mov esi,eax

           mov eax,[tmatrix+20]
           imul ecx
           shrd eax,edx,14
           add esi,eax

           mov eax,[tmatrix+32]
           imul ebp
           shrd eax,edx,14
           add esi,eax

           push esi             ; tmatrix+8

           mov ebx,[vmatrix+12]
           mov ecx,[vmatrix+16]
           mov ebp,[vmatrix+20]

           mov eax,[tmatrix+0]
           imul ebx
           shrd eax,edx,14
           mov esi,eax

           mov eax,[tmatrix+12]
           imul ecx
           shrd eax,edx,14
           add esi,eax

           mov eax,[tmatrix+24]
           imul ebp
           shrd eax,edx,14
           add esi,eax

           push esi             ; tmatrix+12

           mov eax,[tmatrix+4]
           imul ebx
           shrd eax,edx,14
           mov esi,eax

           mov eax,[tmatrix+16]
           imul ecx
           shrd eax,edx,14
           add esi,eax

           mov eax,[tmatrix+28]
           imul ebp
           shrd eax,edx,14
           add esi,eax

           push esi             ; tmatrix+16

           mov eax,[tmatrix+8]
           imul ebx
           shrd eax,edx,14
           mov esi,eax

           mov eax,[tmatrix+20]
           imul ecx
           shrd eax,edx,14
           add esi,eax

           mov eax,[tmatrix+32]
           imul ebp
           shrd eax,edx,14
           add esi,eax

           push esi             ; tmatrix+20

           mov ebx,[vmatrix+24]
           mov ecx,[vmatrix+28]
           mov ebp,[vmatrix+32]

           mov eax,[tmatrix+0]
           imul ebx
           shrd eax,edx,14
           mov esi,eax

           mov eax,[tmatrix+12]
           imul ecx
           shrd eax,edx,14
           add esi,eax

           mov eax,[tmatrix+24]
           imul ebp
           shrd eax,edx,14
           add esi,eax

           push esi             ; tmatrix+24

           mov eax,[tmatrix+4]
           imul ebx
           shrd eax,edx,14
           mov esi,eax

           mov eax,[tmatrix+16]
           imul ecx
           shrd eax,edx,14
           add esi,eax

           mov eax,[tmatrix+28]
           imul ebp
           shrd eax,edx,14
           add esi,eax

           push esi             ; tmatrix+28

           mov eax,[tmatrix+8]
           imul ebx
           shrd eax,edx,14
           mov esi,eax

           mov eax,[tmatrix+20]
           imul ecx
           shrd eax,edx,14
           add esi,eax

           mov eax,[tmatrix+32]
           imul ebp
           shrd eax,edx,14
           add esi,eax

;          push esi             ; tmatrix+32

;          pop esi
           mov [vmatrix+32],esi
           pop esi
           mov [vmatrix+28],esi
           pop esi
           mov [vmatrix+24],esi
           pop esi
           mov [vmatrix+20],esi
           pop esi
           mov [vmatrix+16],esi
           pop esi
           mov [vmatrix+12],esi
           pop esi
           mov [vmatrix+ 8],esi
           pop esi
           mov [vmatrix+ 4],esi
           pop esi
           mov [vmatrix+ 0],esi
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Getroot:  ax = sqr(ax)
;
; In:
;    AX - number to take square root of (0 - 65535)
; Out:
;    AX - Square root
;
; Notes:
; I like TRANs method better - greater resolution and it doesn't require the
; table. Im not too sure whos method is faster.
;
; This routine is not used in the main animation loop.  It could be used for
; things like determining collision detection since collision does not require
; much accuracy and collision does require many many checks.
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
;           include squares.inc
;
;getroot:                      ; get square root of ax, where ax = 0-65535
;           cmp ax,0fe01h      ; since ax cannot be negative anyway!
;           jae sqr255         ; routine requires squares tables.
;           mov si,offset squares
;           mov cx,ax
;           inc cx
;           cld
;nextroot:
;           lodsw
;           cmp ax,cx
;           jbe  nextroot      ; jb is exact but jbe is better approximation
;           mov ax,si
;           sub ax,offset squares+3
;           sar ax,1
;           ret
;sqr255:
;           mov ax,255
;           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; _Squareroot32:  ax = sqr(ax)
; In:
;   EAX - number to take square root of (32bit)
; Out:
;   EAX = EBX = Square root
; Notes:
;  This came from Dave Stamp's VR386 - Hey, he's short, fat, and stupid looking
;  but I liked his routine anyway...
;
;  takes root of 32 bit number to 16 bit result
;  about 220 clocks worst case:
;  3 us on 486/66 and 10 us on 386/25
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

sqrt32  macro
        local skip
        shld edx,eax,2 ; get 2 bits of input to error
        shl eax,2
        add ebx,ebx    ; estimate*2
        mov ecx,ebx    ; temp = est*2
        add ecx,ecx
        cmp edx,ecx    ; error>2*est?
        jle s skip
        inc ebx        ; yes, update for new bit added
        inc ecx
        sub edx,ecx
skip:
        endm

        public _squareroot32

_squareroot32:
        test eax,0ffff0000h  ; can we cut it in half?
        jne s hasupper

        shl eax,16           ; yes, so prescale
        test eax,0ff000000h  ; half again?
        jne do16

        shl eax,8            ; yes, prescale
        jmp do8              ; do 8 loops

hasupper:
        test eax,0ff000000h  ; half again?
        jne do32
        shl eax,8
        jmp do24
do32:
        sqrt32
        sqrt32
        sqrt32
        sqrt32
do24:
        sqrt32
        sqrt32
        sqrt32
        sqrt32
do16:
        sqrt32
        sqrt32
        sqrt32
        sqrt32
do8:
        sqrt32
        sqrt32
        sqrt32
        sqrt32

        mov eax,ebx        ; result in eax and ebx
        ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Sqrt: Routine courtesy TRAN
;
; In:
;   EAX - number to take root of
; Out:
;   EAX - root
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sqrtbasetbl db 0,1,4,9,16,25,36,49,64,81,100,121,144,169,196,225

           public sqrt

           align 16

sqrt:
           pushad
           mov ebp,eax
           bsr ebx,eax
           jnz short sqrtf0
           xor ebx,ebx
sqrtf0:
           shr ebx,3
           lea eax,[ebx*8]
           mov cl,32
           sub cl,al
           rol ebp,cl
           mov eax,ebp
           movzx eax,al
           mov edi,offset sqrtbasetbl
           mov ecx,10h
sqrtl0:
           scasb
           je short sqrtl0d
           jb short sqrtl0d2
           loop sqrtl0
           inc edi
sqrtl0d2:
           dec edi
           inc cl
sqrtl0d:
           movzx edx,byte ptr [edi-1]
           dec cl
           xor cl,0fh
           mov edi,ecx
           mov ecx,ebx
           jecxz short sqrtdone
           sub eax,edx
sqrtml:
           shld eax,ebp,8
           rol ebp,8
           mov ebx,edi
           shl ebx,5
           xor edx,edx
           mov esi,eax
           div ebx
           rol edi,4
           add edi,eax
           add ebx,eax
sqrtf2:
           imul eax,ebx
           mov edx,eax
           mov eax,esi
           sub eax,edx
           jc short sqrtf1
           loop sqrtml
sqrtdone:
           mov [esp+28],edi
           popad
           ret
sqrtf1:
           dec ebx
           dec edi
           movzx eax,bl
           and al,1fh
           jmp sqrtf2

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Set_precal147: Set precal7 for plane transformation
;
; In:
;   ECX - y location to pre-calculate plane to
; Out:
;   precal1
;   precal4
;   precal7
;
; Notes:
; Plane is ecx and allows above formulas to determine where a
; point/object would be along that plane if z is not negative
;
; Good for runway translations or super huge background polygons - not used
; by regular 3d.asm routines
;
; How to use: lets say you've got a billion background objects that are
; on the ground (or all on the same y plane).  you call set_precal147
; with that y plane location and use frotate instead of erotate to
; determine where points rotated along that plane will end up.  this
; speeds the routine up by 33% by cutting out 3 imuls.
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

            align 16

precal1     dd 0
precal4     dd 0
precal7     dd 0

set_precal147:
            if usez eq yes
            mov eax,ecx
            sub eax,eyey
            imul ematrix+4
            shrd eax,edx,14
            mov precal1,eax
            endif

            mov eax,ecx
            sub eax,eyey
            imul ematrix+16
            shrd eax,edx,14
            mov precal4,eax

set_precal7:
            sub ecx,eyey
            mov eax,ecx
            imul ematrix+28
            shrd eax,edx,14
            mov precal7,eax
            ret

            align 16

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Frotate: fast object/point rotation along pre-calculated y plane
;
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;    ematrix - 32 bit rotation matrix - set up by "setsincose" routine
;    precal1 - set up by set_precal147
;    precal4 - set up by set_precal147
;    precal7 - set up by set_precal147
;
; Out:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;
; Notes:
;    see above
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

frotate:
           mov eax,ematrix+8
           imul ebp
           shrd eax,edx,14
           mov edi,eax
           if usez eq yes
           add edi,precal1
           endif
           mov eax,ematrix+0
           imul ebx
           shrd eax,edx,14
           add edi,eax   ; di = new x

           mov eax,ematrix+20
           imul ebp
           shrd eax,edx,14
           mov esi,eax
           add esi,precal4
           mov eax,ematrix+12
           imul ebx
           shrd eax,edx,14
           add esi,eax   ; si = new y

           mov eax,ematrix+32
           imul ebp
           shrd eax,edx,14
           mov ebp,eax
           add ebp,precal7
           mov eax,ematrix+24
           imul ebx
           shrd eax,edx,14
           add ebp,eax   ; bp = new z

           mov ecx,esi
           mov ebx,edi

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Fzrotate: fast single variable solve for object/point rotation along
;           pre-calculated y plane
;
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;    ematrix - 32 bit rotation matrix - set up by "setsincose" routine
;    precal1 - set up by set_precal147
;    precal4 - set up by set_precal147
;    precal7 - set up by set_precal147
;
; Out:
;    ESI - z point
;
; Notes:
; Fast solve for single matrix variable similar to erotate but uses frotate
; plane matrix with precal147
;
; remember , matrix offsets are:
;
;  0 1 2     multiply those by 4 for the doublewords
;  3 4 5
;  6 7 8
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16

fzsolve:
           mov eax,ematrix+32      ; solve z = bx(6)+cx(7)+bp(8)
           imul ebp
           shrd eax,edx,14
           mov esi,eax
           add esi,precal7
           mov eax,ematrix+24
           imul ebx
           shrd eax,edx,14
           add esi,eax   ; si = new z
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Fxrotate: fast single variable solve for object/point rotation along
;           pre-calculated y plane
;
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;    ematrix - 32 bit rotation matrix - set up by "setsincose" routine
;    precal1 - set up by set_precal147
;    precal4 - set up by set_precal147
;    precal7 - set up by set_precal147
;
; Out:
;    EDI - x point
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16
fxsolve:
           mov eax,ematrix+8        ; solve x = bx(0)+cx(1)+bp(2)
           imul ebp
           shrd eax,edx,14
           mov edi,eax
           if usez eq yes
           add edi,precal1
           endif
           mov eax,ematrix+0
           imul ebx
           shrd eax,edx,14
           add edi,eax   ; di = new x
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Fyrotate: fast single variable solve for object/point rotation along
;           pre-calculated y plane
;
; In:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;    ematrix - 32 bit rotation matrix - set up by "setsincose" routine
;    precal1 - set up by set_precal147
;    precal4 - set up by set_precal147
;    precal7 - set up by set_precal147
;    ESI - z point
;    EDI - x point
; Out:
;    EBX - x point
;    ECX - y point
;    EBP - z point
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           align 16
fysolve:
           mov eax,ematrix+20       ; solve y = bx(3)+cx(4)+bp(5)
           imul ebp
           shrd eax,edx,14
           mov ecx,eax
           add ecx,precal4
           mov eax,ematrix+12
           imul ebx
           shrd eax,edx,14
           add ecx,eax   ; cx = new y

           mov ebx,edi
           mov ebp,esi

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Lambert: generate lambert shading 1x3 matrix, completed in 6 imuls
;
; In:
;    ESI - Object # to get angles from
;    vxs[esi*2] - object x angle (0-65536)
;    vys[esi*2] - object y angle (0-65536)
;    vzs[esi*2] - object z angle (0-65536)
;    y_angle_of_sun - (0-65536)
;
; Out:
;    lmatrix  - shading matrix
;    ESI - ?
;
; Notes:
;
;z= ( sz ( cx + ( sx * cy )) + cz * sy ) * 45degrees  [x]
;   ( cz ( cx + ( sx * cy )) - sz * sy ) * 45degrees  [y]
;   ( cx * cy - sx ) * 45 degrees                     [z]
;
;note cos45=sin45=2d41h, but we will use 2d00h (99.2% accurate)
; you can change the y angle of the sun/light but not the x angle.
; changing the x angle would require a new formula.
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

lambert:
           mov ax,vxs[esi*2]
           neg ax
           push eax
           call cosign
           mov vcosx,eax
           pop eax
           call sign
           mov vsinx,eax
           mov ebp,eax            ; ebp = sx

           mov ax,vzs[esi*2]
           neg ax
           push eax
           call cosign
           mov vcosz,eax
           mov edi,eax            ; edi = cz
           pop eax
           call sign
           mov vsinz,eax
           mov edx,eax            ; edx = sz

           mov ax,vys[esi*2]
           neg ax
           add eax,y_angle_of_sun ; 2000h = 45 degrees y angle for light source
           push eax
           call cosign
           mov vcosy,eax
           mov esi,eax            ; esi = cy
           pop eax
           call sign
           mov vsiny,eax          ; eax = sy

           mov ebx,edx            ; ebx = sz

           mov ecx,eax            ; ecx = sy

           mov eax,ebp            ; get sx

           imul esi               ; eax = sx * cy
           shrd eax,edx,14
           sub eax,vcosx          ; eax = cx + ( sx * cy)

           push eax

           imul ebx               ; cx + ( sx * cy) * sz
           shrd eax,edx,14
           mov lmatrix+0,eax

           pop eax

           imul edi               ; di = cz
           shrd eax,edx,14
           mov lmatrix+4,eax      ; cx + ( sx * cy) * cz

           mov eax,ebx
           imul ecx               ; - sz * sy
           shrd eax,edx,14
           sub lmatrix+4,eax

           mov eax,edi
           imul ecx               ; cz * sy
           shrd eax,edx,14
           add lmatrix+0,eax

           mov eax,vcosx          ; (cx * cy - sx) * 45deg
           imul esi
           shrd eax,edx,14
           mov ebx,eax
           add ebx,ebp

           cmul eax,ebx,2d00h     ; * 45degrees
           shrd eax,edx,14
           movsx eax,ax
           mov lmatrix+8,eax

           mov ebx,lmatrix+4
           cmul eax,ebx,2d00h
           shrd eax,edx,14
           mov lmatrix+4,eax

           mov ebx,lmatrix+0
           cmul eax,ebx,2d00h
           shrd eax,edx,14
           mov lmatrix+0,eax

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Pre_cal_lambert: Pre-calculate all neccessary stuff for object DI
;
; In:
;    EDI - Object # to pre-calculate normals for
;    objbase[esi*4] -> offset of object data
; Out:
;    ESI -> minimum address of object data
;    EDI -> maximum address of object data
;    EBP -> points to header for object
;
; Notes:
;
; Precalculate surface normals for object di.  This  is  so  you  don't
; have to type them in when designing new objects. Imagine, 400 points,
; with 350 surfaces, calculating them all manually?  This routine  also
; figures out the iteration skip offset (if you have surfaces dependant
; on other surfaces) and also sets bit 1 if it is a line (two  points),
; and sets bit 4 if 1 point.This routine also sets the number of points
; to skip if an iteration is found. It  counts  the  number  of  points
; within iterations (even iterations within iterations)  and  sets  the
; skip value so any iterations skipped will have a pre-calculated point
; offset.  Did that make sense?
;
; Things done here:
;
; set point command if only 1 connection
; set line  command if only 2 connections
; set normal bit in commands if shading used in texture
; calculate and set shading normals
; calculate offsets for iteration jumps (in case surface not visible)
; calculate number of points to skip for iterations (in case surface not visible)
; set offset flag if iteration uses a point offset (4'th future use word)
; calculate and set auto-intensity of color if auto_s bit set
;
; Most of the above is done so the user (you) wont have to calculate this stuff
; yourself - makes object modification much easier.
;
; If you find the routine to be sloppy remember it is only used
; for object initialization.
;
; This routine  will probably crash if your object is not set up correctly
; The entire 3dvect source will crash if this routine  isn't run  and  the
; chances of you knowing how to do all that  this  routine  does  manually
; are pretty slim since most of you out there are doughheads.
;
; The minimum and maximum addresses are returned in ESI and EDI so you can
; output the resulting object to a binary file. EBP points to the starting
; header of the object (usually, but not neccessaraly, the minimum address)
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

lx1  dd 0
ly1  dd 0
lz1  dd 0

lx2  dd 0
ly2  dd 0
lz2  dd 0

lx3  dd 0
ly3  dd 0
lz3  dd 0

finx dd 0
finy dd 0
finz dd 0

temp1 dw 0
temp2 dw 0 ; number of points
temp3 dw 0 ; number of sides
temp4 dw 0
temp5 dd 0 ; minimum address
temp6 dd 0 ; maximum address

pre_cal_lambert:
           movzx edi,di           ; in case user is lazy
           mov esi,objbase[edi*4]
           mov temp5,esi
           mov temp6,esi
           push esi               ; save for exit
more_reses:
           push esi               ; save header offset
           add esi,4
           lodsd
           add esi,eax            ; handle first resolution

           lodsw
           mov temp2,ax
           lodsw
           mov temp3,ax

           mov xad,0
           mov yad,0
           mov zad,0
           mov temp1,-1
           mov ax,[esi+14]
           mov temp4,ax

           mov eax,[esi+8*2]
           or eax,[esi+8*2+4]
           jnz lam_hhgg
           mov dword ptr [esi+8*2],maxz    ; if no max/min found, force one
           mov dword ptr [esi+8*2+4],minz
lam_hhgg:
           mov eax,[esi+12*2]
           jnz lam_hhgc
           mov dword ptr [esi+12*2],tolerance
lam_hhgc:
           add esi,25*2           ; skip future use bytes

           mov edi,4              ; edi=2 to skip center of gravity
           mov xp,0
           mov yp,0
           mov zp,0
           cmp temp2,0
           je no_points_2
lam_ap12:
           mov bx,w [esi]         ; load all the points into array
           mov cx,w [esi+2]
           mov bp,w [esi+4]
           add bx,w xad
           add cx,w yad
           add bp,w zad
           movsx ebx,bx
           movsx ecx,cx
           movsx ebp,bp
           mov xp[edi],ebx
           mov yp[edi],ecx
           mov zp[edi],ebp
           add esi,6
           add edi,4
           dec temp2
           jne s lam_ap12         ; esi = address of sides now...
no_points_2:
           mov pointindex,edi

lam_loadsides:
           call checkesi          ; check minimum and maximum addresses

           mov edi,esi            ; save in case of line adjust
           mov ax,[esi]           ; get command

           mov bx,ax              ; save command
           test ax,special        ; test if special command
           jz s lam_notmap        ; no, skip through loop
           mov di,ax
           and edi,special-1

           cmp ax,gosub           ; check for jump commands, yeah,yeah, I should have made a table, who cares...
           jne ffgg1
           add esi,2
           lodsw
           push esi
           movsx eax,ax
           sub esi,2
           add esi,eax
           jmp lam_next
ffgg1:
           cmp ax,return
           jne ffgg2
           pop esi
           jmp lam_next
ffgg2:
           cmp ax,goto_offset
           jne ffgg3
           add esi,2
           lodsw
           movsx eax,ax
           sub esi,2
           add esi,eax
           jmp lam_next
ffgg3:
           mov di,number_ofb[edi*2] ; yes, skip special command length
           add esi,edi
           mov bx,0
           cmp ax,sub_object
           je lam_do_it
           cmp ax,static_sub_object
           je lam_do_it
           jmp lam_next           ; go to next side
lam_notmap:
           call pcl_testashade

           mov ax,[esi+2]         ; get texture for both sides
           or  ax,[esi+4]

           test ax,shade          ; test shading bit
           jnz lam_calcit         ; yes, calculate shading normal

           push edi               ; save command location

           add esi,4+4+2          ; skip 2 colour & 2 texture words & command
           mov edi,esi

           lodsw                  ; get first point indexer
           add ax,temp4
           stosw

           mov cx,ax
           mov dx,0
lam_ldlp:
           lodsw                  ; count number of connection points
           add ax,temp4
           stosw

           inc dx
           cmp ax,cx
           jne lam_ldlp

           call checkesi          ; check minimum and maximum addresses

           pop edi                ; pop command location

           cmp dx,1               ; only 1 point?, set point command
           jne lam_test_line
           or  w [edi+0],both
           or  w [edi+2],point
           or  w [edi+4],point
           jmp lam_test_iteration

lam_test_line:
           cmp dx,2               ; only 2 points?, set line command
           jne lam_test_iteration
           or  w [edi+0],both
           or  w [edi+2],line
           or  w [edi+4],line

lam_test_iteration:
           mov ax,0
           test bx,iterate        ; test if iteration command used
           jnz lam_do_it          ; yes,solve internal iteration
lam_next:
           dec temp1
           jnz lam_nopop
           pop ax
           mov temp4,ax
           pop ax
           mov temp1,ax
           jmp lam_next
lam_nopop:
           dec temp3
           jnz lam_loadsides

           pop esi
           lodsd
           add esi,4
           cmp eax,-1             ; last resolution?
           jne more_reses

           call checkesi          ; check minimum and maximum addresses
           pop ebp                ; pop header offset
           mov esi,temp5          ; load up start and end offsets of object
           mov edi,temp6

           ret

lam_calcit:
           push esi               ; save command location
           add esi,4+4+2          ; skip colour and 2 future use words

           lodsw                  ; first point
           add ax,temp4
           mov [esi-2],ax
           push ax
           movzx edi,ax
           shl edi,2

           mov ebx,[xp+edi]
           mov ecx,[yp+edi]
           mov ebp,[zp+edi]

           mov lx1,ebx
           mov ly1,ecx
           mov lz1,ebp

           lodsw                  ; second point
           add ax,temp4
           mov [esi-2],ax
           movzx edi,ax
           shl edi,2

           mov ebx,[xp+edi]
           mov ecx,[yp+edi]
           mov ebp,[zp+edi]

           mov lx2,ebx
           mov ly2,ecx
           mov lz2,ebp

           lodsw                  ; third point
           add ax,temp4
           mov [esi-2],ax
           movzx edi,ax
           shl edi,2

           mov ebx,[xp+edi]
           mov ecx,[yp+edi]
           mov ebp,[zp+edi]

           mov lx3,ebx
           mov ly3,ecx
           mov lz3,ebp

           push esi

           call calc_normal

           pop esi

           pop dx                 ; now find shading normal storage, pop first connector

lam_ldl2:
           lodsw
           add ax,temp4
           mov [esi-2],ax
           cmp ax,dx
           jne lam_ldl2

           mov edi,esi

           mov ax,bx
           stosw
           mov ax,cx
           stosw
           mov ax,bp
           stosw

           add esi,6

           pop edi         ; get original command location back
           or w [edi],normal
           mov bx,[edi]
           jmp lam_test_iteration

lam_surfc_cnt dw 0

; this finds the total number of points to skip if an iteration fails, dx = #
; remember, this is a pre-calculation routine so it doesn't need to be fast.

lam_do_it:
           mov dx,0        ; clear total number of points to skip

           mov ax,[esi+10]        ; test if there is a center of rotation point
           or ax,[esi+12]
           or ax,[esi+14]
           jz done_alter2
           or w [esi+8],centroid  ; set flag if offset found (center of gravity)

           movsx ebx,w [esi+10]
           movsx ecx,w [esi+12]
           movsx ebp,w [esi+14]
           add xad,ebx
           add yad,ecx
           add zad,ebp
           mov ebx,xad
           mov ecx,yad
           mov ebp,zad
           mov edi,pointindex
           mov xp[edi],ebx
           mov yp[edi],ecx
           mov zp[edi],ebp
           add pointindex,2
           add dx,1        ; centroid is an extra point to skip
done_alter2:
           mov ax,temp1
           push ax
           mov ax,temp4
           push ax

           mov ax,[esi+18]
           add temp4,ax
           mov w [esi+18],0

           mov lam_surfc_cnt,0
           push esi        ; this is our return address (continue from here+4)

           lodsw           ; get number of points.
           add dx,ax       ; save as TOTAL number of points to skip
           mov temp2,ax

           lodsw           ; get number of surfaces
           mov temp1,ax
           inc temp1
           add lam_surfc_cnt,ax ; count until this is zero

           mov eax,[esi+8*2]
           or eax,[esi+10*2]
           jnz lam_hhgg2
           mov dword ptr [esi+8*2],maxz  ; if no max/min found, force one
           mov dword ptr [esi+8*2+4],minz
lam_hhgg2:
           mov eax,[esi+12*2]
           jnz lam_hhgl
           mov dword ptr [esi+12*2],tolerance
lam_hhgl:
           add esi,25*2

           mov edi,pointindex
           cmp temp2,0
           je lam_test_check      ; only sides added, no additional points
lam_ap13:
           mov bx,w [esi]         ; load all the points into array
           mov cx,w [esi+2]       ; for calculation of gourad shadings
           mov bp,w [esi+4]
           add bx,w xad
           add cx,w yad
           add bp,w zad
           movsx ebx,bx
           movsx ecx,cx
           movsx ebp,bp
           mov xp[edi],ebx
           mov yp[edi],ecx
           mov zp[edi],ebp
           add esi,6
           add edi,4
           dec temp2
           jne s lam_ap13         ; esi = address of sides now...

           mov pointindex,edi

lam_test_check:
           cmp lam_surfc_cnt,0    ; test if user just wants to add points
           je lam_no_surfs        ; i dont know why anyone would want to do this?

lam_test_until_target:
           lodsw                  ; get command
           mov bx,ax
           test ax,special        ; test if special command
           jz s lam_notmap_it     ; no, skip through loop

           mov di,ax
           and edi,special-1

           cmp ax,gosub           ; check for jump commands, yeah,yeah, I should have made a table, who cares...
           jne ffgg1x
           add esi,2
           lodsw
           push esi
           movsx eax,ax
           sub esi,2
           add esi,eax
           jmp lam_next_it
ffgg1x:
           cmp ax,return
           jne ffgg2x
           pop esi
           jmp lam_next_it
ffgg2x:
           cmp ax,goto_offset
           jne ffgg3x
           add esi,2
           lodsw
           movsx eax,ax
           sub esi,2
           add esi,eax
           jmp lam_next_it
ffgg3x:
           movzx edi,number_ofb[edi*2] ; yes, skip special command length
           add esi,edi
           sub esi,2
           cmp ax,sub_object
           je lam_re_lam
           cmp ax,static_sub_object
           je lam_re_lam
           jmp s lam_nog

lam_notmap_it:
           lodsw
           mov bp,ax
           lodsw
           or bp,ax               ; find if shading bit used, add esi,6 if so
           add esi,4              ; skip 2 colour words

           lodsw                  ; get first point indexer
           mov cx,ax
lam_ldl3:
           lodsw
           cmp ax,cx
           jne lam_ldl3

           test bp,shade          ; test if gouraud normal present
           jz lam_nog
           add esi,6              ; skip it if present
lam_nog:
           test bx,iterate        ; test if iteration command used
           jnz lam_re_lam         ; solve internal iteration again...
lam_next_it:
           dec lam_surfc_cnt
           jnz lam_test_until_target
lam_no_surfs:
           mov edi,esi            ; save current location
           pop esi                ; return original start location
           sub edi,esi            ; get difference between them
           sub di,8               ; offset for loadsides routine - constant

           lodsw                  ; get number of points
           mov bx,ax

           lodsw
           add temp3,ax

           mov cx,dx
           mov ax,6
           imul bx
           movzx ebx,ax

           mov ax,di
           mov edi,esi
           stosw                  ; save offset
           mov ax,cx
           stosw                  ; save number of points found in iterations

           mov eax,[esi+8*2]
           or eax,[esi+10*2]
           jnz lam_hhgg3
           mov dword ptr [esi+8*2],maxz  ; if no max/min found, force one
           mov dword ptr [esi+8*2+4],minz
lam_hhgg3:
           mov eax,[esi+12*2]
           jnz lam_hhgq
           mov dword ptr [esi+12*2],tolerance
lam_hhgq:
           add esi,25*2           ; adjust for next load
           add esi,ebx

           jmp lam_next

lam_re_lam:
           lodsw           ; get number of points for recursed iteration
           add dx,ax       ; save as TOTAL number of points to skip
           mov cx,ax

           lodsw           ; get number of surfaces
           add lam_surfc_cnt,ax ; count until this is zero

           mov eax,[esi+8*2]
           or eax,[esi+10*2]
           jnz lam_hhgg4
           mov dword ptr [esi+8*2],maxz  ; if no max/min found, force one
           mov dword ptr [esi+8*2+4],minz
lam_hhgg4:
           mov eax,[esi+12*2]
           jnz lam_hhgv
           mov dword ptr [esi+12*2],tolerance
lam_hhgv:
           add esi,25*2

           mov eax,6
           imul cx
           add esi,eax

           jmp lam_next_it

checkesi:
           cmp esi,temp5
           jae noesi1
           mov temp5,esi
noesi1:
           cmp esi,temp6
           jbe noesi2
           mov temp6,esi
noesi2:
           ret

pcl_testashade:
           mov ax,[esi+2]
           or  ax,[esi+4]
           test ax,auto_s
           jnz pcl_test2
           ret
pcl_test2:
           pushad
           push esi
           push esi
           mov vxs,0
           mov vys,0
           mov vzs,0
           mov esi,0
           call lambert
           pop esi

           movzx ebx,w [esi+10]
           movzx ecx,w [esi+12]
           movzx edx,w [esi+14]

           mov eax,xp[ebx]
           mov lx1,eax
           mov eax,yp[ebx]
           mov ly1,eax
           mov eax,zp[ebx]
           mov lz1,eax
           mov eax,xp[ecx]
           mov lx2,eax
           mov eax,yp[ecx]
           mov ly2,eax
           mov eax,zp[ecx]
           mov lz2,eax
           mov eax,xp[edx]
           mov lx3,eax
           mov eax,yp[edx]
           mov ly3,eax
           mov eax,zp[edx]
           mov lz3,eax
           call calc_normal

           call lrotate
           pop esi
           push edi

           test w [esi+2],auto_s
           jz pcl_test4
           test w [esi+2],inverse
           jz pcl_test3
           neg edi
pcl_test3:
           add edi,256
           shr edi,1              ; result -256 to +256, turn into 0-256
           mov al,b shading_tables[edi] ; now into 0-15
           xor ah,ah
           add w [esi+6],ax
           mov ax,[esi+2]
           and ax,65535-auto_s
           mov [esi+2],ax
pcl_test4:
           pop edi

           test w [esi+4],auto_s
           jz pcl_test6
           test w [esi+4],inverse
           jz pcl_test5
           neg edi
pcl_test5:
           add edi,256
           shr edi,1              ; result -256 to +256, turn into 0-256
           mov al,b shading_tables[edi] ; now into 0-15
           xor ah,ah
           add w [esi+8],ax
           mov ax,[esi+4]
           and ax,65535-auto_s
           mov [esi+4],ax
pcl_test6:
           popad
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Calc_normal: calculate surface normal
;
; In:
;    LX1 - x of point 1 on triangle
;    LY1 - y of point 1 on triangle
;    LZ1 - z of point 1 on triangle
;    LX2 - x of point 2 on triangle
;    LY2 - y of point 2 on triangle
;    LZ2 - z of point 2 on triangle
;    LX3 - x of point 3 on triangle
;    LY3 - y of point 3 on triangle
;    LZ3 - z of point 3 on triangle
;
; Out:
;    EBX = finx = x of surface normal of triangle
;    ECX = finy = y of surface normal of triangle
;    EBP = finz = z of surface normal of triangle
;
; Notes:
; x2 = x2 - x1
; y2 = y2 - y1
; z2 = z2 - z1
;
; x3 = x3 - x1
; y3 = y3 - y1
; z3 = z3 - z1
;
; x = y2 * z3 - z2 * y3
; y = z2 * x3 - x2 * z3
; z = x2 * y3 - y2 * x3
;
; a = SQR(x ^ 2 + y ^ 2 + z ^ 2)
;
; x = INT(x / a * 256 + .5)
; y = INT(y / a * 256 + .5)
; z = INT(z / a * 256 + .5)
;
; This worked for me on the first try!
;
; If you wanted to get the equation of a plane, you could do this after:
;  d = - x * x1 - y * y1 - z * z1
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

           nshl = 8

calc_normal:
           mov ebx,lx1
           mov ecx,ly1
           mov ebp,lz1

           sub lx2,ebx
           sub ly2,ecx
           sub lz2,ebp

           sub lx3,ebx
           sub ly3,ecx
           sub lz3,ebp

           mov eax,ly2
           mov ebx,lz3
           imul ebx
           mov ecx,eax

           mov eax,lz2
           mov ebx,ly3
           imul ebx
           sub ecx,eax

           mov finx,ecx ; save x of normal

           mov eax,lz2
           mov ebx,lx3
           imul ebx
           mov ecx,eax

           mov eax,lx2
           mov ebx,lz3
           imul ebx
           sub ecx,eax

           mov finy,ecx ; save y of normal

           mov eax,lx2
           mov ebx,ly3
           imul ebx
           mov ecx,eax

           mov eax,ly2
           mov ebx,lx3
           imul ebx
           sub ecx,eax

           mov finz,ecx ; save z of normal

calc_testloop:
           cmp finx,32768 ; make sure (normal^2)*2 is < 2^32
           jge calc_shrit
           cmp finy,32768
           jge calc_shrit
           cmp finz,32768
           jge calc_shrit

           cmp finx,-32768
           jle calc_shrit
           cmp finy,-32768
           jle calc_shrit
           cmp finz,-32768
           jg  ok_2_bite_dust

calc_shrit:
           shr finx,1   ; calculations will be too large if squared, div by 2
           test finx,40000000h
           jz no_neg_calc1
           or   finx,80000000h
no_neg_calc1:
           shr finy,1
           test finy,40000000h
           jz no_neg_calc2
           or   finy,80000000h
no_neg_calc2:
           shr finz,1
           test finz,40000000h
           jz no_neg_calc3
           or   finz,80000000h
no_neg_calc3:
           jmp calc_testloop

ok_2_bite_dust:
           mov eax,finx ; x^2
           mov edi,eax  ; objects
           imul edi
           mov edi,eax

           mov eax,finy ; y^2
           mov esi,eax
           imul esi
           mov esi,eax

           mov eax,finz ; z^2
           mov ebp,eax
           imul ebp

           add eax,esi
           add eax,edi

           call sqrt    ; get square root of number

           mov ecx,eax
           cmp ecx,0
           je lam_abort ; should never happen!

           mov eax,finx
           cdq
           shl eax,nshl ; set unit vector to 2^nshl (256)
           idiv ecx
           mov finx,eax

           mov eax,finy
           cdq
           shl eax,nshl
           idiv ecx
           mov finy,eax

           mov eax,finz
           cdq
           shl eax,nshl
           idiv ecx
           mov finz,eax

           mov ebx,finx
           mov ecx,finy
           mov ebp,finz

lam_abort:
           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Calc_D: Calculate D portion of equation of a plane
; In:
;    EBX = x of surface normal of triangle
;    ECX = y of surface normal of triangle
;    EBP = z of surface normal of triangle
;    LX1 - x of point on triangle (any point)
;    LY1 - y of point on triangle
;    LZ1 - z of point on triangle
; Out:
;    EAX = D     (Ax+By+Cz=D)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
calc_d:
           mov eax,lx1
           imul ebx
           mov esi,eax

           mov eax,ly1
           imul ecx
           add esi,eax

           mov eax,lz1
           imul ebp
           add eax,esi

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Set_up_all_lambert: set up all lambert normals from object si to object di
;
; In:
;    ESI - object # to start at
;    EDI - object # to end at
;    objbase[esi*4 - edi*4] -> offsets to object data
; Out:
;   null
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

set_up_all_lambert:
           movzx edi,di ; in case user is lazy
           movzx esi,si

           xchg edi,esi ; so user doesn't get confuzed
set_lop:
           push esi edi
           call pre_cal_lambert
           pop edi esi
           inc edi
           cmp edi,esi
           jna  set_lop

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Lrotate: rotate surface normal through lambert matrix
;
; In:
;    BX - x of surface normal
;    CX - y of surface normal
;    BP - z of surface normal
;    lmatrix - 16 bit, 1x3 lambert shading matrix - set up by "lambert" routine
; Out:
;    BX - x of surface normal (untouched)
;    CX - y of surface normal (untouched)
;    BP - z of surface normal (untouched)
;   EDI - colour intensity for surface (-255 to +255)
;
; Notes:
;   Your mother is a hamster.
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

lrotate:
           mov eax,lmatrix+8    ; solve edi = bx(0)+cx(4)+bp(8)
           imul bp
           shrd ax,dx,14
           mov edi,eax
           mov eax,lmatrix+4
           imul cx
           shrd ax,dx,14
           add edi,eax
           mov eax,lmatrix+0
           imul bx
           shrd ax,dx,14
           add edi,eax          ; di = new colour -256 to 255 (not edi, di!)
           movsx edi,di

           add edi,256          ; make sure result is within range -256 to 256
           and edi,511          ; sometimes is messes up...
           sub edi,256

           ret

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Shading tables used for fake cosin colour intensity - 256 bytes
; Default is 16 colours per lambert calculation.  But you could have 32, 48
; or whatever you want, even an odd number like 53.  Use the SHADING.BAS
; program to make the table to your custom size.
;
; Shading_bits is the variable for use with the texture command "LAST".  This
; variable tells the routine what bits to  pluck  off  when  determining  the
; shading intensity. Obviosly this cant  be  used  if  your  palette  shading
; length is not a function of 2's complement.
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

  if shading_colours eq 16

; 16 colour shading table

  shading_bits equ 0fh

  align 16
shading_tables:
  db 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1
  db 1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2
  db 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3
  db 3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4
  db 4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5
  db 5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6
  db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7
  db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
  db 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
  db 8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9
  db 9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10
  db 10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11
  db 11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12
  db 12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13
  db 13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14
  db 14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15

  elseif shading_colours eq 32

; 32 colour shading table

  shading_bits equ 1fh

   align 16
shading_tables:
   db 0,0,0,0,0,0,0,1,1,1,1,1,1,1,2,2
   db 2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4
   db 4,4,4,4,5,5,5,5,5,5,5,5,6,6,6,6
   db 6,6,6,6,7,7,7,7,7,7,7,7,8,8,8,8
   db 8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10
   db 10,10,10,10,10,11,11,11,11,11,11,11,11,12,12,12
   db 12,12,12,12,12,12,13,13,13,13,13,13,13,13,14,14
   db 14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15
   db 16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17
   db 17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19
   db 19,19,20,20,20,20,20,20,20,20,20,21,21,21,21,21
   db 21,21,21,22,22,22,22,22,22,22,22,22,23,23,23,23
   db 23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25
   db 25,25,25,25,26,26,26,26,26,26,26,26,27,27,27,27
   db 27,27,27,28,28,28,28,28,28,28,29,29,29,29,29,29
   db 29,29,30,30,30,30,30,30,30,31,31,31,31,31,31,31
   endif

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
;
; Sqrax2bx2: Fast pathagorean solve, sqr(ax^2+bx^2)
;
; In:
;   EAX - a number
;   EBX - another number
; Out:
;   EAX - sqr(EAX^2+EBX^2)
;
; Notes:
;
; Works well for numbers eax<90, ebx<128.  Uses huge table.
; Limit of solve is for 32 bit values. Only works for positive values of ax&bx
; Numbers outside the limit of the tabel could be shr'd and re-tested rather
; than using the mathematical solution. There should be a .BAS Qbasic file
; that generates the table provided with the 3Dvect source.
;
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

pathagorean:
;          include pathagor.inc ; dont include this routine if you don't need it
                                ; 'cause it's a real big table.
sqrax2bx2:
           cmp eax,ebx          ; set eax = smallest
           ja s pa_smal
           xchg eax,ebx
pa_smal:
           cmp eax,90           ; check if parameters of triangle are within
           jae s pa_imul        ; table or are too big for fast load.
           cmp ebx,128
           jae s pa_imul

           shl ebx,7            ; *128
           mov al,b pathagorean[eax+ebx]
           ret
pa_imul:
           imul eax,eax         ; variables too large, do mathematically
           imul ebx,ebx
           add eax,ebx
           jmp sqrt


[ RETURN TO DIRECTORY ]