
--
-- Copyright (C) 2023  <fastrgv@gmail.com>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You may read the full text of the GNU General Public License
-- at <http://www.gnu.org/licenses/>.
--


with system;
with gl, gl.binding, gl.pointers;
with glu, glu.binding, glu.pointers;
with glext, glext.binding, glext.pointers;

with interfaces.c;
use type interfaces.c.unsigned_short;

with ada.numerics.generic_elementary_functions;

with ada.finalization;
with unchecked_deallocation;

with text_io;  use text_io;
with matutils; use matutils;


package body hemi2obj is


procedure initialize( ct: in out circtex ) is
begin
	ct.vert := new varray;
	ct.norm := new varray;
	ct.txuv := new tarray;
	ct.elem := new earray;
end initialize;

procedure vfree is new unchecked_deallocation(varray,vap);
procedure tfree is new unchecked_deallocation(tarray,tap);
procedure efree is new unchecked_deallocation(earray,eap);

procedure finalize( ct: in out circtex ) is
begin
	vfree( ct.vert );
	vfree( ct.norm );
	tfree( ct.txuv );
	efree( ct.elem );
	--text_io.put_line("hemi Free");
end finalize;







	package fmath is new
			ada.Numerics.generic_elementary_functions( float );
	--use fmath;


  onepi : constant float     := 3.14159_26535_89793;
  twopi : constant float     := onepi*2.0;









