BAD LOOKING CUBE - DEMOTOOL/ENGINE

BLC screenshot

A demotool I'm building using Delphi 7 and OpenGL (Classic Version 1). Targeting Windows 2000 PCs.

Architecture

BAD LOOKING CUBE DEMOTOOL PIPELINE
Blender + Plugin CSV Screenplay Interpreter OpenGL Renderer and Time-based Animation
Python Delphi 7 Pascal Delphi 7 Pascal, OpenGL (1.x)
  • Designing models and vertex painting
  • Exporting OBJ's
  • Set dressing level
  • Animating Camera movement
  • Setting basic scene properties (sky, fog)
  • Exporting scene group and animations to CSV
  • Reading CSV
  • Loading models
  • Setting up scene
  • Filling objects with animations from CSV
  • Window rendering
  • 3D Scene rendering
  • Translating, scaling, rotating, and cloning objects
  • Linear and smooth interpolation (time-based)
  • Smooth and flat shading
  • Camera FOV
  • Fog
  • MIDI playback

Useful Code Snippets

As I was writing new engine from scratch I collected a bunch of useful tricks. I'm sharing it for everyone!

Enabling V-SYNC (GL)

type
  TWglSwapIntervalEXT = procedure(interval: GLInt); stdcall;

var
  wglSwapIntervalEXT: TWglSwapIntervalEXT;
procedure InitVSync;
begin
  if not Assigned(wglSwapIntervalEXT) then
  begin
    wglSwapIntervalEXT := wglGetProcAddress('wglSwapIntervalEXT');
    if not Assigned(wglSwapIntervalEXT) then Exit;
  end;
  wglSwapIntervalEXT(1);
end;

Fixing Window Blinking (Redraing)

Rendering OpenGL content in a window is in conflict with internal redrawing of a window. To fix this you need to disable this by overwiting internal procedure. 

TFormDemo = class(TForm)
private
  procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
      
procedure TFormDemo.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  Message.Result := 1;
end;

Lerping

function Lerp(A, B, T: glFloat; Linear: Boolean): glFloat;
var
  TD: Double;
begin
  if Linear then
  begin
    Result := A + (B - A) * T;
  end
  else
  begin
    TD := (1-Cos(T*PI))/2;
    Result := A*(1-TD)+B*TD;
  end;
end;

Accessing Relative Files

At the creation of a window set current path.

SetCurrentDir(ExtractFilePath(Application.ExeName));

Then access file in current (binary) directory.

var
  CSVFile: TextFile;
  Line: String;
  Fields: TStringList;

begin
  AssignFile(CSVFile, 'screenplay.csv');
  Reset(CSVFile);
  Fields := TStringList.Create;
  Fields.Delimiter := ',';
  try
    while not Eof(CSVFile) do
    begin
      ReadLn(CSVFile, Line);
      Fields.DelimitedText := Line;
      ...
    end
  finally
    Fields.Free;
    CloseFile(CSVFile);
  end;
end;

Frame Time Calculation

var
  LastFrameTime, CurrentTime, FrameTime: TDateTime;
// render loop
begin
  CurrentTime := Now;
  FrameTime := MilliSecondsBetween(CurrentTime, LastFrameTime);
  LastFrameTime := CurrentTime;

  ... render frame ...
  
  LabelFrameTime.Caption := Format('%.2fms', [FrameTime]);
end;

Loading Models from Resourece File

Create resource definition file.

models.rc

Inside write resource name and path to actual file. One file per line.

SM_Skybox RCDATA Models/skybox.obj
SM_Terrain RCDATA Models/terrain.obj
SM_Model01 RCDATA Models/model_01.obj
...

Compile resource package. This command will create models.RES package file.

brcc32 models.rc

Add resource files (package + definitions).

implementation
  {$R 'models.RES' 'models.rc'}

Reading data from resource.

function ReadOBJFileFromResource(const ResourceName: string): TModel;
var
  ResStream: TResourceStream;
  FileLines: TStringList;
  i,f: Integer;
  Line: string;
  Model: TModel;
begin
  ResStream := TResourceStream.Create(HInstance, ResourceName, RT_RCDATA);
  FileLines := TStringList.Create;

  try
    FileLines.LoadFromStream(ResStream);

    for i := 0 to FileLines.Count - 1 do
    begin
      Line := Trim(FileLines[i]);
     
      ... read data for model Model ...

    end;
  finally
    FileLines.Free;
  end;
  
  Result := Model;
end;
Model := ReadOBJFileFromResource('SM_Model01');

Calculate Position Between Points for Lerp

Based on current demo time calculate value for lerp between two points in timeline. This makes time-based animation easy. 

function CalcPos(A,B,T: glFloat): glFloat;
begin
  Result := 0;
  if A<>B then
  begin
    Result := (T-A)/(B-A);
  end
  else
  begin
    Result := Max(0.0,Min(Result,1.0));
  end;
end;
Pos := CalcPos(Timeline[Prev].Timestamp, Timeline[Next].Timestamp, DemoTime);
Position.X := Lerp(Timeline[Prev].Params[_X], Timeline[Next].Params[_X], Pos);

Gallery

...

Links

...


BACK TO INDEX