Tuesday, February 12, 2019

Simple 3D Hello World in Firemonkey!

Games with FMX!

Is Firemonkey ready for game development - of course! There are many games out in the wild written with Delphi and also with FMX.

Why this blogpost? Because in the last weeks there are so many questions in forums or FB about this topic.

But where to start?

Do you want to go 2D? 


Great simple answer:

1.) Use a TRectangle with a PNG for your sprite(s).
2.) Use a Timer with 16ms for your game-loop.

Go and write - like me in 2015 - funny games like this:



Perhaps sometime in the future, I will release this game, if I find the time and change the stolen ROM-Images with new creations. ;-)

Do you want 3D? 


Then you might want to use a "3D Engine". Why an engine, because you do not want to do the hard work for all platforms by hand. Or you just use FMX as your engine.

This could look like this:

  1. Create a new FMX 3D application.
  2. Put a dummy object on the form
  3. Put a camera on the form
  4. Put a light on the form
  5. Place the camera at 0,0,-20
  6. Set the light to Point
  7. Place the light at 0,0,-20
  8. Put a Timer on the form and set it to 16ms
  9. In the onTimer event call Invalidate;
  10. In the onRender event call your 3D stuff.
The Form should look like this:


type
  THello3DWorld = class(TForm3D)
    Dummy1: TDummy;
    Camera1: TCamera;
    Timer1: TTimer;
    Light1: TLight;
    procedure Timer1Timer(Sender: TObject);
    procedure Form3DCreate(Sender: TObject);
    procedure Form3DDestroy(Sender: TObject);
    procedure Form3DRender(Sender: TObject; Context: TContext3D);
  private
    { Private-Deklarationen }
    fColorMaterial : TColorMaterial;
    fModelMatrix,
    fRotate        : TMatrix3D;
    fVertexBuffer  : TVertexBuffer;
    fIndexBuffer   : TIndexBuffer;
    Procedure DoInit;
    Procedure DoRotate;
  public
    { Public-Deklarationen }
  end;

In the FormCreate you can create, like in the example - a 3D object. I use a simple Triangle, but you can, of course, create any 3D Mesh or object. You also have to create Material. You can use your own Material (shader) or use the build-in.

This looks like this:

procedure THello3DWorld.Form3DCreate(Sender: TObject);
begin
  fColorMaterial := TColorMaterial.Create;
  fModelMatrix   := TMatrix3D.Identity;

  // 3 for a Triangle 
  fVertexBuffer := TVertexBuffer.Create([TVertexFormat.Vertex],3);
  fIndexBuffer  := TIndexBuffer.Create(3,TIndexFormat.UInt16);

  DoInit;
  Timer1.Enabled := true;
end;

In the DoInit, I setup a triangle for this little demo:

procedure THello3DWorld.DoInit;
var
  lVector : TVector3D;
  lMatrix : TMatrix3D;
begin
  // Create a Rotation Matrix for a equilateral triangle
  lMatrix := TMatrix3D.CreateRotationZ(120*Pi/180);
  LVector := TVector3D.Create(3,0,0); // Size 3

  fVertexBuffer.Vertices[0] := LVector;

  LVector := LVector * LMatrix; // Rotate to next point

  fVertexBuffer.Vertices[1] := LVector;

  LVector := LVector * LMatrix; // Rotate to next point

  fVertexBuffer.Vertices[2] := LVector;

  // Draw Clockwise
  fIndexBuffer.Indices[0]   := 0;
  fIndexBuffer.Indices[1]   := 1;
  fIndexBuffer.Indices[2]   := 2;

  // Create Rotation Matrix for later use 
  fRotate := TMatrix3D.CreateRotationZ(3*Pi/180);
end;

In the on Form3DRender event you can provide all your generated objects to the renderer.

procedure THello3DWorld.Form3DRender(Sender: TObject; Context: TContext3D);
begin
  if ( Context = nil ) or not( Context.BeginScene ) then
    exit;

  try
    Context.SetContextState( TContextState.cs3DScene );
    Context.Clear( TAlphaColorRec.Black ); // or any Color

    // Set ModelMatrix
    Context.SetMatrix( fModelmatrix );

    fColorMaterial.Color := TAlphaColorRec.Yellow;

    // Render Model
    Context.DrawTriangles( fVertexBuffer, fIndexBuffer,
                                          fColorMaterial, 1 );
  finally
    Context.EndScene;
  end;
end;

That's it. Now your FMX program could render any 3D object to the scene.

For entertainment, the object should rotate or do whatever you like. That's why this demo has a DoRotate method.

procedure THello3DWorld.DoRotate;
begin
  fModelMatrix := fModelMatrix * fRotate;
end;

This is a simple Matrix3D rotation. To use this rotation just call DoRotate before you call the invalidate. Normally you would measure the time difference between two frames and do the calculation for this deltaT to get even rotation speeds on all platform / CPUs. But with our timer we are fine for this demo.




With a collection of elements and some logic you can build your own 3D Engine and create some nice 3D stuff or games.





If you like to see more 3D Stuff write me a message.



No comments:

Post a Comment