{
Ported to FPC by Nikolay Nikolov (nickysn@users.sourceforge.net)
}

{
  Texture warp demo for OpenPTC 1.0 C++ API
  Copyright (c) 1998 Jonathan Matthew
  This source code is licensed under the GNU GPL
}

Program TexWarp;

{$MODE objfpc}

Uses
  ptc;

Const
{ colour balance values.  change these if you don't like the colouring }
{ of the texture. }
  red_balance : int32 = 2;
  green_balance : int32 = 3;
  blue_balance : int32 = 1;

Procedure blur(s : TPTCSurface);

Var
  d : Pchar8;
  pitch : Integer;
  spack, r : Integer;

Begin
  { lock surface }
  d := s.lock;
  pitch := s.pitch;
  spack := (s.height - 1) * pitch;

  { first pixel }
  For r := 0 To 3 Do
    d[r] := (d[pitch + r] + d[r + 4] + d[spack + r] + d[pitch - 4 + r]) Div 4;

  { rest of first line }
  For r := 4 To pitch - 1 Do
    d[r] := (d[r + pitch] + d[r + 4] + d[r - 4] + d[spack + r]) Div 4;

  { rest of surface except last line }
  For r := pitch To ((s.height - 1) * pitch) - 1 Do
    d[r] := (d[r - pitch] + d[r + pitch] + d[r + 4] + d[r - 4]) Div 4;

  { last line except last pixel }
  For r := (s.height - 1) * pitch To (s.height * s.pitch) - 5 Do
    d[r] := (d[r - pitch] + d[r + 4] + d[r - 4] + d[r - spack]) Div 4;

  { last pixel }
  For r := (s.height * s.pitch) - 4 To s.height * s.pitch Do
    d[r] := (d[r - pitch] + d[r - 4] + d[r - spack] + d[r + 4 - pitch]) Div 4;
  s.unlock;
End;

Procedure generate(surface : TPTCSurface);

Var
  dest : Pint32;
  i : Integer;
  x, y : Integer;
  d : Pint32;
  cv : int32;
  r, g, b : char8;

Begin
  { draw random dots all over the surface }
  dest := surface.lock;
  For i := 0 To surface.width * surface.height - 1 Do
  Begin
    x := Random(surface.width);
    y := Random(surface.height);
    d := dest + (y * surface.width) + x;
    cv := (Random(100) Shl 16) Or (Random(100) Shl 8) Or Random(100);
    d^ := cv;
  End;
  surface.unlock;
  
  { blur the surface }
  For i := 1 To 5 Do
    blur(surface);
  
  { multiply the color values }
  dest := surface.lock;
  For i := 0 To surface.width * surface.height - 1 Do
  Begin
    cv := dest^;
    r := (cv Shr 16) And 255;
    g := (cv Shr 8) And 255;
    b := cv And 255;
    r *= red_balance;
    g *= green_balance;
    b *= blue_balance;
    If r > 255 Then
      r := 255;
    If g > 255 Then
      g := 255;
    If b > 255 Then
      b := 255;
    dest^ := (r Shl 16) Or (g Shl 8) Or b;
    Inc(dest);
  End;
  surface.unlock;
End;

Procedure grid_map(grid : Pint32; xbase, ybase, xmove, ymove, amp : Single);

Var
  x, y : Integer;
  a, b, id : Single;

Begin
  a := 0;
  For y := 0 To 25 Do
  Begin
    b := 0;
    For x := 0 To 40 Do
    Begin
      { it should be noted that there is no scientific basis for }
      { the following three lines :) }
      grid[0] := int32(Trunc((xbase * 14 + x*4 + xmove*sin(b)+sin(cos(a)*sin(amp))*15) * 65536));
      grid[1] := int32(Trunc((ybase * 31 + y*3 + ymove*cos(b)*sin(sin(a)*cos(amp))*30) * 65536));
      id := (cos(xbase) + sin(ybase) + cos(a*xmove*0.17) + sin(b*ymove*0.11)) * amp * 23;
      If id < -127 Then
        grid[2] := 0
      Else
        If id > 127 Then
	  grid[2] := 255 Shl 16
	Else
	  grid[2] := (128 Shl 16) + Trunc(id * 65536.0);
      grid += 3;
      b += pi / 30;
    End;
    a += pi / 34;
  End;
End;

Procedure make_light_table(lighttable : Pchar8);

Var
  i, j : Integer;
  tv : Integer;

Begin
  For i := 0 To 255 Do
    For j := 0 To 255 Do
    Begin
      { light table goes from 0 to i*2. }
      tv := (i * j) Div 128;
      If tv > 255 Then
        tv := 255;
      lighttable[(j * 256) + i] := tv;
    End;
End;

{ if you want to see how to do this properly, look at the tunnel3d demo. }
{ (not included in this distribution :) }
Procedure texture_warp(dest, grid, texture : Pint32; lighttable : Pchar8);

Var
  utl, utr, ubl, ubr : Integer;
  vtl, vtr, vbl, vbr : Integer;
  itl, itr, ibl, ibr : Integer;
  dudx, dvdx, didx, dudy, dvdy, didy, ddudy, ddvdy, ddidy : Integer;
  dudx2, dvdx2, didx2 : Integer;
  bx, by, px, py : Integer;
  uc, vc, ic, ucx, vcx, icx : Integer;
  
  edi : int32;
  texel : int32;
  
  cbp, dp : Pint32;
  dpix : int32;
  
  ltp : Pchar8;

