unit VertexArrayObject;

interface

uses Windows, SysUtils, Math3D, GL, GLu, GLext, WGLext, GLtools, VertexArray;

type
  TVertexArrayObject = class(TVertexArray)
  protected
    FVertexBuffer    : GLuint;
    FIndexBuffer     : GLuint;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure Init; override;
    procedure Lock; override;
    procedure Unlock; override;
    procedure Render(mode, num, base: cardinal); override;
  end;

implementation

constructor TVertexArrayObject.Create;
var
  i: cardinal;
begin
  if not Load_GL_ATI_vertex_array_object then
    raise Exception.Create('GL_ATI_vertex_array_object required!');
  if not Load_GL_ATI_element_array then
    raise Exception.Create('GL_ATI_element_array required!');
  if not Load_GL_ATI_vertex_attrib_array_object then
    raise Exception.Create('GL_ATI_vertex_attrib_array_object required!');
  if not Load_GL_ATI_map_object_buffer then
    raise Exception.Create('GL_ATI_map_object_buffer required!');

  TotalVertexSize       := 0;
  VertexSize            := 0;
  Color0Size            := 0;
  Color1Size            := 0;
  NormalSize            := 0;
  for i := 0 to 31 do
    TexCoordSize[i] := 0;
  EdgeFlagSize          := 0;
  FogCoordSize          := 0;
  TotalIndexSize        := 0;
  Initialized           := false;
end;

destructor TVertexArrayObject.Destroy;
var
  i: cardinal;
begin
  if Initialized then
  begin
    if VertexSize > 0 then
    begin
      glDisableClientState(GL_VERTEX_ARRAY);
    end;
    if Color0Size > 0 then
    begin
      glDisableClientState(GL_COLOR_ARRAY);
    end;
    if Color1Size > 0 then
    begin
      glDisableClientState(GL_SECONDARY_COLOR_ARRAY_EXT);
    end;
    if NormalSize > 0 then
    begin
      glDisableClientState(GL_NORMAL_ARRAY);
    end;
    for i := 0 to 31 do
      if TexCoordSize[i] > 0 then
      begin
        glClientActiveTexture(GL_TEXTURE0 + i);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
      end;
    if EdgeFlagSize > 0 then
    begin
      glDisableClientState(GL_EDGE_FLAG_ARRAY);
    end;
    if FogCoordSize > 0 then
    begin
      glDisableClientState(GL_FOG_COORDINATE_ARRAY_EXT);
    end;
    if TotalIndexSize > 0 then
    begin
      glDisableClientState(GL_ELEMENT_ARRAY_ATI);
    end;
    glFreeObjectBufferATI(FVertexBuffer);
    glFreeObjectBufferATI(FIndexBuffer);

    FreePool;
  end;
end;

procedure TVertexArrayObject.Init;
var
  i, mode: cardinal;
begin
  case FBufferMode of
    STATIC_MODE    : mode := GL_STATIC_ATI;
    DYNAMIC_MODE   : mode := GL_DYNAMIC_ATI;
    else mode := GL_STATIC_ATI;
  end;
  FVertexBuffer := glNewObjectBufferATI(TotalVertexSize, nil, mode);
  FIndexBuffer := glNewObjectBufferATI(TotalIndexSize, nil, mode);
  if VertexSize > 0 then
  begin
    glArrayObjectATI(GL_VERTEX_ARRAY, 4, GL_FLOAT, 0, FVertexBuffer, VertexOffset);
    glEnableClientState(GL_VERTEX_ARRAY);
  end;
  if Color0Size > 0 then
  begin
    glArrayObjectATI(GL_COLOR_ARRAY, 4, GL_FLOAT, 0, FVertexBuffer, Color0Offset);
    glEnableClientState(GL_COLOR_ARRAY);
  end;
  if Color1Size > 0 then
  begin
    glArrayObjectATI(GL_SECONDARY_COLOR_ARRAY_EXT, 4, GL_FLOAT, 0, FVertexBuffer, Color1Offset);
    glEnableClientState(GL_SECONDARY_COLOR_ARRAY_EXT);
  end;
  if NormalSize > 0 then
  begin
    glArrayObjectATI(GL_NORMAL_ARRAY, 3, GL_FLOAT, 0, FVertexBuffer, NormalOffset);
    glEnableClientState(GL_NORMAL_ARRAY);
  end;
  for i := 0 to 31 do
    if TexCoordSize[i] > 0 then
    begin
      glClientActiveTexture(GL_TEXTURE0 + i);
      glArrayObjectATI(GL_TEXTURE_COORD_ARRAY, 4, GL_FLOAT, 0, FVertexBuffer, TexCoordOffset[i]);
      glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    end;
  if EdgeFlagSize > 0 then
  begin
    glArrayObjectATI(GL_EDGE_FLAG_ARRAY, 4, GL_FLOAT, 0, FVertexBuffer, EdgeFlagOffset);
    glEnableClientState(GL_EDGE_FLAG_ARRAY);
  end;
  if FogCoordSize > 0 then
  begin
    glArrayObjectATI(GL_FOG_COORD_ARRAY, 4, GL_FLOAT, 0, FVertexBuffer, FogCoordOffset);
    glEnableClientState(GL_FOG_COORDINATE_ARRAY_EXT);
  end;
  if TotalIndexSize > 0 then
  begin
    glArrayObjectATI(GL_ELEMENT_ARRAY_ATI, 1, GL_UNSIGNED_INT, 0, FIndexBuffer, 0);
    glEnableClientState(GL_ELEMENT_ARRAY_ATI);
  end;
  Unlock;

  InitPool;

  Initialized           := true;
end;

procedure TVertexArrayObject.Lock;
begin
  glUnmapObjectBufferATI(FVertexBuffer);
  glUnmapObjectBufferATI(FIndexBuffer);
end;

procedure TVertexArrayObject.Unlock;
var
  i: cardinal;
begin
  VertexPointer := cardinal(glMapObjectBufferATI(FVertexBuffer));
  IndexPointer := cardinal(glMapObjectBufferATI(FIndexBuffer));

  Vertex                := pointer(VertexPointer + VertexOffset);
  Color0                := pointer(VertexPointer + Color0Offset);
  Color1                := pointer(VertexPointer + Color1Offset);
  Normal                := pointer(VertexPointer + NormalOffset);
  for i := 0 to 31 do
    TexCoord[i]         := pointer(VertexPointer + TexCoordOffset[i]);
  EdgeFlag              := pointer(VertexPointer + EdgeFlagOffset);
  FogCoord              := pointer(VertexPointer + FogCoordOffset);
  Index                 := pointer(IndexPointer);
end;

procedure TVertexArrayObject.Render;
begin
  glElementPointerATI(GL_UNSIGNED_INT, @Index[base]);
  glDrawElementArrayATI(mode, num);
end;

end.
