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.gltf file. To follow this manual page easily, copy now the whole directory examples/3d_rendering_processing/data into your own project directory. So that you have files like data/car.gltf, data/car.bin, data/textures/car_shell.png inside your project. The name data for a directory name is special, content there can be loaded using the special URL like castle-data:/car.gltf (see detailed data directory docs).

You can also download our demo 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 TCastleWindowBase: To load a 3D model, change your program code to this:

    uses CastleWindow, CastleSceneCore, CastleScene, CastleViewport;
      Window: TCastleWindowBase;
      Viewport: TCastleViewport;
      Scene: TCastleScene;
      Window := TCastleWindowBase.Create(Application);
      Viewport := TCastleViewport.Create(Application);
      Viewport.FullSize := true;
      Viewport.AutoCamera := true; // instead of this, you could do "Viewport.Camera.SetView(...)"
      Viewport.AutoNavigation := true; // instead of this, you could do "Viewport.Navigation := ..."
      Scene := TCastleScene.Create(Application);
      Scene.Spatial := [ssRendering, ssDynamicCollisions];
      Viewport.Items.MainScene := Scene;
  2. If you use Lazarus form with TCastleControlBase: 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, CastleViewport;
    procedure TForm1.FormCreate(Sender: TObject);
      Viewport: TCastleViewport;
      Scene: TCastleScene;
      Viewport := TCastleViewport.Create(Application);
      Viewport.FullSize := true;
      Viewport.AutoCamera := true; // instead of this, you could do "Viewport.Camera.SetView(...)"
      Viewport.AutoNavigation := true; // instead of this, you could do "Viewport.Navigation := ..."
      Scene := TCastleScene.Create(Application);
      Scene.Spatial := [ssRendering, ssDynamicCollisions];
      Viewport.Items.MainScene := Scene;

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

Then we create a viewport, which is a 2D rectangular area in a window that will show the world.

The model is added to the viewport. The model is also set as the MainScene, 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 and navigation

As the comments in the above examples indicate, using Viewport.AutoCamera := true and Viewport.AutoNavigation := true is only one way to configure camera and navigation.

To control the initial camera view:

  1. If you set up the 3D world using the editor, which we advise:

    Set the initial camera by the editor: navigate in editor, and use the "hamburger" menu that appears when you select the viewport to choose "Camera Initial := Current". Save the design.

    Leave Viewport.AutoCamera at (default) value false (otherwise the auto-detection, done at the first render, would override what you set).

  2. Set the camera by code: use the Viewport.Camera.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:

      Vector3(-11.34, 30.04, 96.07), // position
      Vector3(0.10, -0.49, -0.87), // direction
      Vector3(0.35, 0.83, -0.43), // up (current)
      Vector3(0.00, 1.00, 0.00) // gravity up

    How to generate such code? Use view3dscene. Open the model, navigate, then use Clipboard -> Print Current Camera (Viewpoint) (Pascal).

    Again, leave Viewport.AutoCamera at (default) value false (otherwise the auto-detection, done at the first render, would override what you set).

  3. Alternatively initialize the camera defaults (including position / direction / up) based on the model size / nodes.

    To make it work, set Viewport.AutoCamera := true.

    If a model file (set as MainScene, like car.gltf in the example above) has a Viewpoint or OrthoViewpoint X3D node, then this node will determine the initial camera. You can generate such Viewpoint using the view3dscene feature "Console -> Print Current Camera (Viewpoint)", or by setting the camera in Blender before exporting this X3D file.

    Otherwise (if there is no Viewpoint node, or you didn't even set MainScene) then the camera will be auto-detected to look at the world bounding box.

To can also control the navigation ("how does the camera change based on input"):

  1. You can assign a specific TCastleNavigation descendant to Viewport.Navigation.

  2. You can auto-detect the suitable navigation method. To do this, leave Viewport.Navigation as nil, and set Viewport.AutoNavigation := true. Then the navigation instance will be automatically created before rendering.

  3. You can use Viewport.WalkNavigation, Viewport.ExamineNavigation to request given navigation class and assign it to Viewport.Navigation.

  4. You can change the navigation type by setting Viewport.NavigationType.

  5. To control the camera only from your code (by calling Viewport.Camera.SetView(...)), just leave AutoNavigation as false, and leave Navigation as nil. By default, the engine does not automatically interpret any keys/mouse to handle the navigation.

  6. You can also control the initial navigation 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 Viewport.Items.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 and navigation 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.

4. Explanation: What is a TCastleViewport

Viewport allows to display and interact with the 3D world. It is essential to add all your 3D stuff to a viewport.

Viewport is a user interface control, which means that it occupies some space on a 2D window on user screen. By setting Viewport.FullSize := true in examples above we say that the position and size of the viewport is such that it fills the entire parent, which in this case means that viewport fills the entire window.

In more complex scenarios you can have multiple viewports inside your window showing the same world from many cameras. For examples of this, 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

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.