//Infinite 2 - 3D
unit i2_3d;
interface
uses CoreX, classes;

type

tVertex = packed record
  pos      : tvec3f;
  norm     : tvec3f;
  texcoord : tvec2f;
  meshID     : integer;
  procedure parseVert( fs : string; fl : tstringlist );
end;

tVertArray = array of tVertex;

tTri = record
//  verts : array[0..2] of integer;
  texID : integer;
//  procedure parseTri( fs : string; fl : tstringlist );
end;

TTriarray = array of TTri;

tMeshContainer = record
  verts : tVertArray;
  meshes : array of string[16];
  tris : tTriarray;
  buff : TMeshBuffer;
  textures : array of string;
  matrix : tmat4f;
//  name : string[16];
  procedure Clear;
  procedure Load( filename : string );
  function addTri : integer;
  procedure Draw; overload;
  procedure Draw( fM : tmat4f ); overload;
    procedure Dump;
  private
    function FindOrAddTex(fName: string): integer;
end;

//tMeshObject = class(TResObject)
//  meshes : array of tMeshContainer;

// end;

//индекс первого символа в следующей строке
function nextStrRow( currentPos : integer; fs : string ) : integer;

//для быстрой отрисовки квада на экране
procedure HUDQuad( x, y : real; height, width : real; tleft : real = 0; ttop : real = 1; tright : real = 1; tbottom : real = 0; centered : boolean = true );
procedure Quad3D( x, y, z : real; height, width : real; frotation : real = 0 );

//этой функции скорее всего очень не хватает в CoreX :)
function Triangle( fA, fB, fC : tvec3f ) : TTriangle;

//ищет текстуру
//function FindOrAddTex( fName : string ) : integer;

implementation
//uses main;

//ищет текстуру
function tMeshContainer.FindOrAddTex( fName : string ) : integer;
var
  I: Integer;
  CXName : string;
begin
result := -1;

CXName := copy( fName, 0, pos('.',fname) - 1);
  for I := 0 to length( textures ) - 1 do
  if textures[i] = CXName then
    begin
      result := i;
      exit;
    end;

if result = -1 then
begin
  setlength( textures, length( textures ) + 1 );
  textures[length(textures) - 1 ] := CXName;//TTexture.Load(CXname);
  result := length(textures) - 1;
end;
end;

//индекс первого символа в следующей строке
function nextStrRow( currentPos : integer; fs : string ) : integer;
var
  I: longint;
