unit Terrain;

interface

uses
  NewtonImport,
  JEN_Header,
  JEN_Math;

type
  TTerrain = class
  constructor Create;
  private
    FLightPos : TVec3f;
    FCenter : TVec3f;
    FShader : IShaderProgram;
    FPosAttrib : IShaderAttrib;
    FNormalAttrib : IShaderAttrib;
    FMatrixUniform : IShaderUniform;
    FPosUniform : IShaderUniform;
    FLightPosUniform : IShaderUniform;
    FVrtBuff : IGeomBuffer;
    FNormalTex : ITexture;
    FHeightMap : array of array of Single;
    FLandBody : PNewtonBody;
    FVrtCount : Integer;
    FMaxH,FMinH : single;
    procedure GenHeightMap;
  public
    procedure OnDraw;
    procedure OnUpdate(dt: LongInt);
    function GetCenter: Tvec3f;
    function GetLightPos: Tvec3f;
    function GetHeight(X, Y: Single): Single; inline;
  end;

implementation

uses
  Noise,
  Main;

const
   LandW = 140;
   LandH = 140;

constructor TTerrain.Create;
type
  TVertex = record
    p,n : TVec3f;
  end;

var
  i,j,k: LongInt;
  s,c: Single;
  Hex: array[1..8] of TVertex;
  VertxBuff: array of TVertex;
  AttribArray: array of Byte;
  Elevations: array of Word;
  Pos, Pos2: TVec3f;
  Colision: PNewtonCollision;
  m : TMat4f;
  myFile    : File;

  function GetPos(x,y:integer): TVec3f;
  begin
    Result := Vec3f(x*2 +(y mod 2),0,y*2);
    Result.y := GetHeight(Result.x,Result.z);
  end;

begin
  GenHeightMap;

  FCenter := GetPos(LandW div 2 ,LandH div 2);
  OnUpdate(1);

  SetLength(VertxBuff, LandW*LandH*32);
  SetLength(AttribArray, LandW*LandH);
  SetLength(Elevations, LandW*LandH);

  FillChar(AttribArray[0], LandW*LandH, 1);


  for I := 0 to LandW-1 do
    for J := 0 to LandH - 1 do
      Elevations[j + LandW*i] :=Trunc((GetPos(i,j).y-FMinH)/(FMaxH-FMinH)*65534);


  Pos  := GetPos(0,0);
  Pos2 := GetPos(LandW,LandH);
  Pos.y := FMinH;
  Pos2.y := FMaxH+50;

  NewtonSetWorldSize(Game.World, @Pos, @Pos2);
  Colision := NewtonCreateHeightFieldCollision(Game.World, LandW, LandH , 0, @Elevations[0], @AttribArray[0], 2.0, (FMaxH-FMinH)/65534.0, 0);

  Pos := NullVec3f;
  Pos.y := FMinH;
  m.Identity;
  m.e00 :=0;
  m.e20 :=1;
  m.e02 :=1;
  m.e22 :=0;

  m.Pos := Pos;

  FLandBody := NewtonCreateBody(Game.World, Colision, @m);
  NewtonBodySetMassMatrix(FLandBody, 0, 0, 0, 0);
  NewtonReleaseCollision(Game.World, Colision);

  SetLength(AttribArray, 0);
  SetLength(Elevations, 0);

  i := 0;
  while i <= 7 do
  begin
    SinCos(deg2rad*(i-(i div 4))*60, s, c);
    Hex[i+1].p := Vec3f(s/(sqrt(3)/2), 0 ,c*(1+sqrt(3)/4)); Hex[i+1].n := Vec3f((c+1)/2,1,(s+1)/2);
    inc(i);
  end;

  FVrtCount := 0;
  for I := 0 to LandW - 1 do
    for J := 0 to LandH - 1 do
    begin
      Pos := GetPos(i,j);

      Move(Hex, VertxBuff[FVrtCount], 8*24);
      for k := 0 to 7 do
        VertxBuff[FVrtCount+k].p := VertxBuff[FVrtCount+k].p + Pos;
      inc(FVrtCount, 8);

      for k := 0 to 5 do
      begin
        case k of
        0: Pos2 := GetPos(i+j mod 2,j+1);
        1: Pos2 := GetPos(i+1,j);
        2: Pos2 := GetPos(i+j mod 2,j-1);
        3: Pos2 := GetPos(i-(j+1) mod 2,j-1);
        4: Pos2 := GetPos(i-1,j);
        5: Pos2 := GetPos(i-(j+1) mod 2,j+1);
        end;

        if Pos.y <= Pos2.y then continue;
        SinCos(deg2rad*(k*60+30), s, c);

        VertxBuff[FVrtCount].p:= Hex[k + k div 3 +1].p + Pos;
        VertxBuff[FVrtCount].n:= Vec3f(s,0,c);
        inc(FVrtCount);

        VertxBuff[FVrtCount].p:= Hex[k + k div 3 +1].p + Vec3f(Pos.x,Pos2.y,Pos.z);
        VertxBuff[FVrtCount].n:= Vec3f(s,0,c);
        inc(FVrtCount);

        VertxBuff[FVrtCount].p:= Hex[k + k div 3 +2].p + Vec3f(Pos.x,Pos2.y,Pos.z);
        VertxBuff[FVrtCount].n:= Vec3f(s,0,c);
        inc(FVrtCount);

        VertxBuff[FVrtCount].p:= Hex[k + k div 3 +2].p + Pos;
        VertxBuff[FVrtCount].n:= Vec3f(s,0,c);
        inc(FVrtCount);
      end;

    end;

  FVrtBuff := Render.CreateGeomBuffer(gbVertex, FVrtCount, 24, @VertxBuff[0]);

  FNormalTex := ResMan.LoadTexture('Media\Norm.dds');
  FShader := ResMan.LoadShader('Media\Terrain.xml').Compile;
  FPosAttrib := FShader.Attrib('PositionAttr', atVec3f, False);
  FNormalAttrib := FShader.Attrib('NormalAttr', atVec3f, False);
  FMatrixUniform := FShader.Uniform('Jen_ModelViewProjectionMatrix', utMat4);
  FPosUniform := FShader.Uniform('Jen_ViewPos', utVec3);
  FLightPosUniform := FShader.Uniform('Jen_LightPos', utVec3);

  i:=1;
  FShader.Uniform('NormalMap', utInt).Value(i);
