unit Textures;

{***********************************************************

Project:    C-evo External Map Generator
Copyright:  1999-2024 P Blackman
License:    GPLv3+

Unit to provide mapping interface to lower level texture routines.

***********************************************************}

interface

type
    Ttexture = class
    private
        fTable: array of Double;
        fHeight,
        fWidth: Integer;

        procedure SetTexture(X, Y: Integer; V: Double);

    public
        Max, Min: Double;

        function Texture(X, Y: Integer): Double;
        function Scale  (X, Y: Integer): Integer;
        constructor Create(Width, Height: Integer);
        destructor Destroy; override;
        procedure GenData(Wave, Dim, Lac, Oct: Double; ResetNoise: Boolean);
    end;


implementation uses SysUtils, Utils, Noise, Fractal, Message;


procedure Ttexture.SetTexture(X, Y: Integer; V: Double);
begin
    fTable[(X-1) + (Y-1) * fWidth] := V;
end;


function Ttexture.Texture(X, Y: Integer): Double;
begin
    Result := fTable[(X-1) + (Y-1) * fWidth];
end;


{ Scale raw texture values into range 0 -> 255 }
function Ttexture.Scale(X, Y: Integer): Integer;
begin
    Result := Trunc(255 * JumpStep(Min, Max, Texture(X, Y)));
end;


procedure Ttexture.GenData(Wave, Dim, Lac, Oct: Double; ResetNoise: Boolean);

var X, Y: Integer;
    F, P: Double;
    Vec: Vector;
    fBm: TfBm;
    radius, Sn, Cs, angle: Double;

begin
    { Arbitrary Large Numbers }
    Max := -10000000;
    Min := 10000000;

    fBm := TfBm.Create(Dim, Lac, Oct, ResetNoise);

    radius := fWidth / (wave * 2 * PI);
    Sn     := 0;
    Cs     := 0;

    try
        for X := 1 to fWidth do
        begin
            P := X;

            { Use a real cylinder to get round world effect }
            angle := (2 * PI) * P / fwidth;
            Sn    := Sin(Angle);
            cs    := Cos(Angle);

            for Y := 1 to fHeight do
            begin
                Vec.X := 2 * radius * Sn;
                Vec.z := 2 * radius * Cs;
                Vec.y := Y / (wave);

                F := 1.0 + fBM.Compute(Vec);

                if F > Max then
                    Max := F
                else
                if F < Min then
                    Min := F;

                SetTexture(X, Y, F);
            end;
        end;

    except
        on E: Exception do
        begin
            Showmessage(E.Message);
            Halt (1);
        end;
    end;

    fBm.Free;
end;


constructor ttexture.Create(Width, Height: Integer);
begin
    inherited Create;
    fWidth  := Width;
    fHeight := Height;
    SetLength(fTable, fHeight * fWidth);
end;


destructor ttexture.Destroy;
begin
    SetLength(fTable, 0);
    inherited;
end;

end.
