{$IFDEF ANDROID}
procedure TDraw.glOrtho(Left, Right, Bottom, Top, NearVal, FarVal: Double);
begin
  glOrthof(Left, Right, Bottom, Top, NearVal, FarVal);
  {FProjection[ 0] := 2 / (Right - Left);
  FProjection[ 1] := 0;
  FProjection[ 2] := 0;
  FProjection[ 3] := 0;

  FProjection[ 4] := 0;
  FProjection[ 5] := 2 / (Top - Bottom);
  FProjection[ 6] := 0;
  FProjection[ 7] := 0;

  FProjection[ 8] := 0;
  FProjection[ 9] := 0;
  FProjection[10] := - 2 / (FarVal - NearVal);
  FProjection[11] := 0;

  FProjection[12] := - (Right + Left) / (Right - Left);
  FProjection[13] := - (Top + Bottom) / (Top - Bottom);
  FProjection[14] := - (FarVal + NearVal) / (FarVal - NearVal);
  FProjection[15] := 1;

  FShader.SetUniformMatrix4f('uMvp', FProjection);}
end;
{$ENDIF}

const
  SHADER_APPLY_MATRIX_V = '' +
    'uniform mat4    uMvp;'#13 +
    'attribute vec3 aPosition;'#13 +
    'varying vec4 vColor;'#13 +
    ''#13 +
    'void main() {'#13 +
    '   vec4 position = vec4(aPosition.xyz, 1.);'#13 +
    '   gl_Position = uMvp * position;'#13 +
    '}';
  SHADER_APPLY_MATRIX_F = '' +
    '#ifdef GL_ES'#13 +
    'precision highp float;'#13 +
    '#endif'#13 +
    ''#13 +
    'void main(void)'#13 +
    '{'#13 +
    '   gl_FragColor = vColor;'#13 +
    '}';

constructor TDraw.Init(WinApi: PWinApi);
begin
  FWinApi := WinApi;

  glEnable(GL_LINE_SMOOTH);
  glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
end;

procedure TDraw.OnInit;
begin
  FWidth := 1;

  // Clear screen to black on startup
  BeginRender;
  glClearColor(0, 0, 0, 0);
  EndRender;

  {$IFDEF ANDROID}
  //FShader.Init(SHADER_APPLY_MATRIX_V, SHADER_APPLY_MATRIX_F);
  {$ENDIF}
end;

function TDraw.IsOpenGL: Boolean;
begin
  Exit(True);
end;

function TDraw.IsVulkan: Boolean;
begin
  Exit(False);
end;

// TODO FShader.Done

procedure TDraw.BeginRender;
begin
  if WINAPI_SETTINGS.MultiSampling then
    glEnable(glext.GL_MULTISAMPLE);
end;

procedure TDraw.Start2D;
var
  Size: TVec2i;
begin
  //{$IFNDEF ANDROID}
  Size := FWinApi.Window.Size;
  glViewPort(0, 0, Size.X, Size.Y);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  glOrtho(0, Size.X, Size.Y, 0, -1, 1);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
  //glTranslatef(-0.5, -0.5, 0);
  //{$ENDIF}
end;

procedure TDraw.Start2D(const Pos, Size: TVec2i);
var
  WindowSize: TVec2i;
begin
  WindowSize := FWinApi.Window.Size;
  Start2D;
  glTranslatef(Pos.X, Pos.Y, 0);
  glScalef(WindowSize.X / Size.X, WindowSize.Y / Size.Y, 1);
  //glScalef(Size.X / WindowSize.X, Size.Y / WindowSize.Y, 1);
end;

procedure TDraw.Start2D(const Pos: TVec2i; Coeff: Single);
begin
  Start2D;
  glTranslatef(Pos.X, Pos.Y, 0);
  glScalef(Coeff, Coeff, 1);
end;

procedure TDraw.End2D;
begin
end;

procedure TDraw.Start3D(FOVYInDegrees: Single = 90;
                  Aspect: Single = 0;
                  ZNear: Single = 0.1;
                  ZFar: Single = 1000.0);
var
  Size: TVec2i;
  YMax, XMax: Single;
