unit VertexBufferObject;

interface

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

type
  TVertexBufferObject = class(TVertexArray)
  protected
    FVertexBuffer       : cardinal;
    FIndexBuffer        : cardinal;
  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 TVertexBufferObject.Create;
begin
  if not Load_GL_ARB_vertex_buffer_object then
    raise Exception.Create('GL_ARB_vertex_buffer_object required!');

  inherited Create;
end;

destructor TVertexBufferObject.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_INDEX_ARRAY);
    end;
    glDeleteBuffersARB(1, @FVertexBuffer);
    glDeleteBuffersARB(1, @FIndexBuffer);

    FreePool;
  end;
end;

procedure TVertexBufferObject.Init;
var
  i: cardinal;
begin
  case FBufferMode of
    STATIC_MODE    : FBufferMode := GL_STATIC_DRAW_ARB;
    DYNAMIC_MODE   : FBufferMode := GL_DYNAMIC_DRAW_ARB;
    else FBufferMode := GL_STREAM_DRAW_ARB;
  end;

  glGenBuffersARB(1, @FVertexBuffer);
  glBindBufferARB(GL_ARRAY_BUFFER_ARB, FVertexBuffer);
  glBufferDataARB(GL_ARRAY_BUFFER_ARB, pointer(TotalVertexSize), nil, FBufferMode);
  glGenBuffersARB(1, @FIndexBuffer);
  glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, FIndexBuffer);
  glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, pointer(TotalIndexSize), nil, FBufferMode);

  if VertexSize > 0 then
  begin
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(4, GL_FLOAT, 0, pointer(VertexOffset));
  end;
  if Color0Size > 0 then
  begin
    glEnableClientState(GL_COLOR_ARRAY);
    glColorPointer(4, GL_FLOAT, 0, pointer(Color0Offset));
  end;
  if Color1Size > 0 then
  begin
    glEnableClientState(GL_SECONDARY_COLOR_ARRAY_EXT);
    glSecondaryColorPointerEXT(4, GL_FLOAT, 0, pointer(Color1Offset));
  end;
  if NormalSize > 0 then
  begin
    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer(GL_FLOAT, 0, pointer(NormalOffset));
  end;
  for i := 0 to 31 do
    if TexCoordSize[i] > 0 then
    begin
      glClientActiveTexture(GL_TEXTURE0 + i);
      glEnableClientState(GL_TEXTURE_COORD_ARRAY);
      glTexCoordPointer(4, GL_FLOAT, 0, pointer(TexCoordOffset[i]));
    end;
  if EdgeFlagSize > 0 then
  begin
    glEnableClientState(GL_EDGE_FLAG_ARRAY);
    glEdgeFlagPointer(0, pointer(EdgeFlagOffset));
  end;
  if FogCoordSize > 0 then
  begin
    glEnableClientState(GL_FOG_COORDINATE_ARRAY_EXT);
    glFogCoordPointerEXT(GL_FLOAT, 0, pointer(FogCoordOffset));
  end;
  if TotalIndexSize > 0 then
  begin
    glEnableClientState(GL_INDEX_ARRAY);
    glIndexPointer(GL_INT, 0, pointer(0));
  end;
  Unlock;

  InitPool;

  Initialized           := true;
end;

procedure TVertexBufferObject.Lock;
begin
  glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
  glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
end;

procedure TVertexBufferObject.Unlock;
var
  i: cardinal;
begin
  VertexPointer := cardinal(glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB));
  IndexPointer := cardinal(glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB));

  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 TVertexBufferObject.Render;
begin
  glDrawElements(mode, num, GL_UNSIGNED_INT, pointer(base*4));
end;

end.
