; Some simple mathematical functions. Don't look for some logic in
; the function names :-)

; ******* Gestion des matrices 4x4 ******

if DEBUG
f_m4m db 'gl_M4_Mul',0
f_m4ml db 'gl_M4_MulLeft',0
end if

align 4
proc gl_M4_Id uses eax ecx edi, a:dword
	mov edi,[a]
	add edi,4
	mov ecx,14
	mov eax,0.0
	rep stosd
	mov eax,1.0
	stosd
	mov edi,[a]
	stosd
	add edi,16
	stosd
	add edi,16
	stosd
	ret
endp

align 4
proc gl_M4_IsId uses ebx ecx, a:dword
	mov eax,[a]
	xor ebx,ebx
	xor ecx,ecx
	.cycle_01:
		fld dword[eax]
		cmp ecx,ebx
		je .once
			ftst ;ñðàâíåíèå ñ 0.0
			fstsw ax
			sahf
			je @f
			jmp .not_1 ;åñëè äèàãîíàëüíûå ÷èñëà íå ðàâíû 0.0 ìàòðèöà íå åäèíè÷íàÿ
		.once:
			fld1
			fcomp st1 ;ñðàâíåíèå ñ 1.0
			fstsw ax
			test ah,0x40
			je .not_1 ;åñëè íå ðàâíî 1.0 ìàòðèöà íå åäèíè÷íàÿ
		@@:
		ffree st0
		fincstp
		add eax,4
		inc ebx
		btr ebx,2
		jnc .cycle_01
	inc ecx
	bt ecx,2 ;ïðîâåðÿåì ðàâåíñòâî ecx==4
	jnc .cycle_01

	mov eax,1
	jmp @f
	.not_1:
		ffree st0
		fincstp
		xor eax,eax
	@@:
	ret
endp

align 4
proc gl_M4_Mul, c:dword,a:dword,b:dword
pushad
	mov edx,[c]
	xor eax,eax
	.cycle_0: ;i
		xor ebx,ebx
		.cycle_1: ;j
			fldz ;sum=0
			xor ecx,ecx
			M4_reg edi,[a],eax,0
			.cycle_2: ;k
				fld dword[edi]
				add edi,4
				M4_reg esi,[b],ecx,ebx
				fmul dword[esi]
				faddp ;sum += a[i][k] * b[k][j]
				inc ecx
				cmp ecx,4
				jl .cycle_2
			fstp dword[edx] ;c[i][j] = sum
			add edx,4
			inc ebx
			cmp ebx,4
			jl .cycle_1
		inc eax
		cmp eax,4
		jl .cycle_0
if DEBUG ;gl_M4_Mul
	stdcall dbg_print,f_m4m,txt_nl
	stdcall gl_print_matrix,[c],4
	stdcall dbg_print,txt_sp,txt_nl
end if
popad
	ret
endp

; c=c*a
align 4
proc gl_M4_MulLeft, c:dword,b:dword
locals
	i dd ?
	a M4
endl
pushad
	mov ecx,16
	mov esi,[c]
	mov edi,ebp
	sub edi,sizeof.M4
	rep movsd ;êîïèðîâàíèå ìàòðèö [a]=[c]

	mov edx,[c]
	mov dword[i],0
	mov eax,ebp
	sub eax,sizeof.M4
	.cycle_0: ;i
		xor ebx,ebx ;j=0
		.cycle_1: ;j
			fldz ;sum=0
			xor ecx,ecx ;k=0
			M4_reg edi,eax,dword[i],0
			.cycle_2: ;k
				fld dword[edi]
				add edi,4
				M4_reg esi,[b],ecx,ebx
				fmul dword[esi]
				faddp ;sum += a[i][k] * b[k][j]
				inc ecx
				cmp ecx,4
				jl .cycle_2
			fstp dword[edx] ;c[i][j] = sum
			add edx,4
			inc ebx
			cmp ebx,4
			jl .cycle_1
		inc dword[i]
		cmp dword[i],4
		jl .cycle_0