begin
  Size := FWinApi.Window.Size;
  if Aspect = 0 then begin
    Aspect := Size.X / Size.Y;
  end;
  glViewPort(0, 0, Size.X, Size.Y);

  YMax := ZNear * Tan(FOVYInDegrees * pi / 360);
  XMax := YMax * Aspect;

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  glFrustum(-XMax, XMax, -YMax, YMax, ZNear, ZFar);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_ALPHA_TEST);
  //glEnable(GL_CULL_FACE);
  //glCullFace(GL_BACK);
  glAlphaFunc(GL_GREATER, 0.01);
end;

procedure TDraw.End3D;
begin
  //glDisable(GL_CULL_FACE);
  glDisable(GL_ALPHA_TEST);
  glDisable(GL_DEPTH_TEST);
end;

class procedure TDraw.SetColor(const Color: TVec4f);
begin
  //{$IFDEF ANDROID}
  //FColor := dcolors.RGBA(Color);
  //{$ELSE}
  glColor4f(Color.X, Color.Y, Color.Z, Color.W);
  //{$ENDIF}
end;

procedure TDraw.DrawPixel(const P: TVec2i; const Color: TVec4f);
begin
  {$IFNDEF ANDROID}
  glBegin(GL_POINTS);
    glColor4fv(@Color);
    glVertex2iv(@P);
  glEnd;
  {$ENDIF}
end;

procedure TDraw.DrawLine(const P1, P2: TVec2i; const Color: TVec4f);
begin
  {$IFNDEF ANDROID}
  glBegin(GL_LINES);
    glColor4fv(@Color);
    glVertex2iv(@P1);
    glVertex2iv(@P2);
  glEnd;
  {$ENDIF}
end;

procedure TDraw.DrawRect(const P1, P2: TVec2i; const Color: TVec4f);
begin
  {$IFNDEF ANDROID}
  glBegin(GL_QUADS);
    glColor4fv(@Color);
    glVertex2iv(@P1);
    glVertex2i(P1.X, P2.Y);
    glVertex2iv(@P2);
    glVertex2i(P2.X, P1.Y);
  glEnd;
  {$ENDIF}
end;

procedure TDraw.DrawCircle(const P: TVec2f; R: Single; const Color: TVec4f);
begin
  Self.Color(Color).CircleFilled(P, R);
end;

function TDraw.ResetMatrix: TDraw;
begin
  glLoadIdentity;
  Result := Self;
end;

function TDraw.PushMatrix: TDraw;
begin
  glPushMatrix;
  Result := Self;
end;

function TDraw.PopMatrix: TDraw;
begin
  glPopMatrix;
  Result := Self;
end;

function TDraw.MultMatrix(const M: TMat4f): TDraw;
begin
  glMultMatrixf(@M[0]);
  Result := Self;
end;

function TDraw.Translate(const V: TVec2f): TDraw;
begin
  glTranslatef(V.X, V.Y, 0);
  Result := Self;
end;

function TDraw.Translate(const V: TVec3f): TDraw;
begin
  glTranslatef(V.X, V.Y, V.Z);
  Result := Self;
end;

function TDraw.Rotate(Angle: Single): TDraw;
begin
  glRotatef(Angle, 0, 0, 1);
  Result := Self;
end;

function TDraw.Rotate(const V: TVec3f; Angle: Single): TDraw;
begin
  glRotatef(Angle, V.X, V.Y, V.Z);
  Result := Self;
end;

function TDraw.Scale(S: Single): TDraw;
begin
  glScalef(S, S, S);
  Result := Self;
end;

function TDraw.Scale(const V: TVec2f): TDraw;
begin
  glScalef(V.X, V.Y, 1);
  Result := Self;
end;

procedure TDraw.LookAt(const Eye, Center, Up: TVec3f);
var
  Dir: TVec3f;
  Side: TVec3f;
  ActualUp: TVec3f;
  M: TMat4f;
begin
  Dir := Norm3f(Sub3f(Center, Eye));
  Side := Norm3f(Cross3f(Dir, Up));
  ActualUp := Cross3f(Side, Dir);
  M[ 0] := Side.X; M[ 1] := ActualUp.X; M[ 2] := - Dir.X; M[ 3] := 0;
  M[ 4] := Side.Y; M[ 5] := ActualUp.Y; M[ 6] := - Dir.Y; M[ 7] := 0;
  M[ 8] := Side.Z; M[ 9] := ActualUp.Z; M[10] := - Dir.Z; M[11] := 0;
  M[12] := 0;      M[13] := 0;          M[14] := 0;       M[15] := 1;
  glMultMatrixf(@M[0]);
  glTranslatef(- Eye.X, - Eye.Y, - Eye.Z);
