2
votes

I've a question about using OpenGL from Delphi. My intention is to draw a grid of n rows and n columns in a form. i want the cells in the grid to be square like this image:

enter image description here

This is the code I use to create this grid:

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, Winapi.OpenGL, System.SysUtils,
  System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,
  Vcl.Dialogs, Vcl.Menus;

type
  TfrmMain = class(TForm)
    PopupMenu: TPopupMenu;
    mnuDrawGrid: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure mnuDrawGridClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    glDC: HDC;
    oldW: Integer;
    oldH: Integer;
    glContext: HGLRC;
    errorCode: GLenum;
    openGLReady: Boolean;
    procedure DisegnaLinea(const aX1, aY1, aX2, aY2: Double; aSpessore,
      aOffSet: Integer);
    procedure DisegnaTabellone(const aNumRighe, aNumColonne: SmallInt);
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

{ Gestione form -------------------------------------------------------------- }

// OpenGL initialization
procedure TfrmMain.FormCreate(Sender: TObject);
var
  pfd: TPixelFormatDescriptor;
  formatIndex: Integer;
begin
  FillChar(pfd, SizeOf(pfd), 0);
  with pfd do
  begin
    nSize := SizeOf(pfd);
    nVersion := 1;
    dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL;
    iPixelType := PFD_TYPE_RGBA;
    cColorBits := 24;
    cDepthBits := 32;
    iLayerType := PFD_MAIN_PLANE;
  end;
  glDC := GetDC(Handle);
  formatIndex := ChoosePixelFormat(glDC, @pfd);
  if formatIndex = 0 then
    raise Exception.Create('Choose pixel format failed ' + IntToStr(GetLastError));
  if not SetPixelFormat(glDC, formatIndex, @pfd) then
    raise Exception.Create('Set pixel forma failed ' + IntToStr(GetLastError));
  glContext := wglCreateContext(glDC);
  if not glContext = 0 then
    raise Exception.Create('Create context failed ' + IntToStr(GetLastError));
  if not wglMakeCurrent(glDC, glContext) then
    raise Exception.Create('Make current failsed ' + IntToStr(GetLastError));

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  glOrtho(0.0, ClientWidth, 0.0, ClientHeight, 0.0, 1.0);

  oldW := ClientWidth;
  oldH := ClientHeight;

  openGLReady := True;
end;

// OpenGL destruction
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  wglMakeCurrent(Canvas.Handle, 0);
  wglDeleteContext(glContext);
end;

// Form resize
procedure TfrmMain.FormResize(Sender: TObject);
begin
  if not openGLReady then
    exit;
  glViewport(0, 0, ClientWidth, ClientHeight);
  errorCode := glGetError;
  if errorCode <> GL_NO_ERROR then
    raise Exception.Create('Form resize: ' + gluErrorString(errorCode));
  if (ClientWidth <> oldW) and (ClientHeight <> oldH) then
    DisegnaTabellone(10, 10);
  oldW := ClientWidth;
  oldH := ClientHeight;
end;


{ Gestione menu -------------------------------------------------------------- }

// Menu option grid drawing
procedure TfrmMain.mnuDrawGridClick(Sender: TObject);
begin
  DisegnaTabellone(10, 10);
end;



{ OpenGL --------------------------------------------------------------------- }

// Draw a line at aX1, aY1 to aX2, aY2 coordinates
procedure TfrmMain.DisegnaLinea(const aX1, aY1, aX2, aY2: Double; aSpessore,
  aOffSet: Integer);
begin
  glEnable(GL_LINE_SMOOTH);
  glLineWidth(aSpessore);
  glBegin(GL_LINES);
    glVertex2f(aX1, aY1);
    glVertex2f(aX2, aY2);
  glEnd;
end;

// Grid design
procedure TfrmMain.DisegnaTabellone(const aNumRighe, aNumColonne: SmallInt);
const
  vOffSet = 20;
var
  idx: SmallInt;
  pX: Double;
  pY: Double;
  incX: Double;
  incY: Double;
  hPos: Double;
  hWidth: Double;
  widthArea: Integer;
  heightArea: Integer;
  aspectRatio: Double;
begin
  glClearColor(1.0, 1.0, 1.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT);
  glColor3f(0.0, 0.0, 0.0);

  widthArea := ClientWidth;
  heightArea := ClientHeight;
  aspectRatio := widthArea / heightArea;

  pY := vOffSet / 2;
  incY := (heightArea - vOffSet) / aNumRighe;

  pX := (widthArea - aNumColonne * incY) / 2;
  hPos := pX;
  hWidth := hPos + aNumColonne * incY;
  incX := incY;

  // Draw vertical lines
  for idx := 0 to aNumColonne do begin
    DisegnaLinea(pX, vOffSet / 2, pX, heightArea - vOffSet / 2, 3, 0);
    pX := pX + incX;
  end;

   // Draw horizontal lines
  for idx := 0 to aNumRighe do begin
    DisegnaLinea(hPos, pY, hWidth, pY, 3, 0);
    pY := pY + incY;
  end;

  glFlush;
end;

end.

I initialize the pixel format description in the FormCreate event and Ive created a routine called by a popup menu in order to draw the grid. In this routine I make same calculation because I want the grid cells to be square as I said. So I take the height of the form (ClientHeight) and I divide it for the number of rows I need: in this case 10 plus i'ce a little offset in order to have a margin on the top and bottom of the form. Then I calculate the width of the grid for a perfet square. It works well but the problem come when I resize the form. My intension is to draw a new grid smaller or larger according to the form dimensions, but this is only my intention because the grid does'nt mantein the correct aspet ratio and is drawn in a bad manner as ou can see in the following picure:

enter image description here

I can't understand where is the error in my code. I'm nwe at OpenGL so can someone help me?

Eros

1
I recomend using a debugger to see the extreme values you draw with DisegnaLinea() - Ripi2

1 Answers

2
votes
  1. Although you set the new glViewport() on resize, you still use the old orthographic projection matrix which is no more relevant.
  2. So, all you need is an additional glOrtho() before setting a new viewport.
  3. OpenGL immediate mode (glBegin(), glEnd(), glVertex(), etc.) had been obsolete for nearly twenty years; you really shouldn`t be using it in 2017.