unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure drawing(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

const
  HGT=256;
  WID=256;
  CHHGT=32;
  CHWID=64;
  LAYERS=16;//40; //число слоёв по y
  LAYERSTEP=1; //расстояние между слоями по y в масштабе 0..255
  DIST=3; //расстояние до экрана в масштабе 0..255
  dists:array[0..LAYERS-1]of integer=
        //(3,4,5,6,7,8,9,10,12,14,16,18,20,22,25,28,32,36,40,48,52,56,60,65);
        //(3,4,5,6,7,8,9,10,12,14,16,18,20,22,25,28,32,36,40,48,60,76);
        (3,4,5,6,7,8,9,10,12,14,16,18, 21,24,28,33{,39,47});

var
  Form1: TForm1;
  bm:TBitMap;
  curx,cury,curz:integer; //считают в масштабе 0..255
  tscale:array[0..LAYERS-1]of byte;
  tsiny:array[0..255]of shortint;
  tsinx:array[0..255]of byte;
  tsinx2:array[0..255]of byte;
  tscalesinx:array[0..LAYERS-1,0..255]of shortint;
  tscalesinx2:array[0..LAYERS-1,0..255]of shortint;
  fout: file of byte;
  longest: integer;

implementation

{$R *.dfm}

procedure setpixel(x,y,light:integer);
var i,j:integer;
begin
  if light<0 then light:=0;
  if light>=15 then light:=15;
  for i:=(x)*4 to (x)*4+3 do
    for j:=HGT div 2 + (y)*4 to HGT div 2 + (y)*4+3 do
      bm.Canvas.Pixels[i,j]:=$000101*light*16;
end;

function func_siny(y:real {в масштабе 0..255}):integer; {в масштабе +-127}
var
  yphase:real;
begin
  yphase:=y*2*pi/HGT;
  func_siny:=Trunc(127*(0.3*sin(yphase)+0.6*sin(0.6+yphase*2)+0.1*sin(0.15+yphase*3){+0.1*sin(0.5+yphase*10)})); //можно и сложнее
end;

function func_sinx(layer:integer; x:integer; siny:integer {в масштабе 0..255}):real;
var
  xphase:real;
  disty:integer;
  scale:real;
begin
    //disty:=DIST+(layer*LAYERSTEP); //в масштабе 0..255
    disty:=dists[layer];
    scale:=(DIST/4)/disty;
    //scale:=((LAYERS-layer)+1)/200;
    //disty:=(DIST/4)/scale;
  xphase:=x*2*pi/WID;
  //func_sinx:=scale*1.2*60*(0.6*sin(1*xphase+(9.5/127)*siny)+sin(2*xphase+(2.5/127)*siny));//+0.5*sin(3*xphase+1.5*siny));
             //любой k*sin(l*xphase+m*siny) делается просто через ld a,(hl):inc l
  func_sinx:=tscalesinx[layer,(x+Trunc(4{(WID/(2*pi))*(9.5/127)}*siny)+65536)mod 256] + tscalesinx{2}[layer,(2*x+Trunc(1{(WID/(2*pi))*(2.5/127)}*siny)+65536)mod 256];
end;

function func_lightx(x:integer; siny:integer {в масштабе 0..255}):integer;
var
  xphase:real;
begin
  xphase:=x*2*pi/WID;
  //func_lightx:=Trunc((128+60*(0.6*sin(pi/2+1*xphase+(9.5/127)*siny)+sin(pi/2+2*xphase+(2.5/127)*siny)))/16);//+0.5*sin(3*xphase+1.5*siny));
             //любой k*sin(l*xphase+m*siny) делается просто через ld a,(hl):inc l
  func_lightx:=tsinx[({64+}x+Trunc(4{(WID/(2*pi))*(9.5/127)}*siny)+65536)mod 256] + tsinx{2}[({64+}2*x+Trunc(1{(WID/(2*pi))*(2.5/127)}*siny)+65536)mod 256];
end;

procedure TForm1.drawing(Sender: TObject);
var
  x,y,scry,layer,basey:integer;
  mapx:integer;
  disty:integer;
  scale:real;
  light:integer;
  xphase,yphase:real;
  sinx:real;
  siny:integer;
  h:integer;
  drawny:array[0..WID-1]of integer;
  xcounter,x2counter:real;
  xstep,x2step:real;
  length:integer;
begin
  //siny:=func_siny(cury+(DIST+LAYERSTEP)); // = tsiny[cury+4]
  siny:=tsiny[(cury+4)mod 256];
  Memo1.Lines.Add('siny='+IntToStr(siny));
  curz:=119-{+}Trunc(4*func_sinx(0,curx,siny)); //curz+10;
  //curz:= 119 -{+} 4*(tscalesinx[0,(curx+4*siny+65536)mod 256] + tscalesinx[0,(2*curx+1*siny+65536)mod 256]);
  Memo1.Lines.Add('cury='+IntToStr(cury));
  Memo1.Lines.Add('curz='+IntToStr(curz));

  for y:=0 to 191{HGT-1} do begin
    siny:=func_siny(y);
    for x:=0 to WID-1 do begin
      xphase:=x*2*pi/WID;
      sinx:=func_sinx(0,x,siny); //любой k*sin(l*xphase+m*siny) делается просто через ld a,(hl):inc l
      bm.Canvas.Pixels[x,y]:=$010101*Trunc(128+sinx);
    end;
  end;

  //h = физическая высота точки
  //curz = высота наблюдателя
  //disty = расстояние до точки в прямом направлении
  //DIST = расстояние до экрана в прямом направлении
  //надо найти y на экране

  //(curz-y)/DIST = (curz-h)/disty
  //(curz-y) = (curz-h)*DIST/disty
  //y = curz-(curz-h)*DIST/disty = (curz-curz*DIST/disty) + h*DIST/disty
  //ошибка?

  //curz:=100;

  for x:=0 to CHWID-1 do drawny[x]:=CHHGT;

  for layer:=0 to LAYERS-1 do begin
    //disty:=DIST+(layer*LAYERSTEP); //в масштабе 0..255
    disty:=dists[layer];
    //scale:=(DIST/4)/disty;
    scale:=tscale[layer]/1024;
    //scale:=((LAYERS-layer)+1)/200;
    //disty:=(DIST/4)/scale;
    basey:=Trunc(curz*scale);
    siny:=tsiny[(cury+disty)mod 256];//func_siny(cury+disty);
    xstep:=disty/8{(2*DIST)};//1/(8*scale);
    x2step:=xstep*2;
    xcounter:=Trunc(curx-(CHWID div 2)*xstep + 4{(WID/(2*pi))*(9.5/127)}*siny);
    x2counter:=Trunc(2*(curx-(CHWID div 2)*xstep) + 1{(WID/(2*pi))*(2.5/127)}*siny);
    Memo1.Lines.Add('layer='+IntToStr(layer)+', basey='+IntToStr(basey)+', xcounter='+FloatToStr(xcounter)+', x2counter='+FloatToStr(x2counter)+', xstep='+FloatToStr(xstep));
    for x:=0 to CHWID-1 do begin
      mapx:=Trunc(curx+(x-(CHWID div 2))/(8*scale));
      //h:=Trunc({scale*}func_sinx(layer,mapx,siny)); //любой k*sin(l*xphase+m*siny) делается просто через ld a,(hl):inc l
      h:=tscalesinx[layer,(Trunc(xcounter+65536))mod 256] + tscalesinx2[layer,(Trunc(x2counter+65536))mod 256];
      xcounter:=xcounter+xstep;
      x2counter:=x2counter+x2step;
      scry:=basey+{-}h; //растёт снизу
      //light:=func_lightx(mapx,siny);
      light:=tsinx[(Trunc(xcounter+65536))mod 256] + tsinx2[(Trunc(x2counter+65536))mod 256];
      if scry<drawny[x]
      then begin
        length:=drawny[x]-scry;
        //if length>8 then length:=8;
        if (layer=14{LAYERS-1})and(length>longest) then longest:=length;
        for y:=scry to scry+length-1{drawny[x]-1}{HGT-1} do begin
          setpixel(x,y,light);
        end;
        drawny[x]:=scry;
      end;
    end;
  end;

  Image1.Picture.Assign(bm);
  Memo1.Lines.Add('longest='+IntToStr(longest));

  cury:=cury+2;

end;

procedure TForm1.FormCreate(Sender: TObject);
var
 i,j,layer:integer;
 x:integer;
 mapx:integer;
 disty:integer;
 scale:real;
 iscale:byte;
 xphase:real;
 basey:integer;
 siny:integer;
 b:byte;
begin
  curx:=220;//curx+0;
  longest:=0;

  bm:=TBitMap.Create;
  bm.Height:=HGT;
  bm.Width:=WID;
  bm.PixelFormat:=pf24bit;

  for i:=0 to 255 do begin
    tsiny[i]:=func_siny(i);
    if(tsiny[i]<-128)then Memo1.Lines.Add('underflow');
    if(tsiny[i]>+127)then Memo1.Lines.Add('overflow');
  end;

  for layer:=0 to LAYERS-1 do begin
    //disty:=DIST+(layer*LAYERSTEP); //в масштабе 0..255
    disty:=dists[layer];
    scale:=(DIST/4)/disty;
    scale:=scale*1024;
    if scale>255 then scale:=255;
    iscale:=Trunc(scale+0.5);
    //Memo1.Lines.Add('layer='+IntToStr(layer)+', iscale='+IntToStr(iscale));
    tscale[layer]:=iscale;
    scale:=iscale/1024;
    //scale:=((LAYERS-layer)+1)/200;
    //disty:=(DIST/4)/scale;
    basey:=Trunc(curz*scale);
    siny:=tsiny[(cury+disty)mod 256];//func_siny(cury+disty);
    //for x:=0 to CHWID-1 do begin
    for x:=0 to 255 do begin
      mapx:=Trunc(curx+(x-(CHWID div 2))/(8*scale));
      xphase:=x*2*pi/WID;
      //tsinx[layer,x]:=Trunc(scale*func_sinx(mapx,siny));
      tsinx[x]:=Trunc((72{64}-1.2*60*0.6*sin(3.5*pi/8+xphase))/8);
      if(tsinx[x]<0)then Memo1.Lines.Add('tsinx underflow');
      if(tsinx[x]>255)then Memo1.Lines.Add('tsinx overflow');
      tsinx2[x]:=Trunc((72{64}-1.2*60*1.0*sin(3.5*pi/8+xphase))/8);
      if(tsinx2[x]<0)then Memo1.Lines.Add('tsinx2 underflow');
      if(tsinx2[x]>255)then Memo1.Lines.Add('tsinx2 overflow');

      tscalesinx[layer,x]:=Trunc(scale*1.2*60*{0.6*}sin(xphase));
      if(tscalesinx[layer,x]<-128)then Memo1.Lines.Add('tscalesinx underflow');
      if(tscalesinx[layer,x]>+127)then Memo1.Lines.Add('tscalesinx overflow');
      tscalesinx2[layer,x]:=Trunc(scale*1.2*60*1.0*sin(xphase));
      if(tscalesinx2[layer,x]<-128)then Memo1.Lines.Add('tscalesinx2 underflow');
      if(tscalesinx2[layer,x]>+127)then Memo1.Lines.Add('tscalesinx2 overflow');
    end;
  end;

  AssignFile(fout,'dists.dat');
  Rewrite(fout);
    for layer:=0 to LAYERS-1 do begin
     BlockWrite(fout, dists[layer], 1);
     BlockWrite(fout, tscale[layer], 1);
    end;
  CloseFile(fout);


  AssignFile(fout,'tsiny.dat');
  Rewrite(fout);
  BlockWrite(fout, tsiny, 256);
  CloseFile(fout);

  AssignFile(fout,'tsinx.dat');
  Rewrite(fout);
    for x:=0 to 255 do begin
     b:=tsinx[x]*2+($c0 div 2);
     BlockWrite(fout, b, 1);
    end;
  //BlockWrite(fout, tsinx, 256);
  CloseFile(fout);

  //AssignFile(fout,'tsinx2.dat');
  //Rewrite(fout);
  //BlockWrite(fout, tsinx2, 256);
  //CloseFile(fout);

  AssignFile(fout,'tscalesinx.dat');
  Rewrite(fout);
  for layer:=0 to LAYERS-1 do begin
    for x:=0 to 255 do begin
     BlockWrite(fout, tscalesinx[layer,x], 1);
    end;
    for x:=0 to 255 do begin
     //BlockWrite(fout, tscalesinx2[layer,x], 1);
    end;
  end;
  CloseFile(fout);

  drawing(Sender);
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
  drawing(Sender);
end;

end.