end;

procedure TDraw.GetModelViewMatrix(out M: TMat4f);
begin
  glGetFloatv (GL_MODELVIEW_MATRIX, @M[0]);
end;

procedure TDraw.GetProjectionMatrix(out M: TMat4f);
begin
  glGetFloatv (GL_PROJECTION_MATRIX, @M[0]);
end;

procedure TDraw.DrawBuffer(X, Y, W, H: Integer; Data: Pointer);
begin
  Buffer(Data, Vec2i(X, Y), Vec2i(W, H));
end;

procedure TDraw.Pixel(const P: TVec2i; const Color: TVec4f);
begin
  Pixel(P.X, P.Y, dcolors.RGBA(Color));
end;

procedure TDraw.Pixel(X, Y: LongInt; const Color: Cardinal);
begin
  {$IFNDEF ANDROID}
  glBegin(GL_POINTS);
    glColor4ubv(@Color);
    glVertex2i(X, Y);
  glEnd;
  {$ENDIF}
end;

function TDraw.Color(const Value: TVec4f): TDraw;
begin
  glColor4f(Value.X, Value.Y, Value.Z, Value.W);
  Result := Self;
end;

function TDraw.Color(const Value: Cardinal): TDraw;
begin
  glColor4ub(GetRed(Value), GetGreen(Value), GetBlue(Value), GetAlpha(Value));
  Result := Self;
end;

function TDraw.RGBA(R, G, B, A: Byte): TDraw;
begin
  glColor4ub(R, G, B, A);
  Result := Self;
end;

function TDraw.Width(W: Float): TDraw;
begin
  FWidth := W;
  glLineWidth(W);
  Result := Self;
end;

function TDraw.From(const Cursor: TVec2i): TDraw;
begin
  FCursor := Cursor;
  Result := Self;
end;

function TDraw.BlendMode(Mode: TBlendMode): TDraw;
begin
  if Mode = BM_NO then
    glDisable(GL_BLEND)
  else begin
    glEnable(GL_BLEND);
    if Mode = BM_TO_ALPHA then begin
      glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
    end else if Mode = BM_APPLY_ALPHA then begin
      glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    end else
      glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
    if Mode = BM_MUL then begin
      glEnable(GL_ALPHA_TEST);
      glAlphaFunc(GL_GREATER, 0.9);
    end else
      glDisable(GL_ALPHA_TEST);
    case Mode of
      BM_ALPHA:       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      BM_MUL:         glBlendFunc(GL_ZERO,      GL_SRC_COLOR);
      BM_ADD:         glBlendFunc(GL_ONE,       GL_ONE);
      BM_DIFFUSE:     glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
      BM_ALPHA_ADD:   glBlendFunc(GL_SRC_ALPHA, GL_ONE);
      BM_TO_ALPHA:    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      BM_APPLY_ALPHA: glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
    end;
  end;
  Result := Self;
end;

procedure TDraw.BlendModeMax;
begin
  glEnable(GL_BLEND);
  glBlendEquationSeparate(GL_MAX, GL_MAX);
  glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
end;

procedure TDraw.BlendModeMaxOff;
begin
  glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
  glBlendEquation(GL_FUNC_ADD);
end;

procedure TDraw.EnableWireframe;
begin
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
end;

procedure TDraw.DisableWireframe;
begin
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
end;

procedure TDraw.EnableCull(Front: Boolean = True);
begin
  glEnable(GL_CULL_FACE);
  if Front then begin
    glCullFace(GL_BACK);
  end else
    glCullFace(GL_FRONT);
end;

procedure TDraw.DisableCull;
begin
  glDisable(GL_CULL_FACE);
end;

function TDraw.Line(const A, B: TVec2i): TDraw;
begin
  {$IFNDEF ANDROID}
  glBegin(GL_LINES);
    glVertex2iv(@A);
    glVertex2iv(@B);
  glEnd;
  {$ENDIF}
  Result := Self;
