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
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