Loading, displaying a scene (simple loading of 3D models)

Car 3D model

We will now load a 3D model from file using the TCastleScene class, and display it.

1. Get sample 3D model

Note that a 3D model doesn't have to be static. It can include animated stuff, interactions, 3D sounds, scripts, and more. Our scene graph based on X3D is really quite powerful.

A sample 3D model may be found inside the engine examples, in examples/3d_rendering_processing/data/car.x3d file. My advice is to copy now the whole directory data from there into your own project directory (so you have files like data/car.x3d). The name data is special, it cooperates with the default behavior of our ApplicationData function.

You can also download our demo VRML/X3D models. And you can open all the models with view3dscene, to see how do they look like before loading them into your game.

And if you want to make your own 3D model, go ahead — generally use any 3D modeler and export to any 3D format we can handle, preferably X3D. See our guide to creating game data for more information about preparing 3D data.

2. Write the code!

  1. If you use Lazarus form with TCastleControl: To load a 3D model, double click to create an event OnCreate on your TForm1 class, and put there the following code:

    // also add to your uses clauses these units: CastleSceneCore, CastleScene;
     
    procedure TForm1.FormCreate(Sender: TObject);
    var
      Scene: TCastleScene;
    begin
      Scene := TCastleScene.Create(Application);
      Scene.Load('car.x3d');
      Scene.Spatial := [ssRendering, ssDynamicCollisions];
      Scene.ProcessEvents := true;
     
      CastleControl1.SceneManager.Items.Add(Scene);
      CastleControl1.SceneManager.MainScene := Scene;
    end;
  2. If you use TCastleWindow: To load a 3D model, change your program code to this:

    uses CastleWindow, CastleSceneCore, CastleScene;
    var
      Window: TCastleWindow;
      Scene: TCastleScene;
    begin
      Window := TCastleWindow.Create(Application);
      Window.Open;
     
      Scene := TCastleScene.Create(Application);
      Scene.Load('car.x3d');
      Scene.Spatial := [ssRendering, ssDynamicCollisions];
      Scene.ProcessEvents := true;
     
      Window.SceneManager.Items.Add(Scene);
      Window.SceneManager.MainScene := Scene;
     
      Application.Run;
    end.

At the beginning we create a new instance of TCastleScene, and load it's contents from a file. Scene.Spatial determines what spatial structures (octrees for now) are created, the value [ssRendering, ssDynamicCollisions] is the most flexible one (it allows to speed up the rendering by frustum culling, detect collisions between player and level, and it adapts to a dynamic level that may have some animated parts). Scene.ProcessEvents activates animating VRML/X3D models (you can remove it if you know that your level is, and always will be, static).

The level is added to the scene manager. The level is also set as the MainScene of scene manager, this means that some central settings (like initial camera position, initial headlight status and such) can be obtained from this scene.

3. Set the camera

To control the initial camera view:

  1. One option is to control the camera by code: use the SceneManager.RequiredCamera.SetView method. This takes three vectors — position, look direction and look up vector. Simply add the CastleVectors unit to your uses clause, and call this:

    Window.SceneManager.RequiredCamera.SetView(
      Vector3(-7.83,  6.15, -7.55),
      Vector3( 0.47, -0.30,  0.82),
      Vector3( 0.16,  0.95,  0.25)
    );

    Instead of SceneManager.RequiredCamera above, you could use SceneManager.WalkCamera or SceneManager.ExamineCamera to force creating camera of the given type.

    You have a lot of options to control the camera from code:

  2. You can also set camera defaults (including position/direction/up) in your data (X3D files), instead of hardcoding it in Pascal. Use a Viewpoint or OrthoViewpoint node inside the car.x3d file (in general, inside a scene that is set as SceneManager.MainScene) to define the initial camera view. You can generate this Viewpoint using the view3dscene feature "Console -> Print Current Camera (Viewpoint)", or by setting the camera in Blender before exporting this X3D file.

    You can also control the initial camera type using the NavigationInfo X3D node. It determines e.g. whether we are in EXAMINE, WALK, FLY, NONE or other modes.

    Note that, in order for the "bindable" nodes (like Viewpoint, OrthoViewpoint, NavigationInfo) to work, they must be within a scene set as SceneManager.MainScene. The first Viewpoint or OrthoViewpoint is automatically used, just like the first NavigationInfo node. You can always call explicitly Viewpoint.EventSet_Bind.Send(true) to activate (jump to) a specific Viewpoint node.

    The camera settings (position, direction, up, navigation type, avatar height and many others) set in X3D file (by nodes like Viewpoint, or OrthoViewpoint, NavigationInfo) serve as defaults when we create a camera instance. You can override them all by code, as documented above.

  3. Warning: The simple methods TCastleWindow.Load or TCastleControl.Load forcefully reset (free and recreate) the camera. These methods are mostly designed for the case when your 3D world is actually only one 3D scene (e.g. when you make a 3D model viewer). It's usually better to avoid these methods in more complex applications.

4. Explanation: What is a "Scene Manager"

Scene manager contains the whole knowledge about your game 3D world. It is essential to add all your 3D stuff to a scene manager. An instance of scene manager (class TCastleSceneManager) is already created and available in the SceneManager property of the TCastleControl or TCastleWindow instance.

By default TCastleSceneManager also acts as a viewport filling the whole window. So the whole window shows your 3D world. In more complex scenarios you can have many smaller viewports inside your window using TCastleViewport. You can also turn off scene manager from being a viewport (setting TCastleSceneManager.DefaultViewport to false), and then scene manager is really only something that keeps track of 3D world, and nothing more.

For examples of using viewports, see:

  • The chapter about user interface shows how to set additional viewport.
  • Engine example examples/3d_rendering_processing/multiple_viewports.lpr
  • Engine example examples/fps_game/fps_game.lpr

In more advanced scenarios you may need to create and manage scene manager yourself. There are special classes available (TCastleControlCustom or TCastleWindowCustom) that allow you to use your own scene manager instance (or maybe zero, or maybe more than one scene manager instance, maybe a custom scene manager class...).

5. Try some impressive 3D models

An important strength of our engine is that you can express a lot of stuff inside your data, that is inside VRML/X3D models. So many features of our engine (shaders, screen effects, mirrors and many many more) don't have any special Object Pascal examples, because they are simply not needed. For simple uses, you just define what you need inside VRML/X3D file (of course, for advanced usage you can do a lot more with Object Pascal code, and you can always build/modify VRML/X3D graph by Object Pascal code). So be sure to grab our demo VRML/X3D models and try opening them with any engine example program (like the one you just created, or even our view3dscene) — you will find that everything just works, not requiring a single line of Object Pascal code.

Our model viewer view3dscene allows to test your models before loading them to your games.