end;

function TDraw.Line(const A, B: TVec2f): TDraw;
begin
  {$IFNDEF ANDROID}
  glBegin(GL_LINES);
    glVertex2fv(@A);
    glVertex2fv(@B);
  glEnd;
  {$ENDIF}
  Result := Self;
end;

function TDraw.LineTo(const Cursor: TVec2i): TDraw;
begin
  {$IFNDEF ANDROID}
  glBegin(GL_LINES);
    glVertex2iv(@FCursor);
    glVertex2iv(@Cursor);
  glEnd;
  {$ENDIF}
  FCursor := Cursor;
  Result := Self;
end;

function TDraw.Rect(const R: TRect2i): TDraw;
var
  Filled: TRect2i;
begin
  {$IFNDEF ANDROID}
  if FWidth = 1 then begin
    glBegin(GL_LINE_STRIP);
      glVertex2i(R.P.X,         R.P.Y);
      glVertex2i(R.P.X + R.S.X, R.P.Y);
      glVertex2i(R.P.X + R.S.X, R.P.Y + R.S.Y);
      glVertex2i(R.P.X,         R.P.Y + R.S.Y);
      glVertex2i(R.P.X,         R.P.Y);
      glVertex2i(R.P.X,         R.P.Y);
    glEnd;
    //glBegin(GL_LINE_LOOP);
    //  glVertex2i(R.P.X,         R.P.Y);
    //  glVertex2i(R.P.X + R.S.X, R.P.Y);
    //  glVertex2i(R.P.X + R.S.X, R.P.Y + R.S.Y);
    //  glVertex2i(R.P.X,         R.P.Y + R.S.Y);
    //glEnd;
    Exit(Self);
  end;
  Filled.P := R.P;
  Filled.S := Vec2i(R.S.X, Round(FWidth));
  RectFilled(Filled);
  Filled.S := Vec2i(Round(FWidth), R.S.Y);
  RectFilled(Filled);
  Filled.P := Add2i(R.P, R.S);
  Filled.S := Vec2i(- R.S.X, - Round(FWidth));
  RectFilled(Filled);
  Filled.S := Vec2i(- Round(FWidth), - R.S.Y);
  RectFilled(Filled);
  {$ENDIF}
  Result := Self;
end;

function TDraw.Rect(const R: TRect2f): TDraw;
var
  Filled: TRect2f;
begin
  {$IFNDEF ANDROID}
  if FWidth = 1 then begin
    glBegin(GL_LINE_STRIP);
      glVertex2f(R.P.X,         R.P.Y);
      glVertex2f(R.P.X + R.S.X, R.P.Y);
      glVertex2f(R.P.X + R.S.X, R.P.Y + R.S.Y);
      glVertex2f(R.P.X,         R.P.Y + R.S.Y);
      glVertex2f(R.P.X,         R.P.Y);
      glVertex2f(R.P.X,         R.P.Y);
    glEnd;
    //glBegin(GL_LINE_LOOP);
    //  glVertex2f(R.P.X,         R.P.Y);
    //  glVertex2f(R.P.X + R.S.X, R.P.Y);
    //  glVertex2f(R.P.X + R.S.X, R.P.Y + R.S.Y);
    //  glVertex2f(R.P.X,         R.P.Y + R.S.Y);
    //glEnd;
    Exit(Self);
  end;
  Filled.P := R.P;
  Filled.S := Vec2f(R.S.X, FWidth);
  RectFilled(Filled);
  Filled.S := Vec2f(FWidth, R.S.Y);
  RectFilled(Filled);
  Filled.P := Add2f(R.P, R.S);
  Filled.S := Vec2f(- R.S.X, - FWidth);
  RectFilled(Filled);
  Filled.S := Vec2f(- FWidth, - R.S.Y);
  RectFilled(Filled);
  {$ENDIF}
  Result := Self;
end;

function TDraw.Circle(const P: TVec2f; R: Single): TDraw;
{$IFNDEF ANDROID}
var
  Count, I: Integer;
  C, S: TFloat;
{$ENDIF}
begin
  {$IFNDEF ANDROID}
  glBegin(GL_LINE_LOOP);
    Count := GetCircleSegments(R);
    for I := 0 to Count - 1 do begin
        C := Cos((I / Count) * 2*pi);
        S := Sin((I / Count) * 2*pi);
        glVertex2f(P.X + R*C, P.Y + R*S);
    end;
  glEnd;
  {$ENDIF}
  Result := Self;
