unit dshaperenderer;

{$MODE FPC}
{$MODESWITCH ADVANCEDRECORDS}
{$MODESWITCH DEFAULTPARAMETERS}
{$MODESWITCH OUT}
{$MODESWITCH RESULT}

interface

uses
  dstrnum,
  dvector2f,
  dmatrix4f,
  libtess2_tesselator,
  DEng,
  DDraw,
  DDrawVertexBuffer,
  DDrawShader;

const
  SHAPE_EDGE_NO_OTHER_COLOR = 0;

type
TShapeColor = UInt32;

TShapeRendererParams = record
end;

PShapeEdge = ^TShapeEdge;
TShapeEdge = record
  Delta: TVec2f;
  OtherColor: TShapeColor; // SHAPE_EDGE_NO_OTHER_COLOR or RGBA
end;

PShape = ^TShape;
TShape = record
private
  Index: PtrUInt;
  Count: PtrUInt;
end;

PShapeInstance = ^TShapeInstance;
TShapeInstance = object
private
  FShape: TShape;
  FMatrix: TMat4f;
public
  procedure Init(const Shape: TShape);
  procedure Done;
end;

PShapeRenderer = ^TShapeRenderer;
TShapeRenderer = object
private
  FBuffer: TVertexBuffer;
  FVertices: array of TVec2f;
  FColors: array of TShapeColor;

  procedure Sync;
public
  procedure Init;
  procedure Done;

  procedure Bind;
  procedure Unbind;
      // Binds vertex arrays and shaders.

  procedure Draw(const ShapeInstance: TShapeInstance);
end;

TShapeBuilder = object
private
  FShapeRenderer: PShapeRenderer;
  FVertices: array of TVec2f;
  FColors: array of TShapeColor;
  FNormals: array of TVec2f; // ???
  FBgColors: array of TShapeColor;
public
  procedure Init;
  procedure Done;

  procedure StartShape(ShapeRenderer: PShapeRenderer);
  procedure FinishShape(out Shape: TShape);
      // Builds new shape. Call AddPath between StartShape and FinishShape.
      // You cannot render while creating a shape.

  procedure AddContour(Color: TShapeColor; Edges: PShapeEdge; Count: PtrInt);
end;

implementation

procedure TShapeInstance.Init(const Shape: TShape);
begin
  FShape := Shape;
end;

procedure TShapeInstance.Done;
begin
end;

procedure TShapeRenderer.Sync;
begin
  FBuffer.SetCount(Length(FVertices));
  FBuffer.SetVertices(FVertices);
  FBuffer.SetColors(FColors);
  FBuffer.SetUseGPUStorage(True);
  Log('Sync to GPU ' + IntToStr(Length(FVertices)) + ' ' + IntToStr(Length(FColors)));
  FBuffer.SyncGPUStorage;
end;

procedure TShapeRenderer.Init;
begin
  FBuffer.Init;
  SetLength(FVertices, 0);
  SetLength(FColors, 0);
end;

procedure TShapeRenderer.Done;
begin
  FBuffer.Done;
end;

procedure TShapeRenderer.Bind;
begin
  FBuffer.Enable;
end;

procedure TShapeRenderer.Unbind;
begin
  FBuffer.Disable;
end;

procedure TShapeRenderer.Draw(const ShapeInstance: TShapeInstance);
begin
  // TODO use Matrix
  with ShapeInstance.FShape do FBuffer.BatchTriangles(Index, Count);
end;

procedure TShapeBuilder.Init;
begin
end;

procedure TShapeBuilder.Done;
begin
end;

procedure TShapeBuilder.StartShape(ShapeRenderer: PShapeRenderer);
begin
  FShapeRenderer := ShapeRenderer;
  SetLength(FVertices, 0);
  SetLength(FColors, 0);
  SetLength(FBgColors, 0);
  SetLength(FNormals, 0);
end;

procedure TShapeBuilder.FinishShape(out Shape: TShape);
begin
  Shape.Index := Length(FShapeRenderer^.FVertices);
  Shape.Count := Length(FVertices);
  SetLength(FShapeRenderer^.FVertices, Length(FShapeRenderer^.FVertices) + Length(FVertices));
  SetLength(FShapeRenderer^.FColors, Length(FShapeRenderer^.FColors) + Length(FColors));
  Move(FVertices[0], FShapeRenderer^.FVertices[Shape.Index], Shape.Count * SizeOf(TVec2f));
  Move(FColors[0], FShapeRenderer^.FColors[Shape.Index], Shape.Count * SizeOf(TShapeColor));
  FShapeRenderer^.Sync;
end;

procedure TShapeBuilder.AddContour(Color: TShapeColor; Edges: PShapeEdge; Count: PtrInt);
var
  tess: PTESStesselator;
  TessCount: PtrInt;
  X: PTESSindex;
  V: PTESSreal;
  I: PtrInt;
  Last: PtrInt;
begin
  tess := tessNewTess(nil);
  tessAddContour(tess, 2, Pointer(Edges), SizeOf(TShapeEdge), Count);
  if tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, 3, 2, nil) then begin
    TessCount := tessGetElementCount(tess);
    X := tessGetElements(tess);
    V := tessGetVertices(tess);
    Last := Length(FVertices);
    SetLength(FVertices, Last + 3 * TessCount);
    SetLength(FColors,   Last + 3 * TessCount);
    SetLength(FBgColors, Last + 3 * TessCount);
    for I := 0 to TessCount - 1 do begin
      FVertices[Last + I * 3 + 0] := Vec2f(V[2 * X[3 * I + 0]], V[2 * X[3 * I + 0] + 1]);
      FVertices[Last + I * 3 + 1] := Vec2f(V[2 * X[3 * I + 1]], V[2 * X[3 * I + 1] + 1]);
      FVertices[Last + I * 3 + 2] := Vec2f(V[2 * X[3 * I + 2]], V[2 * X[3 * I + 2] + 1]);
      FColors[Last + I * 3 + 0] := Color;
      FColors[Last + I * 3 + 1] := Color;
      FColors[Last + I * 3 + 2] := Color;
      // FBgColors[Last + I * 3 + 0] := ???;
      // FBgColors[Last + I * 3 + 1] := ???;
      // FBgColors[Last + I * 3 + 2] := ???;
    end;
  end else
    Log('TShapeBuilder: failed tesselation');
  tessDeleteTess(tess);
end;

end.