if DEBUG ;gl_M4_MulLeft
	stdcall dbg_print,f_m4ml,txt_nl
	stdcall gl_print_matrix,[c],4
	stdcall dbg_print,txt_sp,txt_nl
end if
popad
	ret
endp

align 4
proc gl_M4_Move uses ecx edi esi, a:dword,b:dword
	mov edi,[a]
	mov esi,[b]
	mov ecx,sizeof.M4/4
	rep movsd
	ret
endp

align 4
proc gl_MoveV3 uses edi esi, a:dword,b:dword
	mov edi,[a]
	mov esi,[b]
	movsd
	movsd
	movsd
	ret
endp

;void gl_MulM4V3(V3 *a,M4 *b,V3 *c)
;{
;        a->X=b->m[0][0]*c->X+b->m[0][1]*c->Y+b->m[0][2]*c->Z+b->m[0][3];
;        a->Y=b->m[1][0]*c->X+b->m[1][1]*c->Y+b->m[1][2]*c->Z+b->m[1][3];
;        a->Z=b->m[2][0]*c->X+b->m[2][1]*c->Y+b->m[2][2]*c->Z+b->m[2][3];
;}

;void gl_MulM3V3(V3 *a,M4 *b,V3 *c)
;{
;        a->X=b->m[0][0]*c->X+b->m[0][1]*c->Y+b->m[0][2]*c->Z;
;        a->Y=b->m[1][0]*c->X+b->m[1][1]*c->Y+b->m[1][2]*c->Z;
;        a->Z=b->m[2][0]*c->X+b->m[2][1]*c->Y+b->m[2][2]*c->Z;
;}

align 4
proc gl_M4_MulV4 uses ebx ecx edx, a:dword, b:dword, c:dword ;V4 *a, M4 *b, V4 *c
	mov ebx,[b]
	mov edx,[c]
	fld dword[edx]
	fld dword[edx+4]
	fld dword[edx+8]
	fld dword[edx+12]
	mov edx,[a]
	mov ecx,4
	.cycle_1:
		fld dword[ebx]    ;st0 = m[_][0]
		fmul st0,st4      ;st0 *= c.X
		fld dword[ebx+4]  ;st0 = m[_][1]
		fmul st0,st4      ;st0 *= c.Y
		faddp
		fld dword[ebx+8]  ;st0 = m[_][2]
		fmul st0,st3      ;st0 *= c.Z
		faddp
		fld dword[ebx+12] ;st0 += m[_][3]
		fmul st0,st2      ;st0 *= c.Z
		faddp
		fstp dword[edx]   ;a.X = b.m[_][0]*c.X +b.m[_][1]*c.Y +b.m[_][2]*c.Z +b.m[_][3]*c.W
		add ebx,16 ;ñëåäóùàÿ ñòðîêà ìàòðèöû
		add edx,4  ;ñëåäóùàÿ êîîðäèíàòà âåêòîðà
	loop .cycle_1
	ffree st0
	fincstp
	ffree st0
	fincstp
	ffree st0
	fincstp
	ffree st0
	fincstp
	ret
endp

; transposition of a 4x4 matrix
align 4
proc gl_M4_Transpose uses eax ecx edx, a:dword, b:dword
	mov eax,[a]
	mov ecx,[b]

	mov edx,[ecx]
	mov [eax],edx
	mov edx,[ecx+0x10]
	mov [eax+0x4],edx
	mov edx,[ecx+0x20]
	mov [eax+0x8],edx
	mov edx,[ecx+0x30]
	mov [eax+0x0c],edx

	mov edx,[ecx+0x4]
	mov [eax+0x10],edx
	mov edx,[ecx+0x14]
	mov [eax+0x14],edx
	mov edx,[ecx+0x24]
	mov [eax+0x18],edx
	mov edx,[ecx+0x34]
	mov [eax+0x1c],edx

	mov edx,[ecx+0x8]
	mov [eax+0x20],edx
	mov edx,[ecx+0x18]
	mov [eax+0x24],edx
	mov edx,[ecx+0x28]
	mov [eax+0x28],edx
	mov edx,[ecx+0x38]
	mov [eax+0x2c],edx

	mov edx,[ecx+0x0c]
	mov [eax+0x30],edx
	mov edx,[ecx+0x1c]
	mov [eax+0x34],edx
	mov edx,[ecx+0x2c]
	mov [eax+0x38],edx
	mov edx,[ecx+0x3c]
	mov [eax+0x3c],edx
	ret