end;

function TDraw.RectFilled(const R: TRect2i): TDraw;
{$IFDEF ANDROID}
var
  VB: TVertexBuffer;
  IB: TIndexBuffer;
  Vertices: array[0..3] of TVec2f;
  // PROBLEM HERE
  //Vertices: array[0..3] of TVec2i;
  Indices: array[0..5] of TIndexBuffer.TIndex;
begin
  Vertices[0] := Vec2f(R.P);
  Vertices[1] := Vec2f(R.P.X + R.S.X, R.P.Y);
  Vertices[2] := Vec2f(R.P.X + R.S.X, R.P.Y + R.S.Y);
  Vertices[3] := Vec2f(R.P.X, R.P.Y + R.S.Y);
  //Vertices[0] := Vec2i(R.P);
  //Vertices[1] := Vec2i(R.P.X + R.S.X, R.P.Y);
  //Vertices[2] := Vec2i(R.P.X + R.S.X, R.P.Y + R.S.Y);
  //Vertices[3] := Vec2i(R.P.X, R.P.Y + R.S.Y);
  VB.Init(Vertices);
  Indices[0] := 0; Indices[1] := 1; Indices[2] := 3;
  Indices[3] := 1; Indices[4] := 2; Indices[5] := 3;
  IB.Init(Indices);
  VB.DrawTriangles(IB);
  IB.Done;
  VB.Done;
  Result := Self;
end;
{$ELSE}
begin
  glBegin(GL_QUADS);
    glVertex2iv(@R.P);
    glVertex2i(R.P.X + R.S.X, R.P.Y);
    glVertex2i(R.P.X + R.S.X, R.P.Y + R.S.Y);
    glVertex2i(R.P.X, R.P.Y + R.S.Y);
  glEnd;
  Result := Self;
end;
{$ENDIF}

function TDraw.RectFilled(const R: TRect2f): TDraw;
{$IFDEF ANDROID}
var
  VB: TVertexBuffer;
  IB: TIndexBuffer;
  Vertices: array[0..3] of TVec2f;
  // PROBLEM HERE
  //Vertices: array[0..3] of TVec2i;
  Indices: array[0..5] of TIndexBuffer.TIndex;
begin
  Vertices[0] := Vec2f(R.P);
  Vertices[1] := Vec2f(R.P.X + R.S.X, R.P.Y);
  Vertices[2] := Vec2f(R.P.X + R.S.X, R.P.Y + R.S.Y);
  Vertices[3] := Vec2f(R.P.X, R.P.Y + R.S.Y);
  VB.Init(Vertices);
  Indices[0] := 0; Indices[1] := 1; Indices[2] := 3;
  Indices[3] := 1; Indices[4] := 2; Indices[5] := 3;
  IB.Init(Indices);
  VB.DrawTriangles(IB);
  IB.Done;
  VB.Done;
  Result := Self;
end;
{$ELSE}
begin
  glBegin(GL_QUADS);
    glVertex2fv(@R.P);
    glVertex2f(R.P.X + R.S.X, R.P.Y);
    glVertex2f(R.P.X + R.S.X, R.P.Y + R.S.Y);
    glVertex2f(R.P.X, R.P.Y + R.S.Y);
  glEnd;
  Result := Self;
end;
{$ENDIF}

function TDraw.CircleFilled(const P: TVec2f; R: Single): TDraw;
{$IFDEF ANDROID}
var
  VB: TVertexBuffer;
  IB: TIndexBuffer;
  I, Count: LongInt;
  C, S: TFloat;
  Vertices2f: array of TVec2f;