Begin
  cbp := grid;
  For by := 0 To 24 Do
  Begin
    For bx := 0 To 39 Do
    Begin
      utl := Integer(cbp^);
      vtl := Integer((cbp + 1)^);
      itl := Integer((cbp + 2)^);
      utr := Integer((cbp + (1 * 3))^);
      vtr := Integer((cbp + (1 * 3) + 1)^);
      itr := Integer((cbp + (1 * 3) + 2)^);
      ubl := Integer((cbp + (41 * 3))^);
      vbl := Integer((cbp + (41 * 3) + 1)^);
      ibl := Integer((cbp + (41 * 3) + 2)^);
      ubr := Integer((cbp + (42 * 3))^);
      vbr := Integer((cbp + (42 * 3) + 1)^);
      ibr := Integer((cbp + (42 * 3) + 2)^);
      dudx := (utr - utl) Div 8;
      dvdx := (vtr - vtl) Div 8;
      didx := (itr - itl) Div 8;
      dudx2 := (ubr - ubl) Div 8;
      dvdx2 := (vbr - vbl) Div 8;
      didx2 := (ibr - ibl) Div 8;
      dudy := (ubl - utl) Div 8;
      dvdy := (vbl - vtl) Div 8;
      didy := (ibl - itl) Div 8;
      ddudy := (dudx2 - dudx) Div 8;
      ddvdy := (dvdx2 - dvdx) Div 8;
      ddidy := (didx2 - didx) Div 8;
      uc := utl;
      vc := vtl;
      ic := itl;
      For py := 0 To 7 Do
      Begin
        ucx := uc;
	vcx := vc;
	icx := ic;
	dp := dest + (((by * 8 + py)*320) + (bx * 8));
	For px := 0 To 7 Do
	Begin

          { get light table pointer for current intensity }
	  ltp := lighttable + ((icx And $FF0000) Shr 8);

          { get texel }
	  edi := ((ucx And $FF0000) Shr 16) + ((vcx And $FF0000) Shr 8);
	  texel := texture[edi];
	  
          { calculate actual colour }
	  dpix := ltp[(texel Shr 16) And 255];
	  dpix := dpix Shl 8;
	  dpix := dpix Or ltp[(texel Shr 8) And 255];
	  dpix := dpix Shl 8;
	  dpix := dpix Or ltp[texel And 255];
	  
	  dp^ := dpix;
	  Inc(dp);
	  
	  ucx += dudx;
	  vcx += dvdx;
	  icx += didx;
	End;
	uc += dudy;
	vc += dvdy;
	ic += didy;
	dudx += ddudy;
	dvdx += ddvdy;
	didx += ddidy;
      End;
      cbp += 3;
    End;
    cbp += 3;
  End;
End;

Var
  format : TPTCFormat;
  texture : TPTCSurface;
  surface : TPTCSurface;
  console : TPTCConsole;
  lighttable : Pchar8;
  { texture grid }
  grid : Array[0..41*26*3-1] Of int32;
  xbase, ybase, xmove, ymove, amp, dct, dxb, dyb, dxm, dym, sa : Single;
  
  p1, p2 : Pint32;

Begin
  format := Nil;
  texture := Nil;
  surface := Nil;
  console := Nil;
  lighttable := Nil;
  Try
    Try
      { create format }
      format := TPTCFormat.Create(32, $00FF0000, $0000FF00, $000000FF);
    
      { create texture surface }
      texture := TPTCSurface.Create(256, 256, format);

      { create texture }
      generate(texture);

      { create lighttable }
      lighttable := GetMem(256 * 256);
      make_light_table(lighttable);

      { create console }
      console := TPTCConsole.Create;

      { open console }
      console.open('Warp demo', 320, 200, format);

      { create drawing surface }
      surface := TPTCSurface.Create(320, 200, format);

      { control values }
      xbase := 0;
      ybase := 0;
      xmove := 0;
      ymove := 0;
      amp := 0;
      dct := 0.024;
      dxb := 0.031;
      dyb := -0.019;
      dxm := 0.015;
      dym := -0.0083;
    
      { main loop }
      While Not console.KeyPressed Do
      Begin
    
        { create texture mapping grid }
        grid_map(grid, xbase, ybase, xmove, ymove*3, amp);

        p1 := surface.lock;
	Try
	  p2 := texture.lock;
	  Try
            { map texture to drawing surface }
            texture_warp(p1, grid, p2, lighttable);
	  Finally
            texture.unlock;
	  End;
	Finally
          surface.unlock;
	End;

        { copy surface to console }
        surface.copy(console);

        { update console }
        console.update;
      
        { move control values (limit them so it doesn't go too far) }
        xbase += dxb;
        If xbase > pi Then
          dxb := -dxb;
        If xbase < (-pi) Then
          dxb := -dxb;
      
        ybase += dyb;
        If ybase > pi Then
          dyb := -dyb;
        If ybase < (-pi) Then
          dyb := -dyb;
      
        xmove += dxm;
        If xmove > pi Then
          dxm := -dxm;
        If xmove < (-pi) Then
          dxm := -dxm;
      
        ymove += dym;
        If ymove > pi Then
          dym := -dym;
        If ymove < (-pi) Then
          dym := -dym;
      
        amp += dct;
        sa := sin(amp);
        If (sa > -0.0001) And (sa < 0.0001) Then
        Begin
          If amp > 8.457547 Then
	    dct := -dct;
	  If amp < -5.365735 Then
	    dct := -dct;
        End;
      End;
    Finally
      console.close;
      console.Free;
      surface.Free;
      texture.Free;
      format.Free;
      If assigned(lighttable) Then
        FreeMem(lighttable);
    End;
  Except
    On e : TPTCError Do
      e.report;
  End;
End.