endp

; inversion of an orthogonal matrix of type Y=M.X+P
;void gl_M4_InvOrtho(M4 *a,M4 b)
;{
;       int i,j;
;       float s;
;       for(i=0;i<3;i++)
;       for(j=0;j<3;j++) a->m[i][j]=b.m[j][i];
;       a->m[3][0]=0.0; a->m[3][1]=0.0; a->m[3][2]=0.0; a->m[3][3]=1.0;
;       for(i=0;i<3;i++) {
;               s=0;
;               for(j=0;j<3;j++) s-=b.m[j][i]*b.m[j][3];
;               a->m[i][3]=s;
;       }
;}

; Inversion of a general nxn matrix.
; Note : m is destroyed

align 4
proc Matrix_Inv uses ebx ecx edx edi esi, r:dword, m:dword, n:dword ;(float *r,float *m,int n)
locals
	max dd ? ;float
	tmp dd ?
endl

	; identitée dans r
	mov eax,0.0
	mov ecx,[n]
	imul ecx,ecx
	mov edi,[r]
	rep stosd ;for(i=0;i<n*n;i++) r[i]=0
	mov eax,1.0
	xor ebx,ebx
	mov edi,[r]
	mov ecx,[n]
	shl ecx,2
	@@: ;for(i=0;i<n;i++)
		cmp ebx,[n]
		jge .end_0
		stosd ;r[i*n+i]=1
		add edi,ecx
		inc ebx
		jmp @b
	.end_0:

	; ebx -> n
	; ecx -> j
	; edx -> k
	; edi -> i
	; esi -> l
	mov ebx,[n]
	xor ecx,ecx
	.cycle_0: ;for(j=0;j<n;j++)
	cmp ecx,ebx
	jge .cycle_0_end
		; recherche du nombre de plus grand module sur la colonne j
		mov eax,ecx
		imul eax,ebx
		add eax,ecx
		shl eax,2
		add eax,[m]
		mov eax,[eax]
		mov [max],eax ;max=m[j*n+j]
		mov edx,ecx ;k=j
		mov edi,ecx
		inc edi
		.cycle_1: ;for(i=j+1;i<n;i++)
		cmp edi,ebx
		jge .cycle_1_end
			mov eax,edi
			imul eax,ebx
			add eax,ecx
			shl eax,2
			add eax,[m]
			fld dword[eax]
			fcom dword[max] ;if (fabs(m[i*n+j])>fabs(max))
			fstsw ax
			sahf
			jbe @f
				mov edx,edi ;k=i
				fst dword[max]
			@@:
			ffree st0
			fincstp
		inc edi
		jmp .cycle_1
		.cycle_1_end:

		; non intersible matrix
		fld dword[max]
		ftst ;if (max==0)
		fstsw ax
		ffree st0
		fincstp
		sahf
		jne @f
			xor eax,eax
			inc eax
			jmp .end_f ;return 1
		@@:

		; permutation des lignes j et k
		cmp ecx,edx ;if (j!=k)
		je .cycle_2_end
			xor edi,edi
			.cycle_2: ;for(i=0;i<n;i++)
			cmp edi,ebx
			jge .cycle_2_end
				;òóò ïîêà esi != l
				mov eax,ecx
				imul eax,ebx
				add eax,edi
				shl eax,2
				add eax,[m]
				mov esi,[eax]
				mov [tmp],esi ;tmp=m[j*n+i]
				mov esi,edx
				imul esi,ebx
				add esi,edi
				shl esi,2
				add esi,[m]
				m2m dword[eax],dword[esi] ;m[j*n+i]=m[k*n+i]
				mov eax,[tmp]
				mov [esi],eax ;m[k*n+i]=tmp

				mov eax,ecx
				imul eax,ebx
				add eax,edi
				shl eax,2
				add eax,[r]
				mov esi,[eax]
				mov [tmp],esi ;tmp=r[j*n+i]
				mov esi,edx
				imul esi,ebx
				add esi,edi
				shl esi,2
				add esi,[r]
				m2m dword[eax],dword[esi] ;r[j*n+i]=r[k*n+i]
				mov eax,[tmp]
				mov [esi],eax ;r[k*n+i]=tmp
			inc edi
			jmp .cycle_2
		.cycle_2_end:

		; multiplication de la ligne j par 1/max
		fld1
		fdiv dword[max]
		fst dword[max] ;max=1/max
		xor edi,edi
		mov eax,ecx
		imul eax,ebx
		shl eax,2
		.cycle_3: ;for(i=0;i<n;i++)
		cmp edi,ebx
		jge .cycle_3_end
			add eax,[m]
			fld dword[eax]
			fmul st0,st1
			fstp dword[eax] ;m[j*n+i]*=max
			sub eax,[m]
			add eax,[r]
			fld dword[eax]
			fmul st0,st1
			fstp dword[eax] ;r[j*n+i]*=max
			sub eax,[r]
			add eax,4
		inc edi
		jmp .cycle_3
		.cycle_3_end:
		ffree st0 ;max
		fincstp

		xor esi,esi
		.cycle_4: ;for(l=0;l<n;l++)
		cmp esi,ebx
		jge .cycle_4_end
			cmp esi,ecx ;if (l!=j)
			je .if_end
			mov eax,esi
			imul eax,ebx
			add eax,ecx
			shl eax,2
			add eax,[m]
			fld dword[eax] ;t=m[l*n+j]
			xor edi,edi
			.cycle_5: ;for(i=0;i<n;i++)
			cmp edi,ebx
			jge .cycle_5_end
				mov eax,ecx
				imul eax,ebx
				add eax,edi
				shl eax,2
				add eax,[m]
				fld dword[eax]
				fmul st0,st1
				mov eax,esi
				imul eax,ebx
				add eax,edi
				shl eax,2
				add eax,[m]
				fsub dword[eax]
				fchs
				fstp dword[eax] ;m[l*n+i]-=m[j*n+i]*t
				mov eax,ecx
				imul eax,ebx
				add eax,edi
				shl eax,2
				add eax,[r]
				fld dword[eax]
				fmul st0,st1
				mov eax,esi
				imul eax,ebx
				add eax,edi
				shl eax,2
				add eax,[r]
				fsub dword[eax]
				fchs
				fstp dword[eax] ;r[l*n+i]-=r[j*n+i]*t
			inc edi
			jmp .cycle_5
			.cycle_5_end:
			ffree st0 ;t
			fincstp
			.if_end:
		inc esi
		jmp .cycle_4
		.cycle_4_end:
	inc ecx
	jmp .cycle_0
	.cycle_0_end:

	xor eax,eax ;return 0
	.end_f:
	ret