begin
  Log('TDraw.CircleFilled');
  Count := GetCircleSegments(R);
  SetLength(Vertices2f, Count + 1);
  VB.Init(Vertices2f);
  //VB.Init;
  //VB.SetCount(Count + 1);
  IB.Init;
  IB.SetCount(3 * Count);
  I := 0;
  //VB.Vertices2f[0] := P;
  Vertices2f[Count] := P;
  for I := 0 to Count - 1 do begin
    C := Cos((I / Count) * 2*pi);
    S := Sin((I / Count) * 2*pi);
    //VB.Vertices2f[I] := Vec2f(P.X + R*C, P.Y + R*S);
    Vertices2f[I] := Vec2f(P.X + R*C, P.Y + R*S);
    IB.Indices[3*I + 0] := Count;
    IB.Indices[3*I + 1] := I;
    IB.Indices[3*I + 2] := (I + 1) mod Count;
  end;
  VB.DrawTriangles(IB);
  IB.Done;
  VB.Done;
  Log('TDraw.CircleFilled done');
  Result := Self;
end;
{$ELSE}
var
  Count, I: Integer;
  C, S: TFloat;
begin
  glBegin(GL_POLYGON);
    Count := GetCircleSegments(R);
    for I := 0 to Count - 1 do begin
        C := Cos((I / Count) * 2*pi);
        S := Sin((I / Count) * 2*pi);
        glVertex2f(P.X + R*C, P.Y + R*S);
    end;
  glEnd;
  Result := Self;
end;
{$ENDIF}

function TDraw.Triangle(const X1, Y1, X2, Y2, X3, Y3: Single): TDraw;
{$IFDEF ANDROID}
var
  VB: TVertexBuffer;
  V: array[0..2] of TVec2f;
begin
  V[0] := Vec2f(X1, Y1);
  V[1] := Vec2f(X2, Y2);
  V[2] := Vec2f(X3, Y3);
  VB.Init(V);
  VB.DrawTriangles;
  VB.Done;
  Result := Self;
end;
{$ELSE}
begin
  glBegin(GL_TRIANGLES);
    glVertex2f(X1, Y1);
    glVertex2f(X2, Y2);
    glVertex2f(X3, Y3);
  glEnd;
  Result := Self;
end;
{$ENDIF}

function TDraw.Triangle(const V1, V2, V3: TVec2f): TDraw;
{$IFDEF ANDROID}
var
  VB: TVertexBuffer;
  V: array[0..2] of TVec2f;
begin
  V[0] := V1;
  V[1] := V2;
  V[2] := V3;
  VB.Init(V);
  VB.DrawTriangles;
  VB.Done;
  Result := Self;
end;
{$ELSE}
begin
  glBegin(GL_TRIANGLES);
    glVertex2fv(@V1);
    glVertex2fv(@V2);
    glVertex2fv(@V3);
  glEnd;
  Result := Self;
end;
{$ENDIF}

function TDraw.Polygon(P: PPolygon2f): TDraw;
{$IFDEF ANDROID}
var
  VB: TVertexBuffer;
begin
  VB.Init(P^.V);
  VB.DrawLineLoop;
  VB.Done;
  Result := Self;
end;
{$ELSE}
var
  I: LongInt;
begin
  glBegin(GL_LINE_LOOP);
    for I := 0 to High(P^.V) do
      glVertex2fv(@P^.V[I]);
  glEnd;
  Result := Self;
end;
{$ENDIF}

function TDraw.FilledPolygon(P: PPolygon2f): TDraw;
{$IFDEF ANDROID}
var
  VB: TVertexBuffer;
begin
  VB.Init(P^.V);
  VB.DrawPolygon;
  VB.Done;
  Result := Self;
end;
{$ELSE}
var
  I: LongInt;
begin
  glBegin(GL_POLYGON);
    for I := 0 to High(P^.V) do
      glVertex2fv(@P^.V[I]);
  glEnd;
  Result := Self;
end;
{$ENDIF}

function TDraw.Line(const A, B: TVec3f): TDraw;
begin
  {$IFNDEF ANDROID}
  glBegin(GL_LINES);
    glVertex3fv(@A);
    glVertex3fv(@B);
  glEnd;
  {$ENDIF}
  Result := Self;
end;

function TDraw.AxisLines(const P: TVec3f; Size: Single = 1): TDraw;
begin
  RGBA(255, 0, 0, 255).Line(P, Vec3f(Size, 0, 0));
  RGBA(0, 255, 0, 255).Line(P, Vec3f(0, Size, 0));
  RGBA(0, 0, 255, 255).Line(P, Vec3f(0, 0, Size));
  Result := Self;
end;

function TDraw.Triangles(var V: array of TVec3f): TDraw;
var
  VB: TVertexBuffer;
