Skip to content

3D Programming with Delphi for .NET and WPF

Note: Make sure to install RAD Studio 2007 December Update before trying to compile this code. If you are on XP, make sure you have .NET 3.0 or 3.5 installed.

Do you know Charles Petzold? Yes. He is the one who authored classical "Programming Windows" book back in eighties. Last year Petzold wrote a very good guide to WPF programming under intriguing title "Applications = Code + Markup", which is a very obvious reference to another classical book written in 1975 by creator of Pascal programming language Niklas Wirth "Algorithms + Data Structures = Programs".
When I heard that Petzold is writing a book on "3D Programming for Windows", I knew that I needed to have this book on my bookshelf.

I was studying 3D visualization algorithms in Warsaw University of Technology, so quaternions, matrices and this type of mathematics do not scare me. Quite contrary I like it a lot. When operator overloading was introduced in Delphi 2006 for Win32 compiler, my first thought was to implement basic operations for quaternions like "add" and "subtract". At the end quaternions are just plain 4-D vectors, that happened to be very useful representations of arbitrary rotations in 3-D space.

Windows Presentation Foundation comes with the impressive number of specialized types for advanced 3D programming.
I like to think about these classes as a kind of standardization for abstractions used in 3D graphics.

Delphi for .NET Simple Scene in Code

"3D Programming for Windows" contains many useful examples of code, and I thought it would be fun to translate one of them to Delphi for .NET. I have decided to implement "Simple Scene In Code" example, that contains a simple scene with just one triangle, camera and a combo box to change the position of the camera and look at the triangle from different angles.


The steps to get started with Delphi for .NET WPF application were outlined in my post on template libraries a few weeks ago. In principle you need to create a new Delphi for .NET Console Application project, and add project references to .NET assemblies where WPF types are implemented. From this point it just a matter of adding correct namespace to "uses" clauses of your Delphi units and you should be up and running.

The program file looks like this:

program DelphiSimpleSceneInCode;      

// {$APPTYPE CONSOLE}      

uses
  System.Windows,
  uFormMain in 'uFormMain.pas';      

var
  app: Application;      

[STAThread]
begin
  try
    app := Application.Create;
    app.Run(TFormMain.Create);      

  except
    on E:Exception do
    begin
      Writeln(E.Classname, ': ', E.Message);
      Readln;
    end;
  end;
end.

Note that I have commented out the {$APPTYPE CONSOLE} directive, so the console window is not displayed.

The main program functionality is implemented inside the "TFormMain" class that represents the main window of my application. Below is the implementation of the "TFormMain" class.

unit uFormMain;      

interface      

uses
  System.Windows,
  System.Windows.Controls,
  System.Windows.Controls.Primitives,
  System.Windows.Media,
  System.Windows.Media.Media3D;      

type
  TFormMain = class(Window)
  strict private
    FCam: PerspectiveCamera;
    procedure ScrollBarOnValueChanged(Sender: TObject;
      args: RoutedPropertyChangedEventArgs);
  public
    constructor Create;
    destructor Destroy; override;
  end;      

implementation      

{ TFormMain }      

constructor TFormMain.Create;
var
  dock: DockPanel;
  scroll: ScrollBar;
  viewport: Viewport3D;
  mesh: MeshGeometry3D;
  indices: Int32Collection;
  geomod: GeometryModel3D;
  modvis: ModelVisual3D;      

begin
  inherited Create;      

  self.Title := 'Delphi for .NET Simple 3D Scene in Code';      

  // Make DockPanel content of window.
  dock := DockPanel.Create;
  self.Content := dock;      

  // Create Scrollbar for moving camera.
  scroll := ScrollBar.Create;
  scroll.Orientation := Orientation.Horizontal;
  scroll.Value := -2;
  scroll.Minimum := -2;
  scroll.Maximum := 2;      

  Include(scroll.ValueChanged, ScrollBarOnValueChanged);      

  dock.Children.Add(scroll);
  DockPanel.SetDock(scroll, System.Windows.Controls.Dock.Bottom);      

  // Create Viewport3D for 3D scene.
  viewport := Viewport3D.Create;
  dock.Children.Add(viewport);      

  // Define the MeshGeometry3D.
  mesh := MeshGeometry3D.Create;
  mesh.Positions.Add(Point3D.Create(0, 0, 0));
  mesh.Positions.Add(Point3D.Create(0, 1, -1));
  mesh.Positions.Add(Point3D.Create(0, 0, -2));
  indices := Int32Collection.Create;
  indices.Add(0);
  indices.Add(1);
  indices.Add(2);
  mesh.TriangleIndices := indices;      

  // Define the GeometryModel3D.
  geomod := GeometryModel3D.Create;
  geomod.Geometry := mesh;
  geomod.Material := DiffuseMaterial.Create(Brushes.Cyan);
  geomod.BackMaterial := DiffuseMaterial.Create(Brushes.Red);      

  // Create ModelVisual3D for GeometryModel3D.
  modvis := ModelVisual3D.Create;
  modvis.Content := geomod;
  viewport.Children.Add(modvis);      

  // Create another ModelVisual3D for light.
  modvis := ModelVisual3D.Create;
  modvis.Content := AmbientLight.Create(Colors.White);
  viewport.Children.Add(modvis);      

  // Create the camera.
  FCam := PerspectiveCamera.Create(
    Point3D.Create(-2,0,5),
    Vector3D.Create(0, 0, -1),
    Vector3D.Create(0, 1, 0),
    45);      

  viewport.Camera := FCam;
end;      

destructor TFormMain.Destroy;
begin
  inherited;
end;      

procedure TFormMain.ScrollBarOnValueChanged(Sender: TObject;
  args: RoutedPropertyChangedEventArgs);
begin
  FCam.Position := Point3D.Create(args.NewValue, 0, 5);
end;      

end.

Translating c# code to Delphi for .NET is very straightforward, however there are certain things you need to know. For example syntax for adding event handlers to events. .NET features multicast event handlers. This is different as compared to traditional Delphi for Win32 programs that sport singlecast events. In c# to add "ScrollBarOnValueChanged" event handler to "ValueChanged" event of the combobox overloaded "+=" operator is used. Delphi uses "Include" and "Exclude" standard routines to respectively add and remove an event handler from an event.

The source code for this post can be downloaded from CodeCentral

Happy 3D coding:-)

Merry Christmas and a Happy New Year for all of You!

{ 1 } Comments

  1. Stewart Gaskell | December 19, 2007 at 3:43 pm | Permalink

    The code doesn’t compile!
    You’ve actually left a comment on the offending line - Include(scroll.ValueChanged, ScrollBarOnValueChanged);

    How to fix?

Post a Comment

Your email is never published nor shared. Required fields are marked *

Bad Behavior has blocked 2 access attempts in the last 7 days.

Close