begin
result := currentpos;
  for I := currentPos to length(fs) do
  begin
      if (fs[i]=#13) and (fs[i+1]=#10) then
      begin
        result := i+2;
        break;
      end;
  end;
end;

//этой функции скорее всего очень не хватает в CoreX :)
function Triangle( fA, fB, fC : tvec3f ) : TTriangle;
begin
  result.A := fA;
  result.B := fB;
  result.C := fC;
end;

//для быстрой отрисовки квада на экране
procedure HUDQuad( x, y : real; height, width : real; tleft : real = 0; ttop : real = 1; tright : real = 1; tbottom : real = 0; centered : boolean = true );
var
halfH,
halfW : real;
begin
halfH := height / 2;
halfW := width / 2;

with gl do
begin
if centered then
begin
  Beginp(GL_QUADS);
    TexCoord2f( tleft, tbottom ); Vertex3f( x - halfW, y - halfH, 0 );
    TexCoord2f( tright, tbottom ); Vertex3f( x + halfW, y - halfH, 0 );
    TexCoord2f( tright, ttop ); Vertex3f( x + halfW, y + halfH, 0 );
    TexCoord2f( tleft, ttop ); Vertex3f( x - halfW, y + halfH, 0 );
  Endp;
end
else
begin
  Beginp(GL_QUADS);
    TexCoord2f( tleft, tbottom ); Vertex3f( x , y - halfH, 0 );
    TexCoord2f( tright, tbottom ); Vertex3f( x + width, y - halfH, 0 );
    TexCoord2f( tright, ttop ); Vertex3f( x + width, y + halfH, 0 );
    TexCoord2f( tleft, ttop ); Vertex3f( x, y + halfH, 0 );
  Endp;
end

end;
end;

procedure Quad3D( x, y, z : real; height, width : real; frotation : real = 0 );
var
halfH,
halfW : real;
m : TMat4f;
begin
halfH := height / 2;
halfW := width / 2;

m.Identity;
m.Pos := vec3f(x,y,z);
m.Rot := render.Camera.ViewMatrix.inverse.Rot;
m.Rotate(frotation,vec3f(0,0,1));
//m.Rotate(-90*deg2rad,vec3f(1,0,0));
with gl do
begin

PushMatrix;
MultMatrixf(m);
//Color3f(1,1,1);
  Beginp(GL_QUADS);
  Normal3f(0,0,1);  TexCoord2f( 0, 0 ); Vertex3f(  - halfW,  - halfH, 0 );
  Normal3f(0,0,1);  TexCoord2f( 1, 0 ); Vertex3f(  + halfW,  - halfH, 0 );
  Normal3f(0,0,1);  TexCoord2f( 1, 1 ); Vertex3f(  + halfW,  + halfH, 0 );
  Normal3f(0,0,1);  TexCoord2f( 0, 1 ); Vertex3f(  - halfW,  + halfH, 0 );
  Endp;
PopMatrix;
end;
end;

{ tMeshContainer }

function tMeshContainer.addTri: integer;
begin
  setlength( tris, length( tris ) + 1 );
  setlength( verts, length( tris ) * 3 );
  result := length( tris ) - 1;
end;

procedure tMeshContainer.Clear;
var
  I: Integer;
begin
  tris := nil;
  verts := nil;
  meshes := nil;
  for I := 0 to length(textures) - 1 do
    ttexture.load(textures[i]).free;
  textures := nil;
  if buff <> nil then buff.Destroy;
  matrix.Identity;
end;

procedure tMeshContainer.Draw;
var
  I, J, First, Last: Integer;
  V : ^tvertex;
  m : tmat4f;
begin
gl.PushMatrix;

m := mat4f( deg2rad * -90, vec3f( 1, 0, 0 ) );//компенсируем триаду макса

gl.MultMatrixf(matrix * m);

buff.Bind;
V := buff.DataPtr;

gl.VertexPointer(3, GL_FLOAT, buff.Stride , @V^.pos);
gl.NormalPointer( GL_FLOAT, buff.Stride , @V^.norm );
gl.TexCoordPointer(2, GL_FLOAT, buff.Stride , @V^.texcoord );

for I := 0 to length(textures) - 1 do
  begin

First := -1;
Last := length(tris);

  for j := 0 to length(tris) - 1 do
    begin
      if (tris[j].texID = I) and (first = -1) then first := J;
      if (first <> -1) and (tris[j].texID <> i) then
      begin
        Last := J ;
        break;
      end;

    end;//for j

    ttexture.load(textures[i]).Bind;

gl.DrawArrays(gl_triangles, first*3, last*3-first*3 );
{gl.Beginp(gl_triangles);
for J := first to last - 1 do
  begin
    gl.TexCoord2fv(verts[j*3].texcoord); gl.Vertex3fv(verts[j*3].pos); gl.Normal3fv(verts[j*3].norm);
    gl.TexCoord2fv(verts[j*3+1].texcoord); gl.Vertex3fv(verts[j*3+1].pos); gl.Normal3fv(verts[j*3+1].norm);
    gl.TexCoord2fv(verts[j*3+2].texcoord); gl.Vertex3fv(verts[j*3+2].pos); gl.Normal3fv(verts[j*3+2].norm);
  end;
gl.Endp;}

  end;//for i

gl.PopMatrix;

end;

procedure tMeshContainer.Draw(fM: tmat4f);
var
  I, J, First, Last: Integer;
  V : ^tvertex;
  m : tmat4f;
begin
render.BlendType := btNone;
gl.PushMatrix;

m := mat4f( deg2rad * -90, vec3f( 1, 0, 0 ) );//компенсируем триаду макса

m.pos := m.pos + fm.pos;

gl.MultMatrixf(matrix * m);

buff.Bind;
V := buff.DataPtr;

gl.VertexPointer(3, GL_FLOAT, buff.Stride , @V^.pos);
gl.NormalPointer( GL_FLOAT, buff.Stride , @V^.norm );
gl.TexCoordPointer(2, GL_FLOAT, buff.Stride , @V^.texcoord );

//gl.e
//render.Mode := rmShadow;
//render.BlendType := btMult;
//ttexture.Load('litemap').Bind(1);

for I := 0 to length(textures) - 1 do
  begin

First := -1;
Last := length(tris);

  for j := 0 to length(tris) - 1 do
    begin
      if (tris[j].texID = I) and (first = -1) then first := J;
      if (first <> -1) and (tris[j].texID <> i) then
      begin
        Last := J ;
        break;
      end;

    end;//for j

    ttexture.load(textures[i]).Bind;
//    ttexture.Load(textures[9]).bind;
//if i=1 then

gl.DrawArrays(gl_triangles, first*3, last*3-first*3 );
gl.Beginp(gl_triangles);
for J := first to last - 1 do
  begin
    gl.TexCoord2fv(verts[j*3].texcoord); gl.Vertex3fv(verts[j*3].pos); gl.Normal3fv(verts[j*3].norm);
    gl.TexCoord2fv(verts[j*3+1].texcoord); gl.Vertex3fv(verts[j*3+1].pos); gl.Normal3fv(verts[j*3+1].norm);
    gl.TexCoord2fv(verts[j*3+2].texcoord); gl.Vertex3fv(verts[j*3+2].pos); gl.Normal3fv(verts[j*3+2].norm);
  end;
gl.Endp;

  end;//for i

gl.PopMatrix;

end;

procedure tMeshContainer.Dump;
var
f : tstringlist;
I: Integer;
s : string;
begin
f := TStringList.Create;
for I := 0 to length(tris) - 1 do
begin
  s := '';
  s := s + textures[tris[i].texID];
  s := s + ' ' + conv( i );
  s := s + ' ' + conv( i * 3 );
  f.Add(s);
end;
f.SaveToFile('MeshDump.txt');
f.Free;
end;

procedure tMeshContainer.Load( filename : string );
var
whole_file, piece : tstringlist;
i, j, id, tid, s, e: longint;
tri_swap : ttriarray;
vert_swap : tVertArray;
begin
clear;
whole_file := TStringList.Create;
piece := TStringList.Create;

whole_file.LoadFromFile( filename );
s := whole_file.IndexOf('nodes');
e := whole_file.IndexOf('end');

setlength( meshes, e - s - 1 );
id := 0;
for I := s + 1 to e - 1 do
begin
  piece.CommaText := whole_file.Strings[i];
  meshes[id] := piece[1];
  id := id + 1;
end;

s := whole_file.IndexOf('triangles') + 1;
e := whole_file.Count - 1;

i := s;
tid := length( textures ) - 1;
repeat
  id := addTri;
  tris[id].texID := FindOrAddTex( whole_file[i] );
  verts[id*3].parseVert(whole_file[i+1],piece);
  verts[id*3+1].parseVert(whole_file[i+2],piece);
  verts[id*3+2].parseVert(whole_file[i+3],piece);
i := i + 4;
until i >= e;

//sort by texture
setlength( tri_swap, length( tris ) );
setlength( vert_swap, length( verts ) );
id := 0;
for J := 0 to length(textures) - 1 do
  for I := 0 to length(tris) - 1 do
  begin
    if tris[i].texID = J then
      begin
        tri_swap[id] := tris[i];
        vert_swap[id*3] := verts[i*3];
        vert_swap[id*3+1] := verts[i*3+1];
        vert_swap[id*3+2] := verts[i*3+2];
        inc(id);
      end;

  end;
tris := tri_swap;
verts := vert_swap;

buff := TMeshBuffer.Create( btVertex, length(verts), sizeof(tvertex),@verts[0]);
whole_file.Free;
piece.Free;
//copy(tri_swap,0,length(tri_swap) - 1);
tri_swap := nil;
vert_swap := nil;
end;

{ tVertex }

procedure tVertex.parseVert( fs : string; fl : tstringlist );
begin
//  0 -5.000000 -1.500000 0.000000 0.000000 0.000000 1.000000 0.003644 0.003108 1 0 1.000000
fl.CommaText := fs;
meshID := conv( fl[0], 0 );
pos := vec3f( conv( fl[1], 0.0 ), conv( fl[2], 0.0 ), conv( fl[3], 0.0 ) );
norm := vec3f( conv( fl[4], 0.0 ), conv( fl[5], 0.0 ), conv( fl[6], 0.0 ) );
{if norm.x = -0 then norm.x := 0;
if norm.y = -0 then norm.y := 0;
if norm.z = -0 then norm.z := 0;}
texcoord := vec2f( conv( fl[7], 0.0 ), conv( fl[8], 0.0 ) );
end;

end.