endp

; inversion of a 4x4 matrix

align 4
proc gl_M4_Inv uses eax ecx edi esi, a:dword, b:dword
locals
	tmp M4
endl
	mov esi,[b]
	mov edi,ebp
	sub edi,sizeof.M4 ;edi = &tmp
	mov ecx,16
	rep movsd
	sub edi,sizeof.M4 ;edi = &tmp
	stdcall Matrix_Inv,[a],edi,4 ;ïîðòèò eax ïîòîìó â uses åñòü eax
	ret
endp

align 4
proc gl_M4_Rotate uses eax ecx, a:dword,t:dword,u:dword
locals
	s dd ? ;float
	c dd ? ;float
	v dd ? ;int
	w dd ? ;int
endl
	mov eax,[u]
	inc eax
	cmp eax,2
	jle @f
		xor eax,eax
	@@:
	mov [v],eax
	inc eax
	cmp eax,2
	jle @f
		xor eax,eax
	@@:
	mov [w],eax
	fld dword [t]
	fsin
	fstp dword [s]
	fld dword [t]
	fcos
	fstp dword [c]

	stdcall gl_M4_Id,[a]

	M4_reg ecx,[a],[v],[v]
	mov eax,[c]
	mov [ecx],eax

	M4_reg ecx,[a],[v],[w]
	fld dword [s]
	fchs
	fstp dword [ecx]

	M4_reg ecx,[a],[w],[v]
	mov eax,[s]
	mov [ecx],eax

	M4_reg ecx,[a],[w],[w]
	mov eax,[c]
	mov [ecx],eax

	ret
