; THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX ; SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO ; END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ; ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS ; IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS ; SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE ; FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE ; CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS ; AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. ; COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. .386 option oldstructs .nolist include psmacros.inc include vecmat.inc .list assume cs:_TEXT, ds:_DATA _DATA segment dword public USE32 'DATA' rcsid db "$Id: vecmat.asm 1.55 1995/08/24 12:04:11 matt Exp $" even ;these symbols enable/disable code which is unused in DESCENT AVG4_ENABLED = 0 V2MN_ENABLED = 0 ;temporary vectors for surface normal calculation tempv0 vms_vector <> tempv1 vms_vector <> xvec vms_vector <> yvec vms_vector <> zvec vms_vector <> tempav vms_angvec <> ;sine & cosine values for angles_2_matrix sinp fix ? cosp fix ? sinb fix ? cosb fix ? sinh fix ? cosh fix ? ;These should never be changed! _vmd_zero_vector label vms_vector fix 0,0,0 _vmd_identity_matrix label vms_matrix fix f1_0,0,0 fix 0,f1_0,0 fix 0,0,f1_0 _DATA ends _TEXT segment dword public USE32 'CODE' ;add two vectors, filling in dest ;takes eax=dest, esi,edi=sources, returns eax=dest vm_vec_add: push ebx ;save work reg for ofs, mov ebx,[esi].ofs add ebx,[edi].ofs mov [eax].ofs,ebx endm pop ebx ret ;subtracts two vectors, filling in dest ;takes eax=dest, esi,edi=sources, returns eax=dest vm_vec_sub: push ebx ;save work reg for ofs, mov ebx,[esi].ofs sub ebx,[edi].ofs mov [eax].ofs,ebx endm pop ebx ret ;adds one vector to antother ;takes edi=dest, esi=source, returns edi=dest vm_vec_add2: push ebx ;save work reg for ofs, mov ebx,[esi].ofs add [edi].ofs,ebx endm pop ebx ret ;subtract one vector from another ;takes edi=dest, esi=source, returns edi=dest vm_vec_sub2: push ebx ;save work reg for ofs, mov ebx,[esi].ofs sub [edi].ofs,ebx endm pop ebx ret ;averages two vectors. takes eax=dest, esi,edi=srcs vm_vec_avg: push ebx for ofs, mov ebx,[esi].ofs add ebx,[edi].ofs sar ebx,1 mov [eax].ofs,ebx endm pop ebx ret if AVG4_ENABLED ;averages four vectors. takes eax=dest, esi,edi,ecx,edx=srcs vm_vec_avg4: push ebx for ofs, mov ebx,[esi].ofs add ebx,[edi].ofs add ebx,[ecx].ofs add ebx,[edx].ofs sar ebx,2 mov [eax].ofs,ebx endm pop ebx ret endif ;scales a vector in place. takes ebx=vector, ecx=scale. returns ebx=vector vm_vec_scale: pushm eax,edx for ofs, mov eax,[ebx].ofs fixmul ecx mov [ebx].ofs,eax endm popm eax,edx ret ;scales and copies a vector. takes edi=dest, ebx=src, ecx=scale. returns edi=vector vm_vec_copy_scale: pushm eax,edx for ofs, mov eax,[ebx].ofs fixmul ecx mov [edi].ofs,eax endm popm eax,edx ret ;scales a vector, adds it to another, and stores in a 3rd ;takes edi=dest, ebx=src1, esi=src2, ecx=scale. returns edi=vector vm_vec_scale_add: pushm eax,edx for ofs, mov eax,[esi].ofs fixmul ecx add eax,[ebx].ofs mov [edi].ofs,eax endm popm eax,edx ret ;scales a vector and adds it to another. takes edi=dest, esi=src, ecx=scale. returns edi=vector vm_vec_scale_add2: pushm eax,edx for ofs, mov eax,[esi].ofs fixmul ecx add [edi].ofs,eax endm popm eax,edx ret ;scales a vector in place, taking n/d for scale. takes edi=vector, ebx=n,ecx=d. returns edi=vector vm_vec_scale2: or ecx,ecx ; @mk, 01/04/94, prevent divide overflow je vmvs_out pushm eax,edx for ofs, mov eax,[edi].ofs imul ebx idiv ecx mov [edi].ofs,eax endm popm eax,edx vmvs_out: ret ;compute the distance between two points. (does sub and mag) ;takes esi,edi=points, returns eax=dist vm_vec_dist: pushm ebx,ecx,edx mov eax,[esi].x sub eax,[edi].x imul eax mov ebx,eax mov ecx,edx mov eax,[esi].y sub eax,[edi].y imul eax add ebx,eax adc ecx,edx mov eax,[esi].z sub eax,[edi].z imul eax add eax,ebx adc edx,ecx call quad_sqrt popm ebx,ecx,edx ret ;computes an approximation of the magnitude of a vector ;uses dist = largest + next_largest*3/8 + smallest*3/16 ;takes esi=vector, returns eax=dist align 4 vm_vec_mag_quick: pushm ebx,ecx,edx mov eax,[esi].x or eax,eax jns eax_ok2 neg eax eax_ok2: mov ebx,[esi].y or ebx,ebx jns ebx_ok2 neg ebx ebx_ok2: mov ecx,[esi].z or ecx,ecx jns ecx_ok2 neg ecx ecx_ok2: mag_quick_eax_ebx_ecx: cmp eax,ebx jg no_swap_ab xchg eax,ebx no_swap_ab: cmp ebx,ecx jg do_add xchg ebx,ecx cmp eax,ebx jg do_add xchg eax,ebx do_add: sar ebx,2 ; b*1/4 sar ecx,3 ; c*1/8 add ebx,ecx ; b*1/4 + c*1/8 add eax,ebx ;a + b*1/4 + c*1/8 sar ebx,1 ; b*1/8 + c*1/16 add eax,ebx ;a + b*3/4 + c*3/16 popm ebx,ecx,edx ret ;computes an approximation of the distance between two points. ;uses dist = largest + next_largest*3/8 + smallest*3/16 ;takes esi,edi=points, returns eax=dist align 4 vm_vec_dist_quick: pushm ebx,ecx,edx mov ebx,[esi].x sub ebx,[edi].x jns ebx_ok neg ebx ebx_ok: mov ecx,[esi].y sub ecx,[edi].y jns ecx_ok neg ecx ecx_ok: mov eax,[esi].z sub eax,[edi].z jns eax_ok neg eax eax_ok: jmp mag_quick_eax_ebx_ecx ;compute magnitude of vector. takes esi=vector, returns eax=mag vm_vec_mag: pushm ebx,ecx,edx mov eax,[esi].x imul eax mov ebx,eax mov ecx,edx mov eax,[esi].y imul eax add ebx,eax adc ecx,edx mov eax,[esi].z imul eax add eax,ebx adc edx,ecx call quad_sqrt popm ebx,ecx,edx ret ;return the normalized direction vector between two points ;dest = normalized(end - start). ;takes edi=dest, esi=endpoint, ebx=startpoint. Returns mag of dir vec ;NOTE: the order of the parameters matches the vector subtraction vm_vec_normalized_dir: pushm ebp,eax,ebx,edx mov ebp,ebx mov eax,[esi].x sub eax,[ebp].x mov [edi].x,eax imul eax mov ebx,eax mov ecx,edx mov eax,[esi].y sub eax,[ebp].y mov [edi].y,eax imul eax add ebx,eax adc ecx,edx mov eax,[esi].z sub eax,[ebp].z mov [edi].z,eax imul eax add eax,ebx adc edx,ecx call quad_sqrt mov ecx,eax ;mag in ecx jecxz no_div2 for ofs, mov eax,[edi].ofs fixdiv ecx mov [edi].ofs,eax endm no_div2: ;return value (mag) in ecx popm ebp,eax,ebx,edx ret ;save as vm_vec_normalized_dir, but with quick sqrt ;takes edi=dest, esi=endpoint, ebx=startpoint. Returns mag of dir vec vm_vec_normalized_dir_quick: pushm eax,edi,esi mov eax,edi mov edi,ebx ;regs right for sub call vm_vec_sub mov esi,eax call vm_vec_normalize_quick ;return value (mag) in ecx popm eax,edi,esi ret ;normalize a vector in place. takes esi=vector ;returns ecx=mag of source vec. trashes edi vm_vec_normalize: push edi mov edi,esi call vm_vec_copy_normalize pop edi ret ;normalize a vector. takes edi=dest, esi=vector ;returns ecx=mag of source vec vm_vec_copy_normalize: pushm eax,ebx,edx mov eax,[esi].x imul eax mov ebx,eax mov ecx,edx mov eax,[esi].y imul eax add ebx,eax adc ecx,edx mov eax,[esi].z imul eax add eax,ebx adc edx,ecx call quad_sqrt mov ecx,eax ;mag in ecx jecxz no_div for ofs, mov eax,[esi].ofs fixdiv ecx mov [edi].ofs,eax endm no_div: popm eax,ebx,edx ret ;normalize a vector in place. takes esi=vector ;returns ecx=mag of source vec. trashes edi ;uses approx. dist vm_vec_normalize_quick: push edi mov edi,esi call vm_vec_copy_normalize_quick pop edi ret ;normalize a vector. takes edi=dest, esi=vector ;returns ecx=mag of source vec ;uses approx. dist vm_vec_copy_normalize_quick: pushm eax,ebx,edx call vm_vec_mag_quick mov ecx,eax ;mag in ecx jecxz no_div_q for ofs, mov eax,[esi].ofs fixdiv ecx mov [edi].ofs,eax endm no_div_q: popm eax,ebx,edx ret ;compute dot product of two vectors. takes esi,edi=vectors, returns eax=dotprod vm_vec_dotprod: pushm ebx,ecx,edx mov eax,[esi].x imul [edi].x mov ebx,eax mov ecx,edx mov eax,[esi].y imul [edi].y add ebx,eax adc ecx,edx mov eax,[esi].z imul [edi].z add eax,ebx adc edx,ecx shrd eax,edx,16 ;ifndef NDEBUG ;check for overflow ;always do overflow check, and return saturated value sar edx,16 ;get real sign from high word mov ebx,edx cdq ;get sign of our result cmp bx,dx ;same sign? je no_oflow ;;debug_brk 'overflow in vm_vec_dotprod' mov eax,7fffffffh or ebx,ebx ;check desired sign jns no_oflow neg eax no_oflow: ;endif popm ebx,ecx,edx ret ;computes cross product of two vectors. takes eax=dest, esi,edi=src vectors ;returns eax=dest. Note: this magnitude of the resultant vector is the ;product of the magnitudes of the two source vectors. This means it is ;quite easy for this routine to overflow and underflow. Be careful that ;your inputs are ok. vm_vec_crossprod: ifndef NDEBUG cmp eax,esi break_if e,'crossprod: dest==src0' cmp eax,edi break_if e,'crossprod: dest==src1' endif pushm ebx,ecx,edx,ebp mov ebp,eax mov eax,[edi].y imul [esi].z mov ebx,eax mov ecx,edx mov eax,[edi].z imul [esi].y sub eax,ebx sbb edx,ecx shrd eax,edx,16 ifndef NDEBUG ;check for overflow mov ebx,edx ;save cdq ;get sign of result shr ebx,16 ;get high 16 of quad result cmp dx,bx ;sign extension the same? break_if ne,'overflow in crossprod' endif mov [ebp].x,eax mov eax,[edi].z imul [esi].x mov ebx,eax mov ecx,edx mov eax,[edi].x imul [esi].z sub eax,ebx sbb edx,ecx shrd eax,edx,16 ifndef NDEBUG ;check for overflow mov ebx,edx ;save cdq ;get sign of result shr ebx,16 ;get high 16 of quad result cmp dx,bx ;sign extension the same? break_if ne,'overflow in crossprod' endif mov [ebp].y,eax mov eax,[edi].x imul [esi].y mov ebx,eax mov ecx,edx mov eax,[edi].y imul [esi].x sub eax,ebx sbb edx,ecx shrd eax,edx,16 ifndef NDEBUG ;check for overflow mov ebx,edx ;save cdq ;get sign of result shr ebx,16 ;get high 16 of quad result cmp dx,bx ;sign extension the same? break_if ne,'overflow in crossprod' endif mov [ebp].z,eax mov eax,ebp ;return dest in eax popm ebx,ecx,edx,ebp ret abs_eax macro cdq xor eax,edx sub eax,edx endm ;computes surface normal from three points. takes ebx=dest, eax,esi,edi=vecs ;returns eax=dest. Result vector is normalized. vm_vec_normal: call vm_vec_perp ;get unnormalized push ecx xchg esi,eax ;get in esi, save esi call vm_vec_normalize xchg eax,esi pop ecx ret ;make sure a vector is reasonably sized to go into a cross product ;trashes eax,ebx,cl,edx check_vec: mov eax,[esi].x abs_eax mov ebx,eax mov eax,[esi].y abs_eax or ebx,eax mov eax,[esi].z abs_eax or ebx,eax jz null_vector xor cl,cl ;init shift count test ebx,0fffc0000h ;too big jz not_too_big check_4_down: test ebx,000f00000h jz check_2_down add cl,4 sar ebx,4 jmp check_4_down check_2_down: test ebx,0fffc0000h jz not_2_down add cl,2 sar ebx,2 jmp check_2_down not_2_down: sar [esi].x,cl sar [esi].y,cl sar [esi].z,cl ret ;maybe too small... not_too_big: test ebx,0ffff8000h jnz not_too_small check_4_up: test ebx,0fffff000h jnz check_2_up add cl,4 sal ebx,4 jmp check_4_up check_2_up: test ebx,0ffff8000h jnz not_2_up add cl,2 sal ebx,2 jmp check_2_up not_2_up: sal [esi].x,cl sal [esi].y,cl sal [esi].z,cl not_too_small: ret null_vector: ; debug_brk commented out by mk on 05/04/94 ;** debug_brk "null vector in check_vec" ret ;computes surface normal from three points. takes ebx=dest, eax,esi,edi=vecs ;returns eax=dest. Result vector is NOT normalized, but this routine does ;make an effort that cross product does not overflow or underflow vm_vec_perp: pushm esi,edi ;save for return push eax ;save src0 xchg esi,edi lea eax,tempv1 call vm_vec_sub ;src2 - src1 mov esi,edi ;get src1 in esi pop edi ;get src0 in edi lea eax,tempv0 call vm_vec_sub ;src1 - src0 mov esi,eax ;tempv0 in esi lea edi,tempv1 ;tempv1 in edi pushm ebx,ecx,edx call check_vec ;make sure reasonable value xchg esi,edi call check_vec ;make sure reasonable value xchg esi,edi popm ebx,ecx,edx mov eax,ebx ;get dest in eax call vm_vec_crossprod popm esi,edi ;restore regs ret ;compute a rotation matrix from three angles. takes edi=dest matrix, ;esi=angles vector. returns edi=dest matrix. vm_angles_2_matrix: pushm eax,edx,ebx,ecx,esi ;get sines & cosines mov ax,[esi].pitch call fix_sincos mov sinp,eax mov cosp,ebx mov ax,[esi].bank call fix_sincos mov sinb,eax mov cosb,ebx mov ax,[esi].head call fix_sincos mov sinh,eax mov cosh,ebx ;alternate entry point with sines & cosines already computed. ;Note all the registers already pushed. sincos_2_matrix: ;now calculate the 9 elements mov eax,sinb fixmul sinh mov ecx,eax ;save sbsh fixmul sinp mov ebx,eax mov eax,cosb fixmul cosh mov esi,eax ;save cbch add eax,ebx mov [edi].m1,eax ;m1=cbch+sbspsh mov eax,esi ;get cbch fixmul sinp add eax,ecx ;add sbsh mov [edi].m8,eax ;m8=sbsh+cbchsp mov eax,cosb fixmul sinh mov ecx,eax ;save cbsh fixmul sinp mov ebx,eax mov eax,sinb fixmul cosh mov esi,eax ;save sbch sub ebx,eax mov [edi].m2,ebx ;m2=cbshsp-sbch mov eax,esi ;get sbch fixmul sinp sub eax,ecx ;sub from cbsh mov [edi].m7,eax ;m7=sbchsp-cbsh mov eax,sinh fixmul cosp mov [edi].m3,eax ;m3=shcp mov eax,sinb fixmul cosp mov [edi].m4,eax ;m4=sbcp mov eax,cosb fixmul cosp mov [edi].m5,eax ;m5=cbcp mov eax,sinp neg eax mov [edi].m6,eax ;m6=-sp mov eax,cosh fixmul cosp mov [edi].m9,eax ;m9=chcp popm eax,edx,ebx,ecx,esi ret m2m macro dest,src mov eax,src mov dest,eax endm m2m_neg macro dest,src mov eax,src neg eax mov dest,eax endm ;create a rotation matrix from one or two vectors. ;requires forward vec, and assumes zero bank if up & right vecs==NULL ;up/right vector need not be exactly perpendicular to forward vec ;takes edi=matrix, esi=forward vec, eax=up vec, ebx=right vec. ;returns edi=matrix. trashes eax,ebx,esi ;Note: this routine loses precision as the forward vector approaches ;straigt up or down (I think) vm_vector_2_matrix: pushm ecx ifndef NDEBUG or esi,esi break_if z,"vm_vector_2_matrix: forward vec cannot be NULL!" endif or eax,eax ;up vector present? jnz use_up_vec ;..yep or ebx,ebx ;right vector present? jz just_forward_vec ;..nope ;use_right_vec push edi ;save matrix mov edi,ebx ;save right vec vm_copy zvec,[esi] lea esi,zvec call vm_vec_normalize or ecx,ecx jecxz bad_vector2 lea esi,xvec vm_copy [esi],[edi] call vm_vec_normalize jecxz bad_vector2 lea eax,yvec ;dest = y mov edi,esi ;src1 = x lea esi,zvec ;scr0 = z call vm_vec_crossprod ;get y = z cross x ;normalize new perpendicular vector mov esi,eax ;get new vec (up) in esi call vm_vec_normalize jecxz bad_vector2 ;now recompute right vector, in case it wasn't entirely perpendiclar lea eax,xvec ;dest = x lea edi,zvec call vm_vec_crossprod ;x = y cross z pop edi ;get matrix back jmp copy_into_matrix ;one of the non-forward vectors caused a problem, so ignore them and ;use just the forward vector bad_vector2: pop edi lea esi,zvec jmp just_forward_vec ;use forward and up vectors use_up_vec: push edi ;save matrix mov edi,eax ;save up vec vm_copy zvec,[esi] lea esi,zvec call vm_vec_normalize jecxz bad_vector2 lea esi,yvec vm_copy [esi],[edi] call vm_vec_normalize jecxz bad_vector2 lea eax,xvec ;dest = x lea edi,zvec ;scr0 = y, scr1 = z call vm_vec_crossprod ;get x vector ;normalize new perpendicular vector xchg esi,eax ;get new vec in esi call vm_vec_normalize jecxz bad_vector2 ;bad_vector3 ;now recompute up vector, in case it wasn't entirely perpendiclar xchg esi,edi ;dest = y, src0 = z, src1 = x call vm_vec_crossprod ;get x vector pop edi ;get matrix back copy_into_matrix: vm_copy [edi].rvec,xvec vm_copy [edi].uvec,yvec vm_copy [edi].fvec,zvec jmp done_v2m bad_vector3: pop edi bad_vector: pop ecx debug_brk '0-len vec in vec_2_mat' ret ;only the forward vector is present just_forward_vec: vm_copy zvec,[esi] lea esi,zvec call vm_vec_normalize jecxz bad_vector vm_copy [edi].fvec,[esi] mov eax,[esi].x or eax,[esi].z ;check both x & z == 0 jnz not_up ;forward vector is straight up (or down) mov [edi].m1,f1_0 push edx mov eax,[esi].y ;get y componant cdq ;get y sign mov eax,-f1_0 xor eax,edx sub eax,edx ;make sign correct mov [edi].m8,eax pop edx xor eax,eax mov [edi].m4,eax mov [edi].m7,eax mov [edi].m2,eax mov [edi].m5,eax jmp done_v2m not_up: m2m xvec.x,[esi].z mov xvec.y,0 m2m_neg xvec.z,[esi].x lea esi,xvec call vm_vec_normalize vm_copy [edi].rvec,[esi] push edi ;save matrix mov edi,esi ;scr1 = x lea esi,zvec ;src0 = z lea eax,yvec ;dest = y call vm_vec_crossprod pop edi vm_copy [edi].uvec,[yvec] done_v2m: pop ecx ret if V2MN_ENABLED ;this version requires that the vectors be more-or-less normalized vm_vector_2_matrix_norm: pushm ecx ifndef NDEBUG or esi,esi break_if z,"vm_vector_2_matrix_norm: forward vec cannot be NULL!" endif or eax,eax ;up vector present? jnz _use_up_vec ;..yep or ebx,ebx ;right vector present? jz _just_forward_vec ;..nope ;use_right_vec push edi ;save matrix mov edi,ebx ;save right vec vm_copy zvec,[esi] vm_copy xvec,[edi] lea eax,yvec ;dest = y call vm_vec_crossprod ;get y = z cross x ;normalize new perpendicular vector xchg esi,eax ;get new vec in esi call vm_vec_normalize jecxz _bad_vector2 ;bad_vector3 ;now recompute right vector, in case it wasn't entirely perpendiclar lea eax,xvec ;dest = x lea edi,zvec call vm_vec_crossprod ;x = y cross z pop edi ;get matrix back jmp _copy_into_matrix ;one of the non-forward vectors caused a problem, so ignore them and ;use just the forward vector _bad_vector2: pop edi lea esi,zvec jmp _just_forward_vec ;use forward and up vectors _use_up_vec: push edi ;save matrix mov edi,eax ;save up vec vm_copy zvec,[esi] vm_copy yvec,[edi] lea eax,xvec ;dest = x lea esi,yvec ;scr0 = y lea edi,zvec ;scr1 = z call vm_vec_crossprod ;get x vector ;normalize new perpendicular vector xchg esi,eax ;get new vec in esi call vm_vec_normalize jecxz _bad_vector2 ;bad_vector3 ;now recompute up vector, in case it wasn't entirely perpendiclar xchg esi,edi ;dest = y, src0 = z, src1 = x call vm_vec_crossprod ;get x vector pop edi ;get matrix back _copy_into_matrix: vm_copy [edi].rvec,xvec vm_copy [edi].uvec,yvec vm_copy [edi].fvec,zvec jmp _done_v2m _bad_vector3: pop edi _bad_vector: pop ecx debug_brk '0-len vec in vec_2_mat' ret ;only the forward vector is present _just_forward_vec: vm_copy zvec,[esi] vm_copy [edi].fvec,[esi] mov eax,[esi].x or eax,[esi].z ;check both x & z == 0 jnz _not_up ;forward vector is straight up (or down) mov [edi].m1,f1_0 push edx mov eax,[esi].y ;get y componant cdq ;get y sign mov eax,-f1_0 xor eax,edx sub eax,edx ;make sign correct mov [edi].m8,eax pop edx xor eax,eax mov [edi].m4,eax mov [edi].m7,eax mov [edi].m2,eax mov [edi].m5,eax jmp _done_v2m _not_up: m2m xvec.x,[esi].z mov xvec.y,0 m2m_neg xvec.z,[esi].x lea esi,xvec call vm_vec_normalize vm_copy [edi].rvec,[esi] push edi ;save matrix mov edi,esi ;scr1 = x lea esi,zvec ;src0 = z lea eax,yvec ;dest = y call vm_vec_crossprod pop edi vm_copy [edi].uvec,[yvec] _done_v2m: pop ecx ret endif ;V2MN_ENABLED ;multiply (dot) two vectors. assumes dest ptr in ebp, src pointers in esi,edi. ;trashes ebx,ecx,edx vv_mul macro dest,x0,y0,z0,x1,y1,z1 mov eax,[esi].x0 imul [edi].x1 mov ebx,eax mov ecx,edx mov eax,[esi].y0 imul [edi].y1 add ebx,eax adc ecx,edx mov eax,[esi].z0 imul [edi].z1 add ebx,eax adc ecx,edx shrd ebx,ecx,16 ;fixup ebx mov [ebp].dest,ebx endm ;rotate a vector by a rotation matrix ;eax=dest vector, esi=src vector, edi=matrix. returns eax=dest vector vm_vec_rotate: ifndef NDEBUG cmp eax,esi break_if e,'vec_rotate: dest==src' endif pushm ebx,ecx,edx,ebp mov ebp,eax ;dest in ebp ;compute x vv_mul x, x,y,z, m1,m4,m7 vv_mul y, x,y,z, m2,m5,m8 vv_mul z, x,y,z, m3,m6,m9 mov eax,ebp ;return eax=dest popm ebx,ecx,edx,ebp ret ;transpose a matrix in place. Takes edi=matrix. returns edi=matrix vm_transpose_matrix: push eax mov eax,[edi].m2 xchg eax,[edi].m4 mov [edi].m2,eax mov eax,[edi].m3 xchg eax,[edi].m7 mov [edi].m3,eax mov eax,[edi].m6 xchg eax,[edi].m8 mov [edi].m6,eax pop eax ret ;copy and transpose a matrix. Takes edi=dest, esi=src. returns edi=dest vm_copy_transpose_matrix: push eax mov eax,[esi].m1 mov [edi].m1,eax mov eax,[esi].m2 mov [edi].m4,eax mov eax,[esi].m3 mov [edi].m7,eax mov eax,[esi].m4 mov [edi].m2,eax mov eax,[esi].m5 mov [edi].m5,eax mov eax,[esi].m6 mov [edi].m8,eax mov eax,[esi].m7 mov [edi].m3,eax mov eax,[esi].m8 mov [edi].m6,eax mov eax,[esi].m9 mov [edi].m9,eax pop eax ret ;mulitply 2 matrices, fill in dest. returns eax=ptr to dest ;takes eax=dest, esi=src0, edi=scr1 vm_matrix_x_matrix: ifndef NDEBUG cmp eax,esi break_if e,'matrix_x_matrix: dest==src0' cmp eax,edi break_if e,'matrix_x_matrix: dest==src1' endif pushm ebx,ecx,edx,ebp mov ebp,eax ;ebp=dest ;;This code would do the same as the nine lines below it, but I'm sure ;;Mike would disapprove ;; for s0,<,,> ;; for s1,<,,> ;; vv_mul @ArgI(1,s0)+@ArgI(1,s1), s0, s1 ;; endm ;; endm vv_mul m1, m1,m2,m3, m1,m4,m7 vv_mul m2, m1,m2,m3, m2,m5,m8 vv_mul m3, m1,m2,m3, m3,m6,m9 vv_mul m4, m4,m5,m6, m1,m4,m7 vv_mul m5, m4,m5,m6, m2,m5,m8 vv_mul m6, m4,m5,m6, m3,m6,m9 vv_mul m7, m7,m8,m9, m1,m4,m7 vv_mul m8, m7,m8,m9, m2,m5,m8 vv_mul m9, m7,m8,m9, m3,m6,m9 mov eax,ebp ;eax=ptr to dest popm ebx,ecx,edx,ebp ret ;computes the delta angle between two vectors ;two entry points: normalized and non-normalized vectors ;takes esi,edi=vectors, eax=optional forward vector ;returns ax=delta angle ;if the forward vector is NULL, the absolute values of the delta angle ;is returned. If it is specified, the rotation around that vector from ;esi to edi is returned. vm_vec_delta_ang: push eax ;save forward vec push ecx ;trashed by normalize call vm_vec_normalize xchg esi,edi call vm_vec_normalize xchg esi,edi ;in case forward vec specified pop ecx jmp do_vda_dot vm_vec_delta_ang_norm: push eax ;save forward vec do_vda_dot: call vm_vec_dotprod call fix_acos ;now angle in ax pop ebx ;get forward vec or ebx,ebx ;null? jz done_vda ;..yes ;do cross product to find sign of angle push edx ;save edx push eax ;save angle push ebx ;save forward vec lea eax,tempv0 ;new vec call vm_vec_crossprod ;esi,edi still set mov esi,eax ;new vector pop edi ;forward vec call vm_vec_dotprod ;eax=dotprod cdq ;get sign pop eax ;get angle xor eax,edx sub eax,edx ;make sign correct pop edx ;restore edx done_vda: ret ;compute a rotation matrix from the forward vector and a rotation around ;that vector. takes esi=vector, ax=angle, edi=matrix. returns edi=dest matrix. ;trashes esi,eax vm_vec_ang_2_matrix: pushm eax,edx,ebx,ecx,esi call fix_sincos mov sinb,eax mov cosb,ebx ;extract heading & pitch from vector mov eax,[esi].y ;m6=-sp neg eax mov sinp,eax fixmul eax sub eax,f1_0 neg eax call fix_sqrt ;eax=cp mov cosp,eax mov ebx,eax mov eax,[esi].x ;sh fixdiv ebx mov sinh,eax mov eax,[esi].z ;ch fixdiv ebx mov cosh,eax jmp sincos_2_matrix ;compute the distance from a point to a plane. takes the normalized normal ;of the plane (ebx), a point on the plane (edi), and the point to check (esi). ;returns distance in eax ;distance is signed, so negative dist is on the back of the plane vm_dist_to_plane: pushm esi,edi lea eax,tempv0 call vm_vec_sub ;vecs in esi,edi mov esi,eax ;vector plane -> point mov edi,ebx ;normal call vm_vec_dotprod popm esi,edi ret ;extract the angles from a matrix. takes esi=matrix, fills in edi=angvec vm_extract_angles_matrix: pushm eax,ebx,edx,ecx ;extract heading & pitch from forward vector mov eax,[esi].fvec.z ;ch mov ebx,[esi].fvec.x ;sh mov ecx,ebx ;check for z==x==0 or ecx,eax jz zero_head ;zero, use head=0 call fix_atan2 zero_head: mov [edi].head,ax ;save heading call fix_sincos ;get back sh push eax ;save sine abs_eax mov ecx,eax ;save abs(sine) mov eax,ebx abs_eax ;abs(cos) cmp eax,ecx ;which is larger? pop eax ;get sine back jg use_cos ;sine is larger, so use it mov ebx,eax ;ebx=sine heading mov eax,[esi].m3 ;cp = shcp / sh jmp get_cosp ;cosine is larger, so use it use_cos: mov eax,[esi].fvec.z ;get chcp get_cosp: fixdiv ebx ;cp = chcp / ch push eax ;save cp ;eax = x (cos p) mov ebx,[esi].fvec.y ;fvec.y = -sp neg ebx ;ebx = y (sin) mov ecx,ebx ;check for z==x==0 or ecx,eax jz zero_pitch ;bogus vec, set p=0 call fix_atan2 zero_pitch: mov [edi].pitch,ax pop ecx ;get cp jecxz cp_zero mov eax,[esi].m4 ;m4 = sbcp fixdiv ecx ;get sb mov ebx,eax ;save sb mov eax,[esi].m5 ;get cbcp fixdiv ecx ;get cb mov ecx,ebx ;check for z==x==0 or ecx,eax jz zero_bank ;bogus vec, set n=0 call fix_atan2 zero_bank: mov [edi].bank,ax m_extract_done: popm eax,ebx,edx,ecx ret ;the cosine of pitch is zero. we're pitched straight up. say no bank cp_zero: mov [edi].bank,0 ;no bank popm eax,ebx,edx,ecx ret ;extract the angles from a vector, assuming zero bank. ;takes esi=vec, edi=angvec ;note versions for normalized and not normalized vector ;unnormalized version TRASHES ESI vm_extract_angles_vector: push edi lea edi,tempv0 call vm_vec_copy_normalize ;ecx=mag mov esi,edi pop edi jecxz extract_done vm_extract_angles_vector_normalized: pushm eax,ebx mov [edi].bank,0 ;always zero bank mov eax,[esi].y neg eax call fix_asin mov [edi].pitch,ax ;p = asin(-y) mov eax,[esi].z mov ebx,[esi].x or ebx,eax jz zero_head2 ;check for up vector mov ebx,[esi].x ;get x again call fix_atan2 zero_head2: mov [edi].head,ax ;h = atan2(x,z) (or zero) popm eax,ebx extract_done: ret _TEXT ends end