begin
  VB.Init(V);
  VB.DrawTriangles;
  VB.Done;
  Result := Self;
end;

function TDraw.Triangles(V: PVec2f; Count: SizeUInt): TDraw; overload;
var
  VB: TVertexBuffer;
begin
  VB.Init;
  VB.SetVertices(V^, Count);
  VB.DrawTriangles;
  VB.Done;
  Result := Self;
end;

function TDraw.Triangles(var V: array of TVec2f): TDraw;
var
  VB: TVertexBuffer;
begin
  VB.Init(V);
  VB.DrawTriangles;
  VB.Done;
  Result := Self;
end;

function TDraw.Triangle(const V1, V2, V3: TVec3f): TDraw;
var
  V: array[0..2] of TVec3f;
begin
  V[0] := V1;
  V[1] := V2;
  V[2] := V3;
  Result := Triangles(V);
end;

function TDraw.AxisAlignedBox(const A, B: TVec3f): TDraw;
const
  Indices: array[0 .. 36 - 1] of TIndexBuffer.TIndex = (
    0, 2, 1,
    0, 3, 2,
    0, 1, 5,
    0, 5, 4,
    0, 7, 3,
    0, 4, 7,
    3, 6, 2,
    3, 7, 6,
    1, 6, 5,
    1, 2, 6,
    4, 6, 7,
    4, 5, 6
  );
var
  VB: TVertexBuffer;
  IB: TIndexBuffer;
  Vertices: array[0 .. 8 - 1] of TVec3f;
begin
  Vertices[0] := A;
  Vertices[1] := Vec3f(B.X, A.Y, A.Z);
  Vertices[2] := Vec3f(B.X, B.Y, A.Z);
  Vertices[3] := Vec3f(A.X, B.Y, A.Z);
  Vertices[4] := Vec3f(A.X, A.Y, B.Z);
  Vertices[5] := Vec3f(B.X, A.Y, B.Z);
  Vertices[6] := B;
  Vertices[7] := Vec3f(A.X, B.Y, B.Z);
  VB.Init(Vertices);
  IB.Init(Indices);
  VB.DrawTriangles(IB);
  IB.Done;
  VB.Done;
  Result := Self;
end;

procedure TDraw.Buffer(Data: Pointer; const P, S: TVec2i);
begin
  {$IFNDEF ANDROID}
  glRasterPos2i(P.X, P.Y);
  glPixelZoom(1.0, -1.0);
  glDrawPixels(S.X, S.Y, GL_RGBA, GL_UNSIGNED_BYTE, Data);
  {$ENDIF}
end;

procedure TDraw.EndRender;
begin
  FWinApi.Window.SwapBuffers;
  LogErrors('TDraw.EndRender');
end;

procedure TDraw.ClearScreen(const Color: TVec4f);
begin
  glClearColor(Color.X, Color.Y, Color.Z, Color.W);
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
end;

procedure TDraw.ClearScreen(R, G, B, A: Byte);
begin
  glClearColor(R / 255, G / 255, B / 255, A / 255);
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
end;

procedure TDraw.ClearDepth;
begin
  glClear(GL_DEPTH_BUFFER_BIT);
end;

procedure TDraw.ReadScreen(Buf: PByte; W, H: LongInt);
var
  I: LongInt;
  Temp: array of Byte;
begin
  glReadPixels(0, 0, W, H, GL_RGBA, GL_UNSIGNED_BYTE, Buf);
  SetLength(Temp, 4 * W);
  for I := 0 to H div 2 - 1 do begin
    Move(Buf[I * 4 * W],            Temp[0],                    4 * W);
    Move(Buf[(H - I - 1) * 4 * W],  Buf[I * 4 * W],             4 * W);
    Move(Temp[0],                   Buf[(H - I - 1) * 4 * W],   4 * W);
  end;
end;

procedure TDraw.SetBlendMode(Mode: TBlendMode);
begin
  BlendMode(Mode);
end;

procedure TDraw.LogErrors(Name: PAnsiChar);
var
  err: LongInt;
begin
  err := glGetError();
  while err <> 0 do begin
    Log('OpenGL error: ' + Name + ': ' + IntToStr(err));
    err := glGetError();
  end;
end;