procedure assert( condition : boolean;  flag: integer:=0 ) is
begin
  if condition=false then
  		put("ASSERTION Failed!  ");
		if flag /= 0 then
			put_line( "@ " & integer'image(flag) );
		end if;
		new_line;
  		raise program_error;
  end if;
end assert;


-- (x/rx)**2 + (y/ry)**2 + (z/rz)**2 = 1
function yht(rx,ry,rz, x,z: float) return float is
	xx : float := x*x/rx/rx;
	zz : float := z*z/rz/rz;
	arg : float := 1.0 - xx - zz;
begin
	if arg>0.0 then
		return ry*fmath.sqrt( arg );
	else
		return 0.0;
	end if;
end yht;



procedure setround( ct: in out circtex;  rx,ry,rz : float ) is

	da : constant float := twopi/float(nang);
	drx : constant float := rx/float(nrad);
	drz : constant float := rz/float(nrad);

	ang0, ang1, rix, riz, rox, roz, x00,z00, x01,z01, x10,z10, x11,z11 : float;
	fi,fj : float;
	ejj : Interfaces.C.unsigned_short;

	ax,ay,az,bx,by,bz,nx,ny,nz: float;

begin

	ct.nv:=0;
	ct.tk:=0;
	ct.ej:=0;
	ejj:=0;

	for i in 0..nang-1 loop
	fi := float(i);
	for j in 0..nrad-1 loop
	fj := float(j);

		ang0 := fi*da;
		ang1 := ang0+da;

		rix := fj*drx;
		riz := fj*drz;
		rox := rix + drx;
		roz := riz + drz;

		x00 := rix*fmath.sin(ang0);
		z00 := riz*fmath.cos(ang0);
		x01 := rix*fmath.sin(ang1);
		z01 := riz*fmath.cos(ang1);
		x10 := rox*fmath.sin(ang0);
		z10 := roz*fmath.cos(ang0);
		x11 := rox*fmath.sin(ang1);
		z11 := roz*fmath.cos(ang1);


		ct.vert(ct.nv+1):=x00;
		ct.vert(ct.nv+2):=yht(rx,ry,rz,x00,z00);
		ct.vert(ct.nv+3):=z00;

		ct.vert(ct.nv+4):=x10;
		ct.vert(ct.nv+5):=yht(rx,ry,rz,x10,z10);
		ct.vert(ct.nv+6):=z10;

		ct.vert(ct.nv+7):=x11;
		ct.vert(ct.nv+8):=yht(rx,ry,rz,x11,z11);
		ct.vert(ct.nv+9):=z11;

		ct.vert(ct.nv+10):=x01;
		ct.vert(ct.nv+11):=yht(rx,ry,rz,x01,z01);
		ct.vert(ct.nv+12):=z01;

-------- begin insert to define normals ---------------------
		ax:=ct.vert(ct.nv+1)-ct.vert(ct.nv+4);
		ay:=ct.vert(ct.nv+2)-ct.vert(ct.nv+5);
		az:=ct.vert(ct.nv+3)-ct.vert(ct.nv+6);
		bx:=ct.vert(ct.nv+7)-ct.vert(ct.nv+4);
		by:=ct.vert(ct.nv+8)-ct.vert(ct.nv+5);
		bz:=ct.vert(ct.nv+9)-ct.vert(ct.nv+6);
		cross(ax,ay,az, bx,by,bz, nx,ny,nz);
		normalize(nx,ny,nz);
		----------------------------------------------------------
		ct.norm(ct.nv+ 1):=nx;  ct.norm(ct.nv+ 2):=ny;  ct.norm(ct.nv+ 3):=nz;
		ct.norm(ct.nv+ 4):=nx;  ct.norm(ct.nv+ 5):=ny;  ct.norm(ct.nv+ 6):=nz;
		ct.norm(ct.nv+ 7):=nx;  ct.norm(ct.nv+ 8):=ny;  ct.norm(ct.nv+ 9):=nz;
		ct.norm(ct.nv+10):=nx;  ct.norm(ct.nv+11):=ny;  ct.norm(ct.nv+12):=nz;
-------- end insert ---------------------------------------

		ct.nv:=ct.nv+12;


	--// the texture coordinates match the rectangular coordinates (x,z)
	-- except here we want (0,0)==>(1/2,1/2)
		ct.txuv(ct.tk+1):=(x00/rx+1.0)*0.5;  ct.txuv(ct.tk+2):=(z00/rz+1.0)*0.5;
		ct.txuv(ct.tk+3):=(x10/rx+1.0)*0.5;  ct.txuv(ct.tk+4):=(z10/rz+1.0)*0.5;
		ct.txuv(ct.tk+5):=(x11/rx+1.0)*0.5;  ct.txuv(ct.tk+6):=(z11/rz+1.0)*0.5;
		ct.txuv(ct.tk+7):=(x01/rx+1.0)*0.5;  ct.txuv(ct.tk+8):=(z01/rz+1.0)*0.5;
		ct.tk := ct.tk+8;


		ct.elem(ct.ej+1):=ejj+0;
		ct.elem(ct.ej+2):=ejj+1;
		ct.elem(ct.ej+3):=ejj+2;
		ct.elem(ct.ej+4):=ejj+2;
		ct.elem(ct.ej+5):=ejj+3;
		ct.elem(ct.ej+6):=ejj+0;
		ct.ej := ct.ej+6;
		ejj := ejj+4;


	end loop; --j
	end loop; --i

	assert( ct.nv<=nvert );
	assert( ct.tk<=nuv );
	assert( ct.ej<=nelm );

	ct.xcen:=0.0;
	ct.ycen:=0.0;
	ct.zcen:=0.0;
	ct.acen:=0.0;

end setround;




procedure setplace( ct: in out circtex;  cx,cy,cz, ang : float ) is
	da : constant float := ang-ct.acen;
	ox,oz, nx,nz : float;
	j: integer;
begin

	--move back to origin
	for i in 1..ct.nv/3 loop
		j:=3*(i-1);
		ct.vert(j+1) := ct.vert(j+1)-ct.xcen;
		ct.vert(j+2) := ct.vert(j+2)-ct.ycen;
		ct.vert(j+3) := ct.vert(j+3)-ct.zcen;
	end loop; --i

	-- rotate
	for i in 1..ct.nv/3 loop
		j:=3*(i-1);

		ox := ct.vert(j+1);
		oz := ct.vert(j+3);

		nx := fmath.cos(da)*ox - fmath.sin(da)*oz;
		nz := fmath.sin(da)*ox + fmath.cos(da)*oz;

		ct.vert(j+1) := nx;
		ct.vert(j+3) := nz;

	end loop; --i

	-- move to new center
	for i in 1..ct.nv/3 loop
		j:=3*(i-1);
		ct.vert(j+1) := ct.vert(j+1)+cx;
		ct.vert(j+2) := ct.vert(j+2)+cy;
		ct.vert(j+3) := ct.vert(j+3)+cz;
	end loop; --i

	-- update stored centers
	ct.xcen:=cx;
	ct.ycen:=cy;
	ct.zcen:=cz;
	ct.acen:=ang;

end setplace;




use gl;
use glext;
use glext.binding;
use gl.binding;

procedure draw( ct: circtex;  vertbuff, uvbuff, elembuff : gluint ) is
begin

	-- 0th attribute:  vertices
	glBindBuffer(gl_array_buffer, vertbuff);
	glBufferData(gl_array_buffer, glsizeiptr(4*ct.nv), ct.vert(1)'address, gl_static_draw);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0,3,gl_float,gl_false,0, system.null_address);

	-- 1st attribute:  texture UV
	glBindBuffer(gl_array_buffer, uvbuff);
	glBufferData(gl_array_buffer, glsizeiptr(4*ct.tk), ct.txuv(1)'address, gl_static_draw);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1,2,gl_float,gl_false,0, system.null_address);

	-- element indices:
	glBindBuffer(gl_element_array_buffer, elembuff);
	glBufferData(gl_element_array_buffer, glsizeiptr(2*ct.ej), ct.elem(1)'address, gl_static_draw);

	glEnable(gl_blend);
	glBlendFunc(gl_src_alpha, gl_one_minus_src_alpha);

	glDrawElements( gl_triangles, glint(ct.nv), gl_unsigned_short, system.null_address );

	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);

end draw;



procedure ldraw( ct: circtex;  vertbuff, uvbuff, normbuff, elembuff : gluint ) is
begin

	-- 0th attribute:  vertices
	glBindBuffer(gl_array_buffer, vertbuff);
	glBufferData(gl_array_buffer, glsizeiptr(4*ct.nv), ct.vert(1)'address, gl_static_draw);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0,3,gl_float,gl_false,0, system.null_address);

	-- 1st attribute:  texture UV
	glBindBuffer(gl_array_buffer, uvbuff);
	glBufferData(gl_array_buffer, glsizeiptr(4*ct.tk), ct.txuv(1)'address, gl_static_draw);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1,2,gl_float,gl_false,0, system.null_address);

	-- 2nd attribute:  normals
	glBindBuffer(gl_array_buffer, normbuff);
	glBufferData(gl_array_buffer, glsizeiptr(4*ct.nv), ct.norm(1)'address, gl_static_draw);
	glEnableVertexAttribArray(2);
	glVertexAttribPointer(2,3,gl_float,gl_false,0, system.null_address);

	-- element indices:
	glBindBuffer(gl_element_array_buffer, elembuff);
	glBufferData(gl_element_array_buffer, glsizeiptr(2*ct.ej), ct.elem(1)'address, gl_static_draw);

	glEnable(gl_blend);
	glBlendFunc(gl_src_alpha, gl_one_minus_src_alpha);

	glDrawElements( gl_triangles, glint(ct.nv), gl_unsigned_short, system.null_address );

	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);
	glDisableVertexAttribArray(2);

end ldraw;




end hemi2obj;

