Skip to content

3.3 Model

Erik van Bilsen edited this page Dec 31, 2016 · 3 revisions

🔗 Source Code

Now it is time to get our hands dirty and start creating the actual loading code. The goal of this tutorial is to create another class that represents a model in its entirety, that is, a model that contains multiple meshes, possibly with multiple objects. A house, that contains a wooden balcony, a tower and perhaps a swimming pool could still be loaded as a single model. We'll load the model from a OBJ file and turn it into TMesh objects we've created in the [last](3.2 Mesh) tutorial.

Without further ado, I present you the class structure of the IModel interface and corresponding TModel class:

type
  IModel = interface
  ['{29B5AB25-129C-4CA6-B368-146577F298F6}']
    procedure Draw;
  end;

type
  TModel = class(TInterfacedObject, IModel)
  private
    FMeshes: TObjectList<TMesh>;
    FDirectory: String;
    FShader: IShader;
  private
    procedure LoadModel(const APath: String);
    ...
  protected
    { IModel }
    procedure Draw;
  public
    constructor Create(const APath: String; const AShader: IShader);
    destructor Destroy; override;
  end;

The TModel class contains a list of TMesh objects and requires us to give it a file location (in assets.zip) in its constructor. It then loads the file right away via the LoadModel method that is called in the constructor. We also store the directory of the file path that we'll later need when loading the material library and textures.

The Draw methodn is nothing special and basically loops over each of the meshes to call their respective Draw method:

procedure TModel.Draw;
var
  I: Integer;
begin
  for I := 0 to FMeshes.Count - 1 do
    FMeshes[I].Draw;
end;

ℹ️ instead of a for..to loop, you can also use a for..in loop here (as in for Mesh in FMeshes do). However, using enumerators is a bit less efficient than using plain for loops, so I tend to avoid them in places where performance is important (although that is arguably not the case here).

Importing a 3D model

With the helper of the TParser class we created in the last tutorial, the LoadModel method can look like this:

procedure TModel.LoadModel(const APath: String);
var
  Parser: TParser;
begin
  FDirectory := TPath.GetDirectoryName(APath) + '/';
  Parser := TParser.Create(APath);
  try
    ParseOBJ(Parser);
  finally
    Parser.Free;
  end;
end;

Note that we extract the directory name from the file path since we need it later to locate material libraries and textures. (Remember to use forward slashes (/) instead of back slashes, since forward slashes work on all platforms).

Parsing an OBJ file now reduces to reading commands and arguments until the end of the file has been reached, and handling each command we want to support:

procedure TModel.ParseOBJ(const AParser: TParser);
var
  Command, Arg1, Arg2, Arg3: String;
begin
  while AParser.ReadLine(Command, Arg1, Arg2, Arg3) do
  begin
    case Command.Chars[0] of
      'f': if (Command = 'f') then
             { Parse face vertex. };

      'm': if (Command = 'mtllib') then
             LoadMtlLib(Arg1);

      'o': if (Command = 'o') then
             { Start a new object/mesh. Store previous mesh if any. }
             StoreMesh;

      'u': if (Command = 'usemtl') then
             { Assign material from material library to mesh. }

      'v': if (Command = 'v') then
             { Add position }
           else if (Command = 'vt') then
             { Add texture coordinate }
           else if (Command = 'vn') then
             { Add normal }
  end;
  StoreMesh;
end;

A little trick I often use when recognizing a string (like a Command in this example) is the use the first character of the string in a case-statement. This is faster than creating one long if-then-else chain for each possible value. (You could also use a dictionary to look up strings, but this method is even faster).

I did not show the source code for each specific command here. There are a few helper records and methods involved in handling these commands, and it would be too much code to show here. Besides, we want to focus this tutorial series on OpenGL and not on file parsing. But if you are interested in the inner workings, then by all means consult the source code in the Sample.Classes unit.

⬅️ [3.2 Mesh](3.2 Mesh) Contents [4.1 Depth Testing](4.1 Depth Testing) ➡️

Learn OpenGL (ES) with Delphi

    1. Getting Started
    • OpenGL (ES)
    • [Creating an OpenGL App](Creating an OpenGL App)
    • [1.1 Hello Window](1.1 Hello Window)
    • [1.2 Hello Triangle](1.2 Hello Triangle)
    • [1.3 Shaders](1.3 Shaders)
    • [1.4 Textures](1.4 Textures)
    • [1.5 Transformations](1.5 Transformations)
    • [1.6 Coordinate Systems](1.6 Coordinate Systems)
    • [1.7 Camera](1.7 Camera)
    • [Review](Getting Started Review)
    1. Lighting
    • [2.1 Colors](2.1 Colors)
    • [2.2 Basic Lighting](2.2 Basic Lighting)
    • [2.3 Materials](2.3 Materials)
    • [2.4 Lighting Maps](2.4 Lighting Maps)
    • [2.5 Light Casters](2.5 Light Casters)
    • [2.6 Multiple Lights](2.6 Multiple Lights)
    • [Review](Lighting Review)
    1. Model Loading
    • [3.1 OBJ Files](3.1 OBJ Files)
    • [3.2 Mesh](3.2 Mesh)
    • [3.3 Model](3.3 Model)
    1. Advanced OpenGL
    • [4.1 Depth Testing](4.1 Depth Testing)
    • [4.2 Stencil Testing](4.2 Stencil Testing)
    • [4.3 Blending](4.3 Blending)
    • [4.4 Face Culling](4.4 Face Culling)
    • [4.5 Framebuffers](4.5 Framebuffers)
    • [4.6 Cubemaps](4.6 Cubemaps)
    • [4.7 Advanced Data](4.7 Advanced Data)
    • [4.8 Advanced GLSL](4.8 Advanced GLSL)
    • [4.9 Geometry Shader](4.9 Geometry Shader)
    • 4.10Instancing
    • [4.11 Anti Aliasing](4.11 Anti Aliasing)
    1. Advanced Lighting
    • [5.1 Advanced Lighting](5.1 Advanced Lighting)
    • [5.2 Gamma Correction](5.2 Gamma Correction)
    • [5.3 Shadows](5.3 Shadows)
      • [5.3.1 Shadow Mapping](5.3.1 Shadow Mapping)
      • [5.3.2 Point Shadows](5.3.2 Point Shadows)
      • [5.3.3 CSM](5.3.3 CSM)
    • [5.4 Normal Mapping](5.4 Normal Mapping)
    • [5.5 Parallax Mapping](5.5 Parallax Mapping)
    • [5.6 HDR](5.6 HDR)
    • [5.7 Bloom](5.7 Bloom)
    • [5.8 Deferred Shading](5.8 Deferred Shading)
    • [5.9 SSAO](5.9 SSAO)
    1. PBR
    • Theory
    • [6.1 Lighting](6.1 Lighting)
    • IBL
      • [Diffuse Irradiance](Diffuse Irradiance)
      • [Specular IBL](Specular IBL)
    1. In Practice
    • [7.1 Debugging](7.1 Debugging)
    • [Text Rendering](Text Rendering)
    • [2D Game](2D Game)

Clone this wiki locally