endp

; inverse of a 3x3 matrix
;void gl_M3_Inv(M3 *a,M3 *m)
;{
;        float det;

;        det = m->m[0][0]*m->m[1][1]*m->m[2][2]-m->m[0][0]*m->m[1][2]*m->m[2][1]-
;                m->m[1][0]*m->m[0][1]*m->m[2][2]+m->m[1][0]*m->m[0][2]*m->m[2][1]+
;                m->m[2][0]*m->m[0][1]*m->m[1][2]-m->m[2][0]*m->m[0][2]*m->m[1][1];

;        a->m[0][0] = (m->m[1][1]*m->m[2][2]-m->m[1][2]*m->m[2][1])/det;
;        a->m[0][1] = -(m->m[0][1]*m->m[2][2]-m->m[0][2]*m->m[2][1])/det;
;        a->m[0][2] = -(-m->m[0][1]*m->m[1][2]+m->m[0][2]*m->m[1][1])/det;
 
;        a->m[1][0] = -(m->m[1][0]*m->m[2][2]-m->m[1][2]*m->m[2][0])/det;
;        a->m[1][1] = (m->m[0][0]*m->m[2][2]-m->m[0][2]*m->m[2][0])/det;
;        a->m[1][2] = -(m->m[0][0]*m->m[1][2]-m->m[0][2]*m->m[1][0])/det;

;        a->m[2][0] = (m->m[1][0]*m->m[2][1]-m->m[1][1]*m->m[2][0])/det;
;        a->m[2][1] = -(m->m[0][0]*m->m[2][1]-m->m[0][1]*m->m[2][0])/det;
;        a->m[2][2] = (m->m[0][0]*m->m[1][1]-m->m[0][1]*m->m[1][0])/det;
;}

; vector arithmetic

align 4
proc gl_V3_Norm uses ebx, a:dword
	mov ebx,[a]
	fld dword[ebx]
	fmul st0,st0
	fld dword[ebx+4]
	fmul st0,st0
	faddp
	fld dword[ebx+8]
	fmul st0,st0
	faddp
	fsqrt ;st0 = sqrt(a.X^2 +a.Y^2 +a.Z^2)
	ftst
	fstsw ax
	sahf
	je .r1 ;if (sqrt(...)==0) return 1
		fld dword[ebx] ;offs_X = 0
		fdiv st0,st1
		fstp dword[ebx] ;a.X/=sqrt(...)
		fld dword[ebx+4]
		fdiv st0,st1
		fstp dword[ebx+4] ;a.Y/=sqrt(...)
		fld dword[ebx+8]
		fdiv st0,st1
		fstp dword[ebx+8] ;a.Z/=sqrt(...)
		xor eax,eax
		jmp @f
	.r1:
		xor eax,eax
		inc eax
	@@:
	ffree st0
	fincstp
	ret
endp

macro gl_V3_New p_mem, x, y, z
{
	mov dword[p_mem],x
	mov dword[p_mem+4],y
	mov dword[p_mem+8],z
}

macro gl_V4_New p_mem, x, y, z, w
{
	mov dword[p_mem],x
	mov dword[p_mem+4],y
	mov dword[p_mem+8],z
	mov dword[p_mem+12],w
}