end;

procedure TTerrain.GenHeightMap;
var
  i,j,k : Integer;
  frequency : Single;
  len : Single;
begin
  SetLengtH(FHeightMap, LandW, LandH);
  FMaxH := -INF;
  FMinH := INF;
  PSeed := Random(100);

  for i := 0 to LandW - 1 do
    for j := 0 to LandH - 1 do
    begin
      len := (sqr(LandW/2 - i) +sqr(LandH/2 - j));
      FHeightMap[i][j] := CompileNoise(i*0.05 , j*0.05)*15 -( len*len/1000000 - len/80 )/2 + 50;
      FMinH := Min(FMinH, FHeightMap[i][j]);
      FMaxH := Max(FMaxH, FHeightMap[i][j]);
    end;

end;

function TTerrain.GetCenter : TVec3f;
begin
  Result := FCenter;
end;

function TTerrain.GetLightPos: Tvec3f;
begin
  Result := FLightPos;
end;

function TTerrain.GetHeight(X, Y: Single): Single;
var
  v1, v2, v3, v4 : Single;
  intX, intY: Cardinal;
  
    function Lerp(a, b,x: Single) : Single; inline;
      var  ft ,f: Single;
    begin
    ft := x * pi;
    f := (1 - cos(ft)) * 0.5;
    result := a*(1-f) + b*f;
    end;

begin
  X := X/2;
  Y := Y/2;
  intX := Clamp(Trunc(X),0,LandW-2);
  intY := Clamp(Trunc(Y),0,LandH-2);

  v1 := FHeightMap[intX  ][intY];
  v2 := FHeightMap[intX+1][intY];
  v3 := FHeightMap[intX  ][intY+1];
  v4 := FHeightMap[intX+1][intY+1];

  Result := Lerp(Lerp(v1, v2, Frac(X)), Lerp(v3, v4, Frac(X)), Frac(Y));  
end;

procedure TTerrain.OnDraw;
var
  m : TMat4f;
  p : TVec3f;
begin
  m := Render.Matrix[mtProj] * Render.Matrix[mtView];
  p := Render.CameraPos;

  Render.DepthWrite := False;
  Render.DepthTest := True;
  Render.BlendType := btAdd;
 // Render.SetColorMask(false, false, false, true);

  FShader.Bind;
  FMatrixUniform.Value(m);
  FPosUniform.Value(p);
  FLightPosUniform.Value(FLightPos);
  FNormalTex.Bind;

  FVrtBuff.Bind;
  FPosAttrib.Value(24,0);
  FNormalAttrib.Value(24,12);
  FPosAttrib.Enable;
  FNormalAttrib.Enable;
  FVrtBuff.Draw(gmQuads, FVrtCount, false);
             {
  Render.SetColorMask(true, true, true, false);
  Render.DepthWrite := False;
  Render.DepthTest :=  False;
  Render.BlendType := btAdd;

  FVrtBuff.Draw(gmQuads, FVrtCount, false); }
end;

procedure TTerrain.OnUpdate(dt : LongInt);
var
  s,c : Single;
begin
  SinCos(Utils.Time/5000,s,c);
  FLightPos := Vec3f(FCenter.x, 0, FCenter.z) + Vec3f(s, 0, c)*30;
  FLightPos.y := GetHeight(FLightPos.x, FLightPos.z) + 6+ sin(Utils.Time/4000)*2;
end